From 0a0a5f3202eb2c8532f3672c8c65ec54aa59c125 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 1 Nov 2011 00:23:04 +1100 Subject: [PATCH 01/10] Fixed bug in context.gc() where the reference was not passed back. --- spidermonkey/context.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spidermonkey/context.c b/spidermonkey/context.c index 6db6d83..d05a5df 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -621,6 +621,8 @@ PyObject* Context_gc(Context* self, PyObject* args, PyObject* kwargs) { JS_GC(self->cx); + + Py_INCREF(self); return (PyObject*) self; } From 06f3419de287c63cf7a57ae513e8754f660a5d60 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 1 Nov 2011 02:14:19 +1100 Subject: [PATCH 02/10] Started down the path of making changes for the current spidermonkey 1.8.5 release. Lots yet to do. --- setup.py | 6 +--- spidermonkey/context.c | 57 ++++++++++++++++++++----------------- spidermonkey/jsobject.c | 1 - spidermonkey/pyobject.c | 35 +++++++++++++---------- spidermonkey/spidermonkey.h | 2 ++ 5 files changed, 54 insertions(+), 47 deletions(-) diff --git a/setup.py b/setup.py index dabb917..92c5bc8 100644 --- a/setup.py +++ b/setup.py @@ -87,10 +87,7 @@ def nspr_config(config=None): return pkg_config("nspr", config) def js_config(config=None): - config = pkg_config("mozilla-js", config) - if "-DJS_THREADSAFE" not in config["extra_compile_args"]: - raise SystemError("Unable to link against a library that was " - "compiled without -DJS_THREADSAFE"); + config = pkg_config("mozjs185", config) return config def platform_config(): @@ -105,7 +102,6 @@ def platform_config(): # Build our configuration config = { "extra_compile_args": [ - "-DJS_THREADSAFE", "-DPOSIX_SOURCE", "-D_BSD_SOURCE", "-Wno-strict-prototypes" # Disable copius JS warnings diff --git a/spidermonkey/context.c b/spidermonkey/context.c index d05a5df..f70e5ad 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -10,26 +10,26 @@ #include // After spidermonkey.h so after Python.h -#include -#include +//#include +//#include // Forward decl for add_prop -JSBool set_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval); +JSBool set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* rval); JSBool -add_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) +add_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) { JSObject* obj = NULL; if(JSVAL_IS_NULL(*rval) || !JSVAL_IS_OBJECT(*rval)) return JS_TRUE; obj = JSVAL_TO_OBJECT(*rval); - if(JS_ObjectIsFunction(jscx, obj)) return set_prop(jscx, jsobj, key, rval); + if(JS_ObjectIsFunction(jscx, obj)) return set_prop(jscx, jsobj, key, JS_TRUE, rval); return JS_TRUE; } JSBool -del_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) +del_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) { Context* pycx = NULL; PyObject* pykey = NULL; @@ -74,7 +74,7 @@ del_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) } JSBool -get_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) +get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) { Context* pycx = NULL; PyObject* pykey = NULL; @@ -122,7 +122,7 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) } JSBool -set_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) +set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* rval) { Context* pycx = NULL; PyObject* pykey = NULL; @@ -162,7 +162,7 @@ set_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* rval) } JSBool -resolve(JSContext* jscx, JSObject* jsobj, jsval key) +resolve(JSContext* jscx, JSObject* jsobj, jsid key) { Context* pycx = NULL; PyObject* pykey = NULL; @@ -200,8 +200,8 @@ resolve(JSContext* jscx, JSObject* jsobj, jsval key) goto done; } - if(!js_DefineProperty(jscx, pycx->root, pid, JSVAL_VOID, NULL, NULL, - JSPROP_SHARED, NULL)) + if(!JS_DefinePropertyById(jscx, pycx->root, pid, JSVAL_VOID, NULL, NULL, + JSPROP_SHARED)) { JS_ReportError(jscx, "Failed to define property."); goto done; @@ -231,7 +231,7 @@ js_global_class = { #define MAX(a, b) ((a) > (b) ? (a) : (b)) JSBool -branch_cb(JSContext* jscx, JSScript* script) +branch_cb(JSContext* jscx) { Context* pycx = (Context*) JS_GetContextPrivate(jscx); time_t now = time(NULL); @@ -257,15 +257,20 @@ branch_cb(JSContext* jscx, JSScript* script) pycx->branch_count = 0; - if(pycx->max_heap > 0 && jscx->runtime->gcBytes > pycx->max_heap) + if(pycx->max_heap > 0) { - // First see if garbage collection gets under the threshold. - JS_GC(jscx); - if(jscx->runtime->gcBytes > pycx->max_heap) - { - PyErr_NoMemory(); - return JS_FALSE; - } + int gcbytes = JS_GetGCParameter(pycx->rt->rt, JSGC_BYTES); + if (gcbytes > pycx->max_heap) + { + // First see if garbage collection gets under the threshold. + JS_GC(jscx); + gcbytes = JS_GetGCParameter(pycx->rt->rt, JSGC_BYTES); + if(gcbytes > pycx->max_heap) + { + PyErr_NoMemory(); + return JS_FALSE; + } + } } if( @@ -380,7 +385,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) self->start_time = 0; self->max_heap = 0; - JS_SetBranchCallback(self->cx, branch_cb); + JS_SetOperationCallback(self->cx, branch_cb); JS_SetErrorReporter(self->cx, report_error_cb); jsopts = JS_GetOptions(self->cx); @@ -453,7 +458,7 @@ Context_add_global(Context* self, PyObject* args, PyObject* kwargs) jsv = py2js(self, pyval); if(jsv == JSVAL_VOID) goto error; - if(!js_SetProperty(self->cx, self->root, kid, &jsv)) + if(!JS_SetPropertyById(self->cx, self->root, kid, &jsv)) { PyErr_SetString(PyExc_AttributeError, "Failed to set global property."); goto error; @@ -488,7 +493,7 @@ Context_rem_global(Context* self, PyObject* args, PyObject* kwargs) PyErr_SetString(JSError, "Failed to create key id."); } - if(!js_GetProperty(self->cx, self->root, kid, &jsv)) + if(!JS_GetPropertyById(self->cx, self->root, kid, &jsv)) { PyErr_SetString(JSError, "Failed to get global property."); goto error; @@ -497,7 +502,7 @@ Context_rem_global(Context* self, PyObject* args, PyObject* kwargs) ret = js2py(self, jsv); if(ret == NULL) goto error; - if(!js_DeleteProperty(self->cx, self->root, kid, &jsv)) + if(!JS_DeletePropertyById(self->cx, self->root, kid)) { PyErr_SetString(JSError, "Failed to remove global property."); goto error; @@ -557,7 +562,7 @@ Context_execute(Context* self, PyObject* args, PyObject* kwargs) JSContext* cx = NULL; JSObject* root = NULL; JSString* script = NULL; - jschar* schars = NULL; + const jschar* schars = NULL; JSBool started_counter = JS_FALSE; char *fname = ""; unsigned int lineno = 1; @@ -575,7 +580,7 @@ Context_execute(Context* self, PyObject* args, PyObject* kwargs) script = py2js_string_obj(self, obj); if(script == NULL) goto error; - schars = JS_GetStringChars(script); + schars = JS_GetStringCharsZ(self->cx, script); slen = JS_GetStringLength(script); cx = self->cx; diff --git a/spidermonkey/jsobject.c b/spidermonkey/jsobject.c index eea3ed3..0cf4cb9 100644 --- a/spidermonkey/jsobject.c +++ b/spidermonkey/jsobject.c @@ -7,7 +7,6 @@ */ #include "spidermonkey.h" -#include PyObject* make_object(PyTypeObject* type, Context* cx, jsval val) diff --git a/spidermonkey/pyobject.c b/spidermonkey/pyobject.c index b0b9572..97c9c5d 100644 --- a/spidermonkey/pyobject.c +++ b/spidermonkey/pyobject.c @@ -7,7 +7,6 @@ */ #include "spidermonkey.h" -#include /* This is a fairly unsafe operation in so much as @@ -32,13 +31,13 @@ get_py_obj(JSContext* cx, JSObject* obj) } JSBool -js_add_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* val) +js_add_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) { return JS_TRUE; } JSBool -js_del_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* val) +js_del_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -80,7 +79,7 @@ js_del_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* val) } JSBool -js_get_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* val) +js_get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -152,7 +151,7 @@ js_get_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* val) } JSBool -js_set_prop(JSContext* jscx, JSObject* jsobj, jsval key, jsval* val) +js_set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* val) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -256,7 +255,7 @@ mk_args_tuple(Context* pycx, JSContext* jscx, uintN argc, jsval* argv) } JSBool -js_call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) +js_call(JSContext* jscx, uintN argc, jsval* vp) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -264,7 +263,9 @@ js_call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) PyObject* ret = NULL; PyObject* attrcheck = NULL; JSBool jsret = JS_FALSE; - + jsval rval; + jsval *argv = JS_ARGV(jscx, vp); + pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) { @@ -300,8 +301,10 @@ js_call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) goto error; } - *rval = py2js(pycx, ret); - if(*rval == JSVAL_VOID) + rval = py2js(pycx, ret); + JS_SET_RVAL(jscx, vp, rval); + + if(rval == JSVAL_VOID) { JS_ReportError(jscx, "Failed to convert Python return value."); goto error; @@ -319,7 +322,7 @@ js_call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) } JSBool -js_ctor(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) +js_ctor(JSContext* jscx, uintN argc, jsval* vp) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -327,7 +330,9 @@ js_ctor(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) PyObject* ret = NULL; PyObject* attrcheck = NULL; JSBool jsret = JS_FALSE; - + jsval *argv = JS_ARGV(jscx, vp); + jsval rval; + pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) { @@ -365,13 +370,15 @@ js_ctor(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) goto error; } - *rval = py2js(pycx, ret); - if(*rval == JSVAL_VOID) + rval = py2js(pycx, ret); + if(rval == JSVAL_VOID) { JS_ReportError(jscx, "Failed to convert Python return value."); goto error; } + JS_SET_RVAL(jscx, vp, rval); + jsret = JS_TRUE; goto success; @@ -420,14 +427,12 @@ create_class(Context* cx, PyObject* pyobj) jsclass->resolve = JS_ResolveStub; jsclass->convert = JS_ConvertStub; jsclass->finalize = js_finalize; - jsclass->getObjectOps = NULL; jsclass->checkAccess = NULL; jsclass->call = js_call; jsclass->construct = js_ctor; jsclass->xdrObject = NULL; jsclass->hasInstance = NULL; jsclass->mark = NULL; - jsclass->reserveSlots = NULL; curr = HashCObj_FromVoidPtr(jsclass); if(curr == NULL) goto error; diff --git a/spidermonkey/spidermonkey.h b/spidermonkey/spidermonkey.h index a1ff5c6..984674f 100644 --- a/spidermonkey/spidermonkey.h +++ b/spidermonkey/spidermonkey.h @@ -12,7 +12,9 @@ #include #include "structmember.h" +#pragma GCC diagnostic ignored "-Wstrict-prototypes" #include +#pragma GCC diagnostic warning "-Wstrict-prototypes" #include "runtime.h" #include "context.h" From 0dbcaacf55c4a303031c8ff587568ae3c3e2c54a Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 1 Nov 2011 15:11:07 +1100 Subject: [PATCH 03/10] First round of changes. Compiles cleanly at this point, but doesn't run. --- spidermonkey/context.c | 2 +- spidermonkey/integer.c | 13 +-------- spidermonkey/jsfunction.c | 4 +-- spidermonkey/jsiterator.c | 4 +-- spidermonkey/jsobject.c | 16 +++++------ spidermonkey/pyiter.c | 59 +++++++++++++++++++++++---------------- spidermonkey/pyobject.c | 6 ++-- spidermonkey/string.c | 4 +-- 8 files changed, 55 insertions(+), 53 deletions(-) diff --git a/spidermonkey/context.c b/spidermonkey/context.c index f70e5ad..66ad47b 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -357,7 +357,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) JS_SetContextPrivate(self->cx, self); // Setup the root of the property lookup doodad. - self->root = JS_NewObject(self->cx, &js_global_class, NULL, NULL); + self->root = JS_NewCompartmentAndGlobalObject(self->cx, &js_global_class, NULL); if(self->root == NULL) { PyErr_SetString(PyExc_RuntimeError, "Error creating root object."); diff --git a/spidermonkey/integer.c b/spidermonkey/integer.c index 7a1b40f..230bd3c 100644 --- a/spidermonkey/integer.c +++ b/spidermonkey/integer.c @@ -32,19 +32,8 @@ long2js_integer(Context* cx, long pyval) { jsval ret = JSVAL_VOID; - if(INT_FITS_IN_JSVAL(pyval)) - { - ret = INT_TO_JSVAL(pyval); - goto done; - } - - if(!JS_NewNumberValue(cx->cx, pyval, &ret)) - { - PyErr_SetString(PyExc_ValueError, "Failed to convert number."); - goto done; - } + ret = INT_TO_JSVAL(pyval); -done: return ret; } diff --git a/spidermonkey/jsfunction.c b/spidermonkey/jsfunction.c index 49cd80f..faf475e 100644 --- a/spidermonkey/jsfunction.c +++ b/spidermonkey/jsfunction.c @@ -23,7 +23,7 @@ js2py_function(Context* cx, jsval val, jsval parent) if(ret == NULL) goto error; ret->parent = parent; - if(!JS_AddRoot(cx->cx, &(ret->parent))) + if(!JS_AddValueRoot(cx->cx, &(ret->parent))) { PyErr_SetString(PyExc_RuntimeError, "Failed to add GC root."); goto error; @@ -44,7 +44,7 @@ Function_dealloc(Function* self) if(self->parent != JSVAL_VOID) { JS_BeginRequest(self->obj.cx->cx); - JS_RemoveRoot(self->obj.cx->cx, &(self->parent)); + JS_RemoveValueRoot(self->obj.cx->cx, &(self->parent)); JS_EndRequest(self->obj.cx->cx); } diff --git a/spidermonkey/jsiterator.c b/spidermonkey/jsiterator.c index 4c7fe54..6677725 100644 --- a/spidermonkey/jsiterator.c +++ b/spidermonkey/jsiterator.c @@ -29,7 +29,7 @@ Iterator_Wrap(Context* cx, JSObject* obj) if(self->iter == NULL) goto error; self->root = OBJECT_TO_JSVAL(self->iter); - if(!JS_AddRoot(cx->cx, &(self->root))) + if(!JS_AddValueRoot(cx->cx, &(self->root))) { PyErr_SetString(PyExc_RuntimeError, "Failed to set GC root."); goto error; @@ -81,7 +81,7 @@ Iterator_dealloc(Iterator* self) if(self->iter != NULL) { JS_BeginRequest(self->cx->cx); - JS_RemoveRoot(self->cx->cx, &(self->root)); + JS_RemoveValueRoot(self->cx->cx, &(self->root)); JS_EndRequest(self->cx->cx); } diff --git a/spidermonkey/jsobject.c b/spidermonkey/jsobject.c index 0cf4cb9..9f33d6f 100644 --- a/spidermonkey/jsobject.c +++ b/spidermonkey/jsobject.c @@ -56,7 +56,7 @@ make_object(PyTypeObject* type, Context* cx, jsval val) wrapped->val = val; wrapped->obj = obj; - if(!JS_AddRoot(cx->cx, &(wrapped->val))) + if(!JS_AddValueRoot(cx->cx, &(wrapped->val))) { PyErr_SetString(PyExc_RuntimeError, "Failed to set GC root."); goto error; @@ -116,7 +116,7 @@ Object_dealloc(Object* self) if(self->val != JSVAL_VOID) { JS_BeginRequest(self->cx->cx); - JS_RemoveRoot(self->cx->cx, &(self->val)); + JS_RemoveValueRoot(self->cx->cx, &(self->val)); JS_EndRequest(self->cx->cx); } @@ -128,7 +128,7 @@ Object_repr(Object* self) { //jsval val; JSString* repr = NULL; - jschar* rchars = NULL; + const jschar* rchars = NULL; size_t rlen; JS_BeginRequest(self->cx->cx); @@ -141,7 +141,7 @@ Object_repr(Object* self) return NULL; } - rchars = JS_GetStringChars(repr); + rchars = JS_GetStringCharsZ(self->cx->cx, repr); rlen = JS_GetStringLength(repr); JS_EndRequest(self->cx->cx); @@ -195,7 +195,7 @@ Object_getitem(Object* self, PyObject* key) goto done; } - if(!js_GetProperty(self->cx->cx, self->obj, pid, &pval)) + if(!JS_GetPropertyById(self->cx->cx, self->obj, pid, &pval)) { PyErr_SetString(PyExc_AttributeError, "Failed to get property."); goto done; @@ -232,7 +232,7 @@ Object_setitem(Object* self, PyObject* key, PyObject* val) vval = py2js(self->cx, val); if(vval == JSVAL_VOID) goto done; - if(!js_SetProperty(self->cx->cx, self->obj, pid, &vval)) + if(!JS_SetPropertyById(self->cx->cx, self->obj, pid, &vval)) { PyErr_SetString(PyExc_AttributeError, "Failed to set property."); goto done; @@ -240,7 +240,7 @@ Object_setitem(Object* self, PyObject* key, PyObject* val) } else { - if(!js_DeleteProperty(self->cx->cx, self->obj, pid, &vval)) + if(!JS_DeletePropertyById(self->cx->cx, self->obj, pid)) { PyErr_SetString(PyExc_AttributeError, "Failed to delete property."); goto done; @@ -310,7 +310,7 @@ Object_rich_cmp(Object* self, PyObject* other, int op) goto error; } - if(!js_GetProperty(self->cx->cx, self->obj, pid, &pval)) + if(!JS_GetPropertyById(self->cx->cx, self->obj, pid, &pval)) { PyErr_SetString(PyExc_RuntimeError, "Failed to get property."); goto error; diff --git a/spidermonkey/pyiter.c b/spidermonkey/pyiter.c index 09e9292..c9cccdd 100644 --- a/spidermonkey/pyiter.c +++ b/spidermonkey/pyiter.c @@ -50,9 +50,11 @@ finalize(JSContext* jscx, JSObject* jsobj) } JSBool -call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) +call(JSContext* jscx, uintN argc, jsval* vp) { - jsval objval = argv[-2]; + jsval *argv = JS_ARGV(jscx, vp); + jsval objval = JS_CALLEE(jscx, vp); + JSObject* obj = JSVAL_TO_OBJECT(objval); if(argc >= 1 && JSVAL_IS_BOOLEAN(argv[0]) && !JSVAL_TO_BOOLEAN(argv[0])) @@ -64,7 +66,8 @@ call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) } } - *rval = argv[-2]; + JS_SET_RVAL(jscx, vp, objval); + return JS_TRUE; } @@ -83,7 +86,7 @@ is_for_each(JSContext* cx, JSObject* obj, JSBool* rval) } JSBool -def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) +def_next(JSContext* jscx, uintN argc, jsval* vp) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -92,6 +95,8 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) PyObject* value = NULL; JSBool ret = JS_FALSE; JSBool foreach = JS_FALSE; + jsval rval; + JSObject *funcobj = JSVAL_TO_OBJECT(JS_CALLEE(jscx, vp)); // For StopIteration throw JSObject* glbl = JS_GetGlobalObject(jscx); @@ -104,14 +109,14 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) goto done; } - iter = get_js_slot(jscx, jsobj, 1); + iter = get_js_slot(jscx, funcobj, 1); if(!PyIter_Check(iter)) { JS_ReportError(jscx, "Object is not an iterator."); goto done; } - pyobj = get_js_slot(jscx, jsobj, 0); + pyobj = get_js_slot(jscx, funcobj, 0); if(pyobj == NULL) { JS_ReportError(jscx, "Failed to find iterated object."); @@ -136,7 +141,7 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) goto done; } - if(!is_for_each(jscx, jsobj, &foreach)) + if(!is_for_each(jscx, funcobj, &foreach)) { JS_ReportError(jscx, "Failed to get iterator flag."); goto done; @@ -150,14 +155,16 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) JS_ReportError(jscx, "Failed to get value in 'for each'"); goto done; } - *rval = py2js(pycx, value); + rval = py2js(pycx, value); } else { - *rval = py2js(pycx, next); + rval = py2js(pycx, next); } - if(*rval != JSVAL_VOID) ret = JS_TRUE; + JS_SET_RVAL(jscx, vp, rval); + + if(rval != JSVAL_VOID) ret = JS_TRUE; done: Py_XDECREF(next); @@ -166,7 +173,7 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) } JSBool -seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) +seq_next(JSContext* jscx, uintN argc, jsval* vp) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -175,9 +182,11 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) PyObject* value = NULL; JSBool ret = JS_FALSE; JSBool foreach = JS_FALSE; + jsval rval; long maxval = -1; long currval = -1; - + JSObject *jsthis = JSVAL_TO_OBJECT(JS_THIS(jscx, vp)); + // For StopIteration throw JSObject* glbl = JS_GetGlobalObject(jscx); jsval exc = JSVAL_VOID; @@ -189,7 +198,7 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) goto done; } - pyobj = get_js_slot(jscx, jsobj, 0); + pyobj = get_js_slot(jscx, jsthis, 0); if(!PySequence_Check(pyobj)) { JS_ReportError(jscx, "Object is not a sequence."); @@ -199,7 +208,7 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) maxval = PyObject_Length(pyobj); if(maxval < 0) goto done; - iter = get_js_slot(jscx, jsobj, 1); + iter = get_js_slot(jscx, jsthis, 1); if(!PyInt_Check(iter)) { JS_ReportError(jscx, "Object is not an integer."); @@ -228,13 +237,13 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) next = PyInt_FromLong(currval + 1); if(next == NULL) goto done; - if(!JS_SetReservedSlot(jscx, jsobj, 1, PRIVATE_TO_JSVAL(next))) + if(!JS_SetReservedSlot(jscx, jsthis, 1, PRIVATE_TO_JSVAL(next))) { PyErr_SetString(PyExc_RuntimeError, "Failed to store base object."); goto done; } - if(!is_for_each(jscx, jsobj, &foreach)) + if(!is_for_each(jscx, jsthis, &foreach)) { JS_ReportError(jscx, "Failed to get iterator flag."); goto done; @@ -248,15 +257,17 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval) JS_ReportError(jscx, "Failed to get array element in 'for each'"); goto done; } - *rval = py2js(pycx, value); + rval = py2js(pycx, value); } else { - *rval = py2js(pycx, iter); + rval = py2js(pycx, iter); } next = iter; - if(*rval != JSVAL_VOID) ret = JS_TRUE; + + JS_SET_RVAL(jscx, vp, rval); + if(rval != JSVAL_VOID) ret = JS_TRUE; done: Py_XDECREF(next); @@ -271,7 +282,7 @@ js_iter_class = { JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, + JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, @@ -287,13 +298,13 @@ js_iter_class = { }; static JSFunctionSpec js_def_iter_functions[] = { - {"next", def_next, 0, 0, 0}, - {0, 0, 0, 0, 0} + {"next", def_next, 0, 0}, + {0, 0, 0, 0} }; static JSFunctionSpec js_seq_iter_functions[] = { - {"next", seq_next, 0, 0, 0}, - {0, 0, 0, 0, 0} + {"next", seq_next, 0, 0}, + {0, 0, 0, 0} }; JSBool diff --git a/spidermonkey/pyobject.c b/spidermonkey/pyobject.c index 97c9c5d..27c130e 100644 --- a/spidermonkey/pyobject.c +++ b/spidermonkey/pyobject.c @@ -265,6 +265,7 @@ js_call(JSContext* jscx, uintN argc, jsval* vp) JSBool jsret = JS_FALSE; jsval rval; jsval *argv = JS_ARGV(jscx, vp); + jsval funcobj = JS_CALLEE(jscx, vp); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) @@ -273,7 +274,7 @@ js_call(JSContext* jscx, uintN argc, jsval* vp) goto error; } - pyobj = get_py_obj(jscx, JSVAL_TO_OBJECT(argv[-2])); + pyobj = get_py_obj(jscx, JSVAL_TO_OBJECT(funcobj)); if(!PyCallable_Check(pyobj)) { @@ -331,6 +332,7 @@ js_ctor(JSContext* jscx, uintN argc, jsval* vp) PyObject* attrcheck = NULL; JSBool jsret = JS_FALSE; jsval *argv = JS_ARGV(jscx, vp); + jsval funcobj = JS_CALLEE(jscx, vp); jsval rval; pycx = (Context*) JS_GetContextPrivate(jscx); @@ -340,7 +342,7 @@ js_ctor(JSContext* jscx, uintN argc, jsval* vp) goto error; } - pyobj = get_py_obj(jscx, JSVAL_TO_OBJECT(argv[-2])); + pyobj = get_py_obj(jscx, JSVAL_TO_OBJECT(funcobj)); if(!PyCallable_Check(pyobj)) { diff --git a/spidermonkey/string.c b/spidermonkey/string.c index 5c734a2..952b421 100644 --- a/spidermonkey/string.c +++ b/spidermonkey/string.c @@ -72,7 +72,7 @@ PyObject* js2py_string(Context* cx, jsval val) { JSString* str; - jschar* bytes; + const jschar* bytes; size_t len; if(!JSVAL_IS_STRING(val)) @@ -83,7 +83,7 @@ js2py_string(Context* cx, jsval val) str = JSVAL_TO_STRING(val); len = JS_GetStringLength(str); - bytes = JS_GetStringChars(str); + bytes = JS_GetStringCharsZ(cx->cx, str); return PyUnicode_Decode((const char*) bytes, len*2, "utf-16", "strict"); } From c26ccca04e151a6518bccf89bdb7fe3463d767e8 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 1 Nov 2011 18:19:07 +1100 Subject: [PATCH 04/10] - Added --prebuilt-library switch to setup.py so that a downloaded version of spidermonkey can be used easily. - Added new calling sequences with jsid instead of jsval - Temporarily removed checks for JS_THREADSAFE, but a lot has changed in 1.8.5 that requires review - Changed malloc() to calloc() to assure that undocumented structure members are initialized to zero. - Eliminated internal "js_" calls and replaced with new "JS_" API equivalents. --- setup.py | 15 +++++++++++++-- spidermonkey/context.c | 27 +++++++++++++++++++++------ spidermonkey/jsfunction.c | 2 +- spidermonkey/jsobject.c | 2 +- spidermonkey/pyobject.c | 21 +++++++++++++++------ 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 92c5bc8..ee1e997 100644 --- a/setup.py +++ b/setup.py @@ -33,11 +33,14 @@ ez_setup.use_setuptools() from setuptools import setup, Extension +PREBUILT_PATH = os.path.abspath("../js-1.8.5/js/src/build-debug/dist") + DEBUG = "--debug" in sys.argv USE_SYSTEM_LIB = "--system-library" in sys.argv +USE_PREBUILT = "--prebuilt-library" in sys.argv def find_sources(extensions=(".c", ".cpp")): - if USE_SYSTEM_LIB: + if USE_SYSTEM_LIB or USE_PREBUILT: return [ fname for ext in extensions @@ -107,7 +110,6 @@ def platform_config(): "-Wno-strict-prototypes" # Disable copius JS warnings ], "include_dirs": [ - "spidermonkey/libjs", "spidermonkey/%s-%s" % (sysname, machine) ], "library_dirs": [], @@ -115,6 +117,13 @@ def platform_config(): "extra_link_args": [] } + if USE_PREBUILT: + config["include_dirs"] += [os.path.join(PREBUILT_PATH, "include")] + config["library_dirs"] += [os.path.join(PREBUILT_PATH, "lib")] + config["libraries"] += ["js_static", "stdc++"] + else: + config["include_dirs"] += ["spidermonkey/libjs"] + # Debug builds are useful for finding errors in # the request counting semantics for Spidermonkey if DEBUG: @@ -142,6 +151,8 @@ def platform_config(): "Build a DEBUG version of spidermonkey.")) Distribution.global_options.append(("system-library", None, "Link against an installed system library.")) +Distribution.global_options.append(("prebuilt-library", None, + "Link against prebuilt library in %s." % PREBUILT_PATH)) setup( name = "python-spidermonkey", diff --git a/spidermonkey/context.c b/spidermonkey/context.c index 66ad47b..5bdabe5 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -14,12 +14,15 @@ //#include // Forward decl for add_prop -JSBool set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* rval); +JSBool set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* rval); JSBool -add_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) +add_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) { JSObject* obj = NULL; + jsval key; + + JS_IdToValue(jscx, keyid, &key); if(JSVAL_IS_NULL(*rval) || !JSVAL_IS_OBJECT(*rval)) return JS_TRUE; @@ -29,12 +32,15 @@ add_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) } JSBool -del_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) +del_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) { Context* pycx = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; JSBool ret = JS_FALSE; + jsval key; + + JS_IdToValue(jscx, keyid, &key); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) @@ -74,12 +80,15 @@ del_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) } JSBool -get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) +get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) { Context* pycx = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; JSBool ret = JS_FALSE; + jsval key; + + JS_IdToValue(jscx, keyid, &key); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) @@ -122,12 +131,15 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* rval) } JSBool -set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* rval) +set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* rval) { Context* pycx = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; JSBool ret = JS_FALSE; + jsval key; + + JS_IdToValue(jscx, keyid, &key); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) @@ -162,12 +174,15 @@ set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* rval) } JSBool -resolve(JSContext* jscx, JSObject* jsobj, jsid key) +resolve(JSContext* jscx, JSObject* jsobj, jsid keyid) { Context* pycx = NULL; PyObject* pykey = NULL; jsid pid; JSBool ret = JS_FALSE; + jsval key; + + JS_IdToValue(jscx, keyid, &key); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) diff --git a/spidermonkey/jsfunction.c b/spidermonkey/jsfunction.c index faf475e..35d30e0 100644 --- a/spidermonkey/jsfunction.c +++ b/spidermonkey/jsfunction.c @@ -70,7 +70,7 @@ Function_call(Function* self, PyObject* args, PyObject* kwargs) argc = PySequence_Length(args); if(argc < 0) goto error; - argv = malloc(sizeof(jsval) * argc); + argv = calloc(argc, sizeof(jsval)); if(argv == NULL) { PyErr_NoMemory(); diff --git a/spidermonkey/jsobject.c b/spidermonkey/jsobject.c index 9f33d6f..1153e50 100644 --- a/spidermonkey/jsobject.c +++ b/spidermonkey/jsobject.c @@ -26,7 +26,7 @@ make_object(PyTypeObject* type, Context* cx, jsval val) // Unwrapping if its wrapped. obj = JSVAL_TO_OBJECT(val); - klass = JS_GetClass(cx->cx, obj); + klass = JS_GET_CLASS(cx->cx, obj); if(klass != NULL && (klass->flags & flags) == flags) { if(JS_GetReservedSlot(cx->cx, obj, 0, &priv)) diff --git a/spidermonkey/pyobject.c b/spidermonkey/pyobject.c index 27c130e..a65617d 100644 --- a/spidermonkey/pyobject.c +++ b/spidermonkey/pyobject.c @@ -31,19 +31,22 @@ get_py_obj(JSContext* cx, JSObject* obj) } JSBool -js_add_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) +js_add_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) { return JS_TRUE; } JSBool -js_del_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) +js_del_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) { Context* pycx = NULL; PyObject* pyobj = NULL; PyObject* pykey = NULL; JSBool ret = JS_FALSE; - + jsval key; + + JS_IdToValue(jscx, keyid, &key); + pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) { @@ -79,7 +82,7 @@ js_del_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) } JSBool -js_get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) +js_get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) { Context* pycx = NULL; PyObject* pyobj = NULL; @@ -88,6 +91,9 @@ js_get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) PyObject* pyval = NULL; JSBool ret = JS_FALSE; const char* data; + jsval key; + + JS_IdToValue(jscx, keyid, &key); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) @@ -151,13 +157,16 @@ js_get_prop(JSContext* jscx, JSObject* jsobj, jsid key, jsval* val) } JSBool -js_set_prop(JSContext* jscx, JSObject* jsobj, jsid key, JSBool strict, jsval* val) +js_set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* val) { Context* pycx = NULL; PyObject* pyobj = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; JSBool ret = JS_FALSE; + jsval key; + + JS_IdToValue(jscx, keyid, &key); pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) @@ -403,7 +412,7 @@ create_class(Context* cx, PyObject* pyobj) curr = Context_get_class(cx, pyobj->ob_type->tp_name); if(curr != NULL) return (JSClass*) HashCObj_AsVoidPtr(curr); - jsclass = (JSClass*) malloc(sizeof(JSClass)); + jsclass = (JSClass*) calloc(1, sizeof(JSClass)); if(jsclass == NULL) { PyErr_NoMemory(); From 2388fde54e2cbb11282e660135a9b8a723069510 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Wed, 2 Nov 2011 19:37:35 +1100 Subject: [PATCH 05/10] - Fixed notorious memory corruption bug (malloc length incorrect in pyobject.c) - Deprecated old embedded libjs build and removed old libjs code. - Added ability to add finalize functions to general hashobj wrappers to handle synchronization of tasks between js and python - make_object now NEVER returns an object which is not of the requested type. There was erroneous code which unwrapped existing py objects which were causing memory overwrites with certain types of return values. - Added unwrap_pyobject() to handle cases where we want to unwrap rather than double-wrap python objects passed back from js - Still some bugs in iterators. - Still some problems with old memory and runtime checks do to the Operation Callbacks change in 1.8.5. --- setup.py | 7 +- spidermonkey/context.c | 33 +- spidermonkey/context.h | 3 +- spidermonkey/convert.c | 11 +- spidermonkey/hashcobj.c | 12 +- spidermonkey/hashcobj.h | 5 +- spidermonkey/jsfunction.c | 2 +- spidermonkey/jsiterator.c | 2 +- spidermonkey/jsobject.c | 32 +- spidermonkey/libjs/js.msg | 301 -- spidermonkey/libjs/jsapi.c | 5011 ------------------ spidermonkey/libjs/jsapi.h | 2220 -------- spidermonkey/libjs/jsarena.c | 502 -- spidermonkey/libjs/jsarena.h | 303 -- spidermonkey/libjs/jsarray.c | 1864 ------- spidermonkey/libjs/jsarray.h | 95 - spidermonkey/libjs/jsatom.c | 999 ---- spidermonkey/libjs/jsatom.h | 456 -- spidermonkey/libjs/jsbit.h | 195 - spidermonkey/libjs/jsbool.c | 227 - spidermonkey/libjs/jsbool.h | 76 - spidermonkey/libjs/jsclist.h | 139 - spidermonkey/libjs/jscntxt.c | 1229 ----- spidermonkey/libjs/jscntxt.h | 1013 ---- spidermonkey/libjs/jscompat.h | 57 - spidermonkey/libjs/jsconfig.h | 208 - spidermonkey/libjs/jsdate.c | 2371 --------- spidermonkey/libjs/jsdate.h | 120 - spidermonkey/libjs/jsdbgapi.c | 1439 ----- spidermonkey/libjs/jsdbgapi.h | 406 -- spidermonkey/libjs/jsdhash.c | 826 --- spidermonkey/libjs/jsdhash.h | 581 --- spidermonkey/libjs/jsdtoa.c | 3132 ----------- spidermonkey/libjs/jsdtoa.h | 130 - spidermonkey/libjs/jsemit.c | 6845 ------------------------ spidermonkey/libjs/jsemit.h | 743 --- spidermonkey/libjs/jsexn.c | 1348 ----- spidermonkey/libjs/jsexn.h | 96 - spidermonkey/libjs/jsfile.c | 2735 ---------- spidermonkey/libjs/jsfile.h | 56 - spidermonkey/libjs/jsfile.msg | 90 - spidermonkey/libjs/jsfun.c | 2330 --------- spidermonkey/libjs/jsfun.h | 170 - spidermonkey/libjs/jsgc.c | 3201 ------------ spidermonkey/libjs/jsgc.h | 368 -- spidermonkey/libjs/jshash.c | 483 -- spidermonkey/libjs/jshash.h | 151 - spidermonkey/libjs/jsinterp.c | 6216 ---------------------- spidermonkey/libjs/jsinterp.h | 361 -- spidermonkey/libjs/jsiter.c | 1080 ---- spidermonkey/libjs/jsiter.h | 114 - spidermonkey/libjs/jskeyword.tbl | 124 - spidermonkey/libjs/jslibmath.h | 266 - spidermonkey/libjs/jslock.c | 1303 ----- spidermonkey/libjs/jslock.h | 266 - spidermonkey/libjs/jslog2.c | 94 - spidermonkey/libjs/jslong.c | 281 - spidermonkey/libjs/jslong.h | 437 -- spidermonkey/libjs/jsmath.c | 514 -- spidermonkey/libjs/jsmath.h | 57 - spidermonkey/libjs/jsnum.c | 1147 ---- spidermonkey/libjs/jsnum.h | 268 - spidermonkey/libjs/jsobj.c | 5035 ------------------ spidermonkey/libjs/jsobj.h | 596 --- spidermonkey/libjs/jsopcode.c | 4794 ----------------- spidermonkey/libjs/jsopcode.h | 318 -- spidermonkey/libjs/jsopcode.tbl | 478 -- spidermonkey/libjs/jsosdep.h | 115 - spidermonkey/libjs/jsotypes.h | 202 - spidermonkey/libjs/jsparse.c | 6547 ----------------------- spidermonkey/libjs/jsparse.h | 438 -- spidermonkey/libjs/jsprf.c | 1266 ----- spidermonkey/libjs/jsprf.h | 150 - spidermonkey/libjs/jsproto.tbl | 116 - spidermonkey/libjs/jsprvtd.h | 202 - spidermonkey/libjs/jspubtd.h | 667 --- spidermonkey/libjs/jsregexp.c | 4206 --------------- spidermonkey/libjs/jsregexp.h | 183 - spidermonkey/libjs/jsscan.c | 2101 -------- spidermonkey/libjs/jsscan.h | 389 -- spidermonkey/libjs/jsscope.c | 1776 ------- spidermonkey/libjs/jsscope.h | 407 -- spidermonkey/libjs/jsscript.c | 1717 ------ spidermonkey/libjs/jsscript.h | 225 - spidermonkey/libjs/jsshell.msg | 50 - spidermonkey/libjs/jsstddef.h | 83 - spidermonkey/libjs/jsstr.c | 4818 ----------------- spidermonkey/libjs/jsstr.h | 500 -- spidermonkey/libjs/jstypes.h | 464 -- spidermonkey/libjs/jsutil.c | 198 - spidermonkey/libjs/jsutil.h | 106 - spidermonkey/libjs/jsxdrapi.c | 835 --- spidermonkey/libjs/jsxdrapi.h | 223 - spidermonkey/libjs/jsxml.c | 8357 ------------------------------ spidermonkey/libjs/jsxml.h | 332 -- spidermonkey/libjs/prmjtime.c | 440 -- spidermonkey/libjs/prmjtime.h | 95 - spidermonkey/libjs/resource.h | 15 - spidermonkey/pyobject.c | 65 +- spidermonkey/pyobject.h | 1 + tests/test-context.py | 5 + 101 files changed, 101 insertions(+), 103565 deletions(-) delete mode 100644 spidermonkey/libjs/js.msg delete mode 100644 spidermonkey/libjs/jsapi.c delete mode 100644 spidermonkey/libjs/jsapi.h delete mode 100644 spidermonkey/libjs/jsarena.c delete mode 100644 spidermonkey/libjs/jsarena.h delete mode 100644 spidermonkey/libjs/jsarray.c delete mode 100644 spidermonkey/libjs/jsarray.h delete mode 100644 spidermonkey/libjs/jsatom.c delete mode 100644 spidermonkey/libjs/jsatom.h delete mode 100644 spidermonkey/libjs/jsbit.h delete mode 100644 spidermonkey/libjs/jsbool.c delete mode 100644 spidermonkey/libjs/jsbool.h delete mode 100644 spidermonkey/libjs/jsclist.h delete mode 100644 spidermonkey/libjs/jscntxt.c delete mode 100644 spidermonkey/libjs/jscntxt.h delete mode 100644 spidermonkey/libjs/jscompat.h delete mode 100644 spidermonkey/libjs/jsconfig.h delete mode 100644 spidermonkey/libjs/jsdate.c delete mode 100644 spidermonkey/libjs/jsdate.h delete mode 100644 spidermonkey/libjs/jsdbgapi.c delete mode 100644 spidermonkey/libjs/jsdbgapi.h delete mode 100644 spidermonkey/libjs/jsdhash.c delete mode 100644 spidermonkey/libjs/jsdhash.h delete mode 100644 spidermonkey/libjs/jsdtoa.c delete mode 100644 spidermonkey/libjs/jsdtoa.h delete mode 100644 spidermonkey/libjs/jsemit.c delete mode 100644 spidermonkey/libjs/jsemit.h delete mode 100644 spidermonkey/libjs/jsexn.c delete mode 100644 spidermonkey/libjs/jsexn.h delete mode 100644 spidermonkey/libjs/jsfile.c delete mode 100644 spidermonkey/libjs/jsfile.h delete mode 100644 spidermonkey/libjs/jsfile.msg delete mode 100644 spidermonkey/libjs/jsfun.c delete mode 100644 spidermonkey/libjs/jsfun.h delete mode 100644 spidermonkey/libjs/jsgc.c delete mode 100644 spidermonkey/libjs/jsgc.h delete mode 100644 spidermonkey/libjs/jshash.c delete mode 100644 spidermonkey/libjs/jshash.h delete mode 100644 spidermonkey/libjs/jsinterp.c delete mode 100644 spidermonkey/libjs/jsinterp.h delete mode 100644 spidermonkey/libjs/jsiter.c delete mode 100644 spidermonkey/libjs/jsiter.h delete mode 100644 spidermonkey/libjs/jskeyword.tbl delete mode 100644 spidermonkey/libjs/jslibmath.h delete mode 100644 spidermonkey/libjs/jslock.c delete mode 100644 spidermonkey/libjs/jslock.h delete mode 100644 spidermonkey/libjs/jslog2.c delete mode 100644 spidermonkey/libjs/jslong.c delete mode 100644 spidermonkey/libjs/jslong.h delete mode 100644 spidermonkey/libjs/jsmath.c delete mode 100644 spidermonkey/libjs/jsmath.h delete mode 100644 spidermonkey/libjs/jsnum.c delete mode 100644 spidermonkey/libjs/jsnum.h delete mode 100644 spidermonkey/libjs/jsobj.c delete mode 100644 spidermonkey/libjs/jsobj.h delete mode 100644 spidermonkey/libjs/jsopcode.c delete mode 100644 spidermonkey/libjs/jsopcode.h delete mode 100644 spidermonkey/libjs/jsopcode.tbl delete mode 100644 spidermonkey/libjs/jsosdep.h delete mode 100644 spidermonkey/libjs/jsotypes.h delete mode 100644 spidermonkey/libjs/jsparse.c delete mode 100644 spidermonkey/libjs/jsparse.h delete mode 100644 spidermonkey/libjs/jsprf.c delete mode 100644 spidermonkey/libjs/jsprf.h delete mode 100644 spidermonkey/libjs/jsproto.tbl delete mode 100644 spidermonkey/libjs/jsprvtd.h delete mode 100644 spidermonkey/libjs/jspubtd.h delete mode 100644 spidermonkey/libjs/jsregexp.c delete mode 100644 spidermonkey/libjs/jsregexp.h delete mode 100644 spidermonkey/libjs/jsscan.c delete mode 100644 spidermonkey/libjs/jsscan.h delete mode 100644 spidermonkey/libjs/jsscope.c delete mode 100644 spidermonkey/libjs/jsscope.h delete mode 100644 spidermonkey/libjs/jsscript.c delete mode 100644 spidermonkey/libjs/jsscript.h delete mode 100644 spidermonkey/libjs/jsshell.msg delete mode 100644 spidermonkey/libjs/jsstddef.h delete mode 100644 spidermonkey/libjs/jsstr.c delete mode 100644 spidermonkey/libjs/jsstr.h delete mode 100644 spidermonkey/libjs/jstypes.h delete mode 100644 spidermonkey/libjs/jsutil.c delete mode 100644 spidermonkey/libjs/jsutil.h delete mode 100644 spidermonkey/libjs/jsxdrapi.c delete mode 100644 spidermonkey/libjs/jsxdrapi.h delete mode 100644 spidermonkey/libjs/jsxml.c delete mode 100644 spidermonkey/libjs/jsxml.h delete mode 100644 spidermonkey/libjs/prmjtime.c delete mode 100644 spidermonkey/libjs/prmjtime.h delete mode 100644 spidermonkey/libjs/resource.h diff --git a/setup.py b/setup.py index ee1e997..b647ce8 100644 --- a/setup.py +++ b/setup.py @@ -36,8 +36,9 @@ PREBUILT_PATH = os.path.abspath("../js-1.8.5/js/src/build-debug/dist") DEBUG = "--debug" in sys.argv -USE_SYSTEM_LIB = "--system-library" in sys.argv +USE_LOCAL_LIB = "--local-library" in sys.argv USE_PREBUILT = "--prebuilt-library" in sys.argv +USE_SYSTEM_LIB = not (USE_LOCAL_LIB or USE_PREBUILT) def find_sources(extensions=(".c", ".cpp")): if USE_SYSTEM_LIB or USE_PREBUILT: @@ -149,8 +150,8 @@ def platform_config(): Distribution.global_options.append(("debug", None, "Build a DEBUG version of spidermonkey.")) -Distribution.global_options.append(("system-library", None, - "Link against an installed system library.")) +Distribution.global_options.append(("local-library", None, + "Link against a local copy of the library (deprecated).")) Distribution.global_options.append(("prebuilt-library", None, "Link against prebuilt library in %s." % PREBUILT_PATH)) diff --git a/spidermonkey/context.c b/spidermonkey/context.c index 5bdabe5..684afd9 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -352,6 +352,9 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) self->objects = (PySetObject*) PySet_New(NULL); if(self->objects == NULL) goto error; + self->root_objects = (PySetObject*) PySet_New(NULL); + if(self->root_objects == NULL) goto error; + self->cx = JS_NewContext(runtime->rt, 8192); if(self->cx == NULL) { @@ -436,6 +439,13 @@ Context_init(Context* self, PyObject* args, PyObject* kwargs) void Context_dealloc(Context* self) { + while (PySet_GET_SIZE(self->root_objects)) + { + PyObject* inroot = PySet_Pop((PyObject *)self->root_objects); + /* We need to undo the effect of adding this object to the root, but not sure how since it's not known */ + Py_DECREF(inroot); + } + if(self->cx != NULL) { JS_DestroyContext(self->cx); @@ -444,6 +454,7 @@ Context_dealloc(Context* self) Py_XDECREF(self->global); Py_XDECREF(self->access); Py_XDECREF(self->objects); + Py_XDECREF(self->root_objects); Py_XDECREF(self->classes); Py_XDECREF(self->rt); } @@ -823,19 +834,27 @@ Context_add_class(Context* cx, const char* key, PyObject* val) } int -Context_has_object(Context* cx, PyObject* val) +Context_add_root(Context* cx, PyObject* val, const char *idstring) { - return PySet_Contains((PyObject*) cx->objects, val); + if (PySet_Contains((PyObject*) cx->root_objects, val) > 0) + { + Py_DECREF(val); + return 0; + } + + return PySet_Add((PyObject*) cx->root_objects, val); } -int -Context_add_object(Context* cx, PyObject* val) +void +addobject_decref(void *ptr) { - return PySet_Add((PyObject*) cx->objects, val); + PyObject* pyo = (PyObject*) ptr; + Py_DECREF(pyo); } int -Context_rem_object(Context* cx, PyObject* val) +Context_add_object(Context* cx, PyObject* val) { - return PySet_Discard((PyObject*) cx->objects, val); + PyObject* wrapped = HashCObj_FromVoidPtr(val, addobject_decref); + return PySet_Add((PyObject*) cx->objects, wrapped); } diff --git a/spidermonkey/context.h b/spidermonkey/context.h index b8b0921..e715aa1 100644 --- a/spidermonkey/context.h +++ b/spidermonkey/context.h @@ -23,6 +23,7 @@ typedef struct { JSObject* root; PyDictObject* classes; PySetObject* objects; + PySetObject* root_objects; uint32 branch_count; long max_heap; time_t max_time; @@ -34,9 +35,7 @@ int Context_add_class(Context* cx, const char* key, PyObject* val); int Context_has_access(Context*, JSContext*, PyObject*, PyObject*); -int Context_has_object(Context* cx, PyObject* val); int Context_add_object(Context* cx, PyObject* val); -int Context_rem_object(Context* cx, PyObject* val); extern PyTypeObject _ContextType; diff --git a/spidermonkey/convert.c b/spidermonkey/convert.c index 6bfc3cf..d84d8a8 100644 --- a/spidermonkey/convert.c +++ b/spidermonkey/convert.c @@ -58,6 +58,7 @@ PyObject* js2py_with_parent(Context* cx, jsval val, jsval parent) { JSType vtype = JS_TypeOfValue(cx->cx, val); + PyObject* unwrapped; /* There's not JSType for null. Or rather, its @@ -87,7 +88,15 @@ js2py_with_parent(Context* cx, jsval val, jsval parent) if(JSVAL_IS_INT(val)) return js2py_integer(cx, val); else return js2py_double(cx, val); } - else if(vtype == JSTYPE_FUNCTION) + + /* Now try to unwrap any incoming object in so we don't rewrap our own objects being passed around. */ + + unwrapped = unwrap_pyobject(cx, val); + + if (unwrapped != NULL) + return unwrapped; + + if(vtype == JSTYPE_FUNCTION) { return js2py_function(cx, val, parent); } diff --git a/spidermonkey/hashcobj.c b/spidermonkey/hashcobj.c index e53eaf5..d6d0684 100644 --- a/spidermonkey/hashcobj.c +++ b/spidermonkey/hashcobj.c @@ -9,13 +9,14 @@ #include "spidermonkey.h" PyObject* -HashCObj_FromVoidPtr(void *cobj) +HashCObj_FromVoidPtr(void *cobj, HASHCOBJ_FINALIZER finalizer) { HashCObj* self = NULL; self = PyObject_NEW(HashCObj, HashCObjType); if(self == NULL) goto error; self->cobj = cobj; + self->finalizer = finalizer; goto success; @@ -77,13 +78,20 @@ HashCObj_hash(HashCObj* self) return _Py_HashPointer(self->cobj); } +void +HashCObj_dealloc(HashCObj* self) +{ + if (self->finalizer != NULL) + (*self->finalizer)(self->cobj); +} + PyTypeObject _HashCObjType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "spidermonkey._HashCObj", /*tp_name*/ sizeof(HashCObj), /*tp_basicsize*/ 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ + (destructor)HashCObj_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ diff --git a/spidermonkey/hashcobj.h b/spidermonkey/hashcobj.h index 51d0191..1fb5a5a 100644 --- a/spidermonkey/hashcobj.h +++ b/spidermonkey/hashcobj.h @@ -13,9 +13,12 @@ A class to implement Python hashing of C pointers. */ +typedef void (*HASHCOBJ_FINALIZER)(void *data); + typedef struct { PyObject_HEAD void* cobj; + HASHCOBJ_FINALIZER finalizer; } HashCObj; extern PyTypeObject _HashCObjType; @@ -25,7 +28,7 @@ extern PyTypeObject _HashCObjType; BIG FUCKING NOTE: This constructor never Py_INCREF's the returned object. */ -PyObject* HashCObj_FromVoidPtr(void *cobj); +PyObject* HashCObj_FromVoidPtr(void *cobj, HASHCOBJ_FINALIZER finalizer); void* HashCObj_AsVoidPtr(PyObject* self); #endif diff --git a/spidermonkey/jsfunction.c b/spidermonkey/jsfunction.c index 35d30e0..2a784bf 100644 --- a/spidermonkey/jsfunction.c +++ b/spidermonkey/jsfunction.c @@ -23,7 +23,7 @@ js2py_function(Context* cx, jsval val, jsval parent) if(ret == NULL) goto error; ret->parent = parent; - if(!JS_AddValueRoot(cx->cx, &(ret->parent))) + if(!JS_AddNamedValueRoot(cx->cx, &(ret->parent), "js2py_function")) { PyErr_SetString(PyExc_RuntimeError, "Failed to add GC root."); goto error; diff --git a/spidermonkey/jsiterator.c b/spidermonkey/jsiterator.c index 6677725..31b5a08 100644 --- a/spidermonkey/jsiterator.c +++ b/spidermonkey/jsiterator.c @@ -29,7 +29,7 @@ Iterator_Wrap(Context* cx, JSObject* obj) if(self->iter == NULL) goto error; self->root = OBJECT_TO_JSVAL(self->iter); - if(!JS_AddValueRoot(cx->cx, &(self->root))) + if(!JS_AddNamedValueRoot(cx->cx, &(self->root), "Iterator_Wrap")) { PyErr_SetString(PyExc_RuntimeError, "Failed to set GC root."); goto error; diff --git a/spidermonkey/jsobject.c b/spidermonkey/jsobject.c index 1153e50..427aa0e 100644 --- a/spidermonkey/jsobject.c +++ b/spidermonkey/jsobject.c @@ -13,39 +13,11 @@ make_object(PyTypeObject* type, Context* cx, jsval val) { Object* wrapped = NULL; PyObject* tpl = NULL; - PyObject* hashable = NULL; PyObject* ret = NULL; - void* raw = NULL; - uint32 flags = JSCLASS_HAS_RESERVED_SLOTS(1); - JSClass* klass = NULL; - JSObject* obj = NULL; - jsval priv; - int found; + JSObject* obj = JSVAL_TO_OBJECT(val); JS_BeginRequest(cx->cx); - // Unwrapping if its wrapped. - obj = JSVAL_TO_OBJECT(val); - klass = JS_GET_CLASS(cx->cx, obj); - if(klass != NULL && (klass->flags & flags) == flags) - { - if(JS_GetReservedSlot(cx->cx, obj, 0, &priv)) - { - raw = (PyObject*) JSVAL_TO_PRIVATE(priv); - hashable = HashCObj_FromVoidPtr(raw); - if(hashable == NULL) goto error; - - found = Context_has_object(cx, hashable); - if(found < 0) goto error; - if(found > 0) - { - ret = (PyObject*) raw; - Py_INCREF(ret); - goto success; - } - } - } - // Wrap JS value tpl = Py_BuildValue("(O)", cx); if(tpl == NULL) goto error; @@ -56,7 +28,7 @@ make_object(PyTypeObject* type, Context* cx, jsval val) wrapped->val = val; wrapped->obj = obj; - if(!JS_AddValueRoot(cx->cx, &(wrapped->val))) + if(!JS_AddNamedValueRoot(cx->cx, &(wrapped->val), "make_object")) { PyErr_SetString(PyExc_RuntimeError, "Failed to set GC root."); goto error; diff --git a/spidermonkey/libjs/js.msg b/spidermonkey/libjs/js.msg deleted file mode 100644 index 2686af0..0000000 --- a/spidermonkey/libjs/js.msg +++ /dev/null @@ -1,301 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This is the JavaScript error message file. - * - * The format for each JS error message is: - * - * MSG_DEF(, , , , - * ) - * - * where ; - * is a legal C identifer that will be used in the - * JS engine source. - * - * is an unique integral value identifying this error. - * - * is an integer literal specifying the total number of - * replaceable arguments in the following format string. - * - * is an exception index from the enum in jsexn.c; - * JSEXN_NONE for none. The given exception index will be raised by the - * engine when the corresponding error occurs. - * - * is a string literal, optionally containing sequences - * {X} where X is an integer representing the argument number that will - * be replaced with a string value when the error is reported. - * - * e.g. - * - * MSG_DEF(JSMSG_NOT_A_SUBSPECIES, 73, JSEXN_NONE, 2, - * "{0} is not a member of the {1} family") - * - * can be used: - * - * JS_ReportErrorNumber(JSMSG_NOT_A_SUBSPECIES, "Rhino", "Monkey"); - * - * to report: - * - * "Rhino is not a member of the Monkey family" - * - * Before adding a new MSG_DEF at the end, look for JSMSG_UNUSED free - * index placeholders in the middle of the list. - */ - -MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSMSG_NOT_DEFINED, 1, 1, JSEXN_REFERENCEERR, "{0} is not defined") -MSG_DEF(JSMSG_INACTIVE, 2, 0, JSEXN_INTERNALERR, "nothing active on context") -MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}") -MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}") -MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}") -MSG_DEF(JSMSG_CANT_LOCK, 6, 0, JSEXN_INTERNALERR, "can't lock memory") -MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_INTERNALERR, "can't unlock memory") -MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") -MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_TYPEERR, "{0} has no constructor") -MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}") -MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION, 11, 1, JSEXN_TYPEERR, "{0} is not a scripted function") -MSG_DEF(JSMSG_BAD_SORT_ARG, 12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument") -MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER, 13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}") -MSG_DEF(JSMSG_TOO_MANY_LITERALS, 14, 0, JSEXN_INTERNALERR, "too many literals") -MSG_DEF(JSMSG_CANT_WATCH, 15, 1, JSEXN_TYPEERR, "can't watch non-native objects of class {0}") -MSG_DEF(JSMSG_STACK_UNDERFLOW, 16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}") -MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large") -MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space") -MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only") -MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter") -MSG_DEF(JSMSG_BAD_ITERATOR, 21, 3, JSEXN_TYPEERR, "{0} has invalid {1} value {2}") -MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function") -MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor") -MSG_DEF(JSMSG_STACK_OVERFLOW, 24, 1, JSEXN_INTERNALERR, "stack overflow in {0}") -MSG_DEF(JSMSG_NOT_EXPORTED, 25, 1, JSEXN_TYPEERR, "{0} is not exported") -MSG_DEF(JSMSG_OVER_RECURSED, 26, 0, JSEXN_INTERNALERR, "too much recursion") -MSG_DEF(JSMSG_IN_NOT_OBJECT, 27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}") -MSG_DEF(JSMSG_BAD_NEW_RESULT, 28, 1, JSEXN_TYPEERR, "invalid new expression result {0}") -MSG_DEF(JSMSG_BAD_SHARP_DEF, 29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=") -MSG_DEF(JSMSG_BAD_SHARP_USE, 30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#") -MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}") -MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}") -MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}") -MSG_DEF(JSMSG_PAREN_BEFORE_LET, 34, 0, JSEXN_SYNTAXERR, "missing ( before let head") -MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer") -MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value") -MSG_DEF(JSMSG_PERMANENT, 37, 1, JSEXN_ERR, "{0} is permanent") -MSG_DEF(JSMSG_CANT_CONVERT_TO, 38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}") -MSG_DEF(JSMSG_NO_PROPERTIES, 39, 1, JSEXN_TYPEERR, "{0} has no properties") -MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_TYPEERR, "can't find class id {0}") -MSG_DEF(JSMSG_CANT_XDR_CLASS, 41, 1, JSEXN_TYPEERR, "can't XDR class {0}") -MSG_DEF(JSMSG_BYTECODE_TOO_BIG, 42, 2, JSEXN_INTERNALERR, "bytecode {0} too large (limit {1})") -MSG_DEF(JSMSG_UNKNOWN_FORMAT, 43, 1, JSEXN_INTERNALERR, "unknown bytecode format {0}") -MSG_DEF(JSMSG_TOO_MANY_CON_ARGS, 44, 0, JSEXN_SYNTAXERR, "too many constructor arguments") -MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS, 45, 0, JSEXN_SYNTAXERR, "too many function arguments") -MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifier {0}") -MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}") -MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}") -MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum") -MSG_DEF(JSMSG_BAD_DESTRUCT_DECL, 50, 0, JSEXN_SYNTAXERR, "missing = in destructuring declaration") -MSG_DEF(JSMSG_BAD_DESTRUCT_ASS, 51, 0, JSEXN_SYNTAXERR, "invalid destructuring assignment operator") -MSG_DEF(JSMSG_PAREN_AFTER_LET, 52, 0, JSEXN_SYNTAXERR, "missing ) after let head") -MSG_DEF(JSMSG_CURLY_AFTER_LET, 53, 0, JSEXN_SYNTAXERR, "missing } after let block") -MSG_DEF(JSMSG_MISSING_PAREN, 54, 0, JSEXN_SYNTAXERR, "unterminated parenthetical") -MSG_DEF(JSMSG_UNTERM_CLASS, 55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}") -MSG_DEF(JSMSG_TRAILING_SLASH, 56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression") -MSG_DEF(JSMSG_BAD_CLASS_RANGE, 57, 0, JSEXN_SYNTAXERR, "invalid range in character class") -MSG_DEF(JSMSG_BAD_FLAG, 58, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}") -MSG_DEF(JSMSG_NO_INPUT, 59, 3, JSEXN_SYNTAXERR, "no input for /{0}/{1}{2}") -MSG_DEF(JSMSG_CANT_OPEN, 60, 2, JSEXN_ERR, "can't open {0}: {1}") -MSG_DEF(JSMSG_BAD_STRING_MASK, 61, 1, JSEXN_ERR, "invalid string escape mask {0}") -MSG_DEF(JSMSG_UNMATCHED_RIGHT_PAREN, 62, 0, JSEXN_SYNTAXERR, "unmatched ) in regular expression") -MSG_DEF(JSMSG_END_OF_DATA, 63, 0, JSEXN_INTERNALERR, "unexpected end of data") -MSG_DEF(JSMSG_SEEK_BEYOND_START, 64, 0, JSEXN_INTERNALERR, "illegal seek beyond start") -MSG_DEF(JSMSG_SEEK_BEYOND_END, 65, 0, JSEXN_INTERNALERR, "illegal seek beyond end") -MSG_DEF(JSMSG_END_SEEK, 66, 0, JSEXN_INTERNALERR, "illegal end-based seek") -MSG_DEF(JSMSG_WHITHER_WHENCE, 67, 1, JSEXN_INTERNALERR, "unknown seek whence: {0}") -MSG_DEF(JSMSG_BAD_SCRIPT_MAGIC, 68, 0, JSEXN_INTERNALERR, "bad script XDR magic number") -MSG_DEF(JSMSG_PAREN_BEFORE_FORMAL, 69, 0, JSEXN_SYNTAXERR, "missing ( before formal parameters") -MSG_DEF(JSMSG_MISSING_FORMAL, 70, 0, JSEXN_SYNTAXERR, "missing formal parameter") -MSG_DEF(JSMSG_PAREN_AFTER_FORMAL, 71, 0, JSEXN_SYNTAXERR, "missing ) after formal parameters") -MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 72, 0, JSEXN_SYNTAXERR, "missing { before function body") -MSG_DEF(JSMSG_CURLY_AFTER_BODY, 73, 0, JSEXN_SYNTAXERR, "missing } after function body") -MSG_DEF(JSMSG_PAREN_BEFORE_COND, 74, 0, JSEXN_SYNTAXERR, "missing ( before condition") -MSG_DEF(JSMSG_PAREN_AFTER_COND, 75, 0, JSEXN_SYNTAXERR, "missing ) after condition") -MSG_DEF(JSMSG_NO_IMPORT_NAME, 76, 0, JSEXN_SYNTAXERR, "missing name in import statement") -MSG_DEF(JSMSG_NAME_AFTER_DOT, 77, 0, JSEXN_SYNTAXERR, "missing name after . operator") -MSG_DEF(JSMSG_BRACKET_IN_INDEX, 78, 0, JSEXN_SYNTAXERR, "missing ] in index expression") -MSG_DEF(JSMSG_NO_EXPORT_NAME, 79, 0, JSEXN_SYNTAXERR, "missing name in export statement") -MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 80, 0, JSEXN_SYNTAXERR, "missing ( before switch expression") -MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 81, 0, JSEXN_SYNTAXERR, "missing ) after switch expression") -MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 82, 0, JSEXN_SYNTAXERR, "missing { before switch body") -MSG_DEF(JSMSG_COLON_AFTER_CASE, 83, 0, JSEXN_SYNTAXERR, "missing : after case label") -MSG_DEF(JSMSG_WHILE_AFTER_DO, 84, 0, JSEXN_SYNTAXERR, "missing while after do-loop body") -MSG_DEF(JSMSG_PAREN_AFTER_FOR, 85, 0, JSEXN_SYNTAXERR, "missing ( after for") -MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 86, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer") -MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 87, 0, JSEXN_SYNTAXERR, "missing ; after for-loop condition") -MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL, 88, 0, JSEXN_SYNTAXERR, "missing ) after for-loop control") -MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 89, 0, JSEXN_SYNTAXERR, "missing { before try block") -MSG_DEF(JSMSG_CURLY_AFTER_TRY, 90, 0, JSEXN_SYNTAXERR, "missing } after try block") -MSG_DEF(JSMSG_PAREN_BEFORE_CATCH, 91, 0, JSEXN_SYNTAXERR, "missing ( before catch") -MSG_DEF(JSMSG_CATCH_IDENTIFIER, 92, 0, JSEXN_SYNTAXERR, "missing identifier in catch") -MSG_DEF(JSMSG_PAREN_AFTER_CATCH, 93, 0, JSEXN_SYNTAXERR, "missing ) after catch") -MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 94, 0, JSEXN_SYNTAXERR, "missing { before catch block") -MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 95, 0, JSEXN_SYNTAXERR, "missing } after catch block") -MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 96, 0, JSEXN_SYNTAXERR, "missing { before finally block") -MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 97, 0, JSEXN_SYNTAXERR, "missing } after finally block") -MSG_DEF(JSMSG_CATCH_OR_FINALLY, 98, 0, JSEXN_SYNTAXERR, "missing catch or finally after try") -MSG_DEF(JSMSG_PAREN_BEFORE_WITH, 99, 0, JSEXN_SYNTAXERR, "missing ( before with-statement object") -MSG_DEF(JSMSG_PAREN_AFTER_WITH, 100, 0, JSEXN_SYNTAXERR, "missing ) after with-statement object") -MSG_DEF(JSMSG_CURLY_IN_COMPOUND, 101, 0, JSEXN_SYNTAXERR, "missing } in compound statement") -MSG_DEF(JSMSG_NO_VARIABLE_NAME, 102, 0, JSEXN_SYNTAXERR, "missing variable name") -MSG_DEF(JSMSG_COLON_IN_COND, 103, 0, JSEXN_SYNTAXERR, "missing : in conditional expression") -MSG_DEF(JSMSG_PAREN_AFTER_ARGS, 104, 0, JSEXN_SYNTAXERR, "missing ) after argument list") -MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 105, 0, JSEXN_SYNTAXERR, "missing ] after element list") -MSG_DEF(JSMSG_COLON_AFTER_ID, 106, 0, JSEXN_SYNTAXERR, "missing : after property id") -MSG_DEF(JSMSG_CURLY_AFTER_LIST, 107, 0, JSEXN_SYNTAXERR, "missing } after property list") -MSG_DEF(JSMSG_PAREN_IN_PAREN, 108, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical") -MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before statement") -MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value") -MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}") -MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}") -MSG_DEF(JSMSG_BAD_IMPORT, 113, 0, JSEXN_SYNTAXERR, "invalid import expression") -MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default") -MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases") -MSG_DEF(JSMSG_BAD_SWITCH, 116, 0, JSEXN_SYNTAXERR, "invalid switch statement") -MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE, 117, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side") -MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 118, 0, JSEXN_SYNTAXERR, "catch after unconditional catch") -MSG_DEF(JSMSG_CATCH_WITHOUT_TRY, 119, 0, JSEXN_SYNTAXERR, "catch without try") -MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 120, 0, JSEXN_SYNTAXERR, "finally without try") -MSG_DEF(JSMSG_LABEL_NOT_FOUND, 121, 0, JSEXN_SYNTAXERR, "label not found") -MSG_DEF(JSMSG_TOUGH_BREAK, 122, 0, JSEXN_SYNTAXERR, "invalid break") -MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "invalid continue") -MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 124, 1, JSEXN_SYNTAXERR, "{0} not in function") -MSG_DEF(JSMSG_BAD_LABEL, 125, 0, JSEXN_SYNTAXERR, "invalid label") -MSG_DEF(JSMSG_DUPLICATE_LABEL, 126, 0, JSEXN_SYNTAXERR, "duplicate label") -MSG_DEF(JSMSG_VAR_HIDES_ARG, 127, 1, JSEXN_TYPEERR, "variable {0} hides argument") -MSG_DEF(JSMSG_BAD_VAR_INIT, 128, 0, JSEXN_SYNTAXERR, "invalid variable initialization") -MSG_DEF(JSMSG_BAD_LEFTSIDE_OF_ASS, 129, 0, JSEXN_SYNTAXERR, "invalid assignment left-hand side") -MSG_DEF(JSMSG_BAD_OPERAND, 130, 1, JSEXN_SYNTAXERR, "invalid {0} operand") -MSG_DEF(JSMSG_BAD_PROP_ID, 131, 0, JSEXN_SYNTAXERR, "invalid property id") -MSG_DEF(JSMSG_RESERVED_ID, 132, 1, JSEXN_SYNTAXERR, "{0} is a reserved identifier") -MSG_DEF(JSMSG_SYNTAX_ERROR, 133, 0, JSEXN_SYNTAXERR, "syntax error") -MSG_DEF(JSMSG_BAD_SHARP_VAR_DEF, 134, 0, JSEXN_SYNTAXERR, "invalid sharp variable definition") -MSG_DEF(JSMSG_BAD_PROTOTYPE, 135, 1, JSEXN_TYPEERR, "'prototype' property of {0} is not an object") -MSG_DEF(JSMSG_MISSING_EXPONENT, 136, 0, JSEXN_SYNTAXERR, "missing exponent") -MSG_DEF(JSMSG_OUT_OF_MEMORY, 137, 0, JSEXN_ERR, "out of memory") -MSG_DEF(JSMSG_UNTERMINATED_STRING, 138, 0, JSEXN_SYNTAXERR, "unterminated string literal") -MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") -MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment") -MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal") -MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 142, 0, JSEXN_SYNTAXERR, "invalid flag after regular expression") -MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number") -MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character") -MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") -MSG_DEF(JSMSG_BAD_INDIRECT_CALL, 146, 1, JSEXN_EVALERR, "function {0} must be called directly, and not by way of a function of another name") -MSG_DEF(JSMSG_UNCAUGHT_EXCEPTION, 147, 1, JSEXN_INTERNALERR, "uncaught exception: {0}") -MSG_DEF(JSMSG_INVALID_BACKREF, 148, 0, JSEXN_SYNTAXERR, "non-octal digit in an escape sequence that doesn't match a back-reference") -MSG_DEF(JSMSG_BAD_BACKREF, 149, 0, JSEXN_SYNTAXERR, "back-reference exceeds number of capturing parentheses") -MSG_DEF(JSMSG_PRECISION_RANGE, 150, 1, JSEXN_RANGEERR, "precision {0} out of range") -MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 151, 1, JSEXN_SYNTAXERR, "invalid {0} usage") -MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 152, 0, JSEXN_RANGEERR, "invalid array length") -MSG_DEF(JSMSG_CANT_DESCRIBE_PROPS, 153, 1, JSEXN_TYPEERR, "can't describe non-native properties of class {0}") -MSG_DEF(JSMSG_BAD_APPLY_ARGS, 154, 1, JSEXN_TYPEERR, "second argument to Function.prototype.{0} must be an array") -MSG_DEF(JSMSG_REDECLARED_VAR, 155, 2, JSEXN_TYPEERR, "redeclaration of {0} {1}") -MSG_DEF(JSMSG_UNDECLARED_VAR, 156, 1, JSEXN_TYPEERR, "assignment to undeclared variable {0}") -MSG_DEF(JSMSG_ANON_NO_RETURN_VALUE, 157, 0, JSEXN_TYPEERR, "anonymous function does not always return a value") -MSG_DEF(JSMSG_DEPRECATED_USAGE, 158, 1, JSEXN_REFERENCEERR, "deprecated {0} usage") -MSG_DEF(JSMSG_BAD_URI, 159, 0, JSEXN_URIERR, "malformed URI sequence") -MSG_DEF(JSMSG_GETTER_ONLY, 160, 0, JSEXN_TYPEERR, "setting a property that has only a getter") -MSG_DEF(JSMSG_TRAILING_COMMA, 161, 0, JSEXN_SYNTAXERR, "trailing comma is not legal in ECMA-262 object initializers") -MSG_DEF(JSMSG_UNDEFINED_PROP, 162, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}") -MSG_DEF(JSMSG_USELESS_EXPR, 163, 0, JSEXN_TYPEERR, "useless expression") -MSG_DEF(JSMSG_REDECLARED_PARAM, 164, 1, JSEXN_TYPEERR, "redeclaration of formal parameter {0}") -MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags when constructing one RegExp from another") -MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range") -MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals") -MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects") -MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS, 169, 0, JSEXN_SYNTAXERR, "too many catch variables") -MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup") -MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character") -MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace") -MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name") -MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression") -MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 1, JSEXN_TYPEERR, "already executing generator {0}") -MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression") -MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}") -MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}") -MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}") -MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML") -MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList") -MSG_DEF(JSMSG_BAD_GENERATOR_SEND, 182, 1, JSEXN_TYPEERR, "attempt to send {0} to newborn generator") -MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute") -MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value") -MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 1, JSEXN_SYNTAXERR, "XML tag name mismatch (expected {0})") -MSG_DEF(JSMSG_BAD_XML_TAG_SYNTAX, 186, 0, JSEXN_SYNTAXERR, "invalid XML tag syntax") -MSG_DEF(JSMSG_BAD_XML_LIST_SYNTAX, 187, 0, JSEXN_SYNTAXERR, "invalid XML list syntax") -MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 188, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}") -MSG_DEF(JSMSG_CANT_SET_XML_ATTRS, 189, 0, JSEXN_INTERNALERR, "can't set XML property attributes") -MSG_DEF(JSMSG_END_OF_XML_SOURCE, 190, 0, JSEXN_SYNTAXERR, "unexpected end of XML source") -MSG_DEF(JSMSG_END_OF_XML_ENTITY, 191, 0, JSEXN_SYNTAXERR, "unexpected end of XML entity") -MSG_DEF(JSMSG_BAD_XML_QNAME, 192, 0, JSEXN_SYNTAXERR, "invalid XML qualified name") -MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP, 193, 0, JSEXN_SYNTAXERR, "invalid for each loop") -MSG_DEF(JSMSG_BAD_XMLLIST_PUT, 194, 1, JSEXN_TYPEERR, "can't set property {0} in XMLList") -MSG_DEF(JSMSG_UNKNOWN_XML_ENTITY, 195, 1, JSEXN_TYPEERR, "unknown XML entity {0}") -MSG_DEF(JSMSG_BAD_XML_NCR, 196, 1, JSEXN_TYPEERR, "malformed XML character {0}") -MSG_DEF(JSMSG_UNDEFINED_XML_NAME, 197, 1, JSEXN_REFERENCEERR, "reference to undefined XML name {0}") -MSG_DEF(JSMSG_DUPLICATE_XML_ATTR, 198, 1, JSEXN_TYPEERR, "duplicate XML attribute {0}") -MSG_DEF(JSMSG_TOO_MANY_FUN_VARS, 199, 0, JSEXN_SYNTAXERR, "too many local variables") -MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG, 200, 0, JSEXN_INTERNALERR, "array initialiser too large") -MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX, 201, 0, JSEXN_INTERNALERR, "regular expression too complex") -MSG_DEF(JSMSG_BUFFER_TOO_SMALL, 202, 0, JSEXN_INTERNALERR, "buffer too small") -MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 203, 1, JSEXN_TYPEERR, "bad surrogate character {0}") -MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large") -MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}") -MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called") -MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}") -MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") -MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") -MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (") -MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for") -MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value") -MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace") -MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}") -MSG_DEF(JSMSG_BAD_YIELD_SYNTAX, 215, 0, JSEXN_SYNTAXERR, "yield expression must be parenthesized") -MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side") -MSG_DEF(JSMSG_YIELD_FROM_FILTER, 217, 0, JSEXN_INTERNALERR, "yield not yet supported from filtering predicate") -MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 218, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing") -MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements") diff --git a/spidermonkey/libjs/jsapi.c b/spidermonkey/libjs/jsapi.c deleted file mode 100644 index f03fa36..0000000 --- a/spidermonkey/libjs/jsapi.c +++ /dev/null @@ -1,5011 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JavaScript API. - */ -#include "jsstddef.h" -#include -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdate.h" -#include "jsdtoa.h" -#include "jsemit.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsmath.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "prmjtime.h" - -#if JS_HAS_FILE_OBJECT -#include "jsfile.h" -#endif - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_GENERATORS -#include "jsiter.h" -#endif - -#ifdef HAVE_VA_LIST_AS_ARRAY -#define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap)) -#else -#define JS_ADDRESSOF_VA_LIST(ap) (&(ap)) -#endif - -#if defined(JS_PARANOID_REQUEST) && defined(JS_THREADSAFE) -#define CHECK_REQUEST(cx) JS_ASSERT(cx->requestDepth) -#else -#define CHECK_REQUEST(cx) ((void)0) -#endif - -JS_PUBLIC_API(int64) -JS_Now() -{ - return PRMJ_Now(); -} - -JS_PUBLIC_API(jsval) -JS_GetNaNValue(JSContext *cx) -{ - return DOUBLE_TO_JSVAL(cx->runtime->jsNaN); -} - -JS_PUBLIC_API(jsval) -JS_GetNegativeInfinityValue(JSContext *cx) -{ - return DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); -} - -JS_PUBLIC_API(jsval) -JS_GetPositiveInfinityValue(JSContext *cx) -{ - return DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); -} - -JS_PUBLIC_API(jsval) -JS_GetEmptyStringValue(JSContext *cx) -{ - return STRING_TO_JSVAL(cx->runtime->emptyString); -} - -static JSBool -TryArgumentFormatter(JSContext *cx, const char **formatp, JSBool fromJS, - jsval **vpp, va_list *app) -{ - const char *format; - JSArgumentFormatMap *map; - - format = *formatp; - for (map = cx->argumentFormatMap; map; map = map->next) { - if (!strncmp(format, map->format, map->length)) { - *formatp = format + map->length; - return map->formatter(cx, format, fromJS, vpp, app); - } - } - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_CHAR, format); - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, - ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, format); - ok = JS_ConvertArgumentsVA(cx, argc, argv, format, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, - const char *format, va_list ap) -{ - jsval *sp; - JSBool required; - char c; - JSFunction *fun; - jsdouble d; - JSString *str; - JSObject *obj; - - CHECK_REQUEST(cx); - sp = argv; - required = JS_TRUE; - while ((c = *format++) != '\0') { - if (isspace(c)) - continue; - if (c == '/') { - required = JS_FALSE; - continue; - } - if (sp == argv + argc) { - if (required) { - fun = js_ValueToFunction(cx, &argv[-2], 0); - if (fun) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", argc); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_MORE_ARGS_NEEDED, - JS_GetFunctionName(fun), numBuf, - (argc == 1) ? "" : "s"); - } - return JS_FALSE; - } - break; - } - switch (c) { - case 'b': - if (!js_ValueToBoolean(cx, *sp, va_arg(ap, JSBool *))) - return JS_FALSE; - break; - case 'c': - if (!js_ValueToUint16(cx, *sp, va_arg(ap, uint16 *))) - return JS_FALSE; - break; - case 'i': - if (!js_ValueToECMAInt32(cx, *sp, va_arg(ap, int32 *))) - return JS_FALSE; - break; - case 'u': - if (!js_ValueToECMAUint32(cx, *sp, va_arg(ap, uint32 *))) - return JS_FALSE; - break; - case 'j': - if (!js_ValueToInt32(cx, *sp, va_arg(ap, int32 *))) - return JS_FALSE; - break; - case 'd': - if (!js_ValueToNumber(cx, *sp, va_arg(ap, jsdouble *))) - return JS_FALSE; - break; - case 'I': - if (!js_ValueToNumber(cx, *sp, &d)) - return JS_FALSE; - *va_arg(ap, jsdouble *) = js_DoubleToInteger(d); - break; - case 's': - case 'S': - case 'W': - str = js_ValueToString(cx, *sp); - if (!str) - return JS_FALSE; - *sp = STRING_TO_JSVAL(str); - if (c == 's') - *va_arg(ap, char **) = JS_GetStringBytes(str); - else if (c == 'W') - *va_arg(ap, jschar **) = JS_GetStringChars(str); - else - *va_arg(ap, JSString **) = str; - break; - case 'o': - if (!js_ValueToObject(cx, *sp, &obj)) - return JS_FALSE; - *sp = OBJECT_TO_JSVAL(obj); - *va_arg(ap, JSObject **) = obj; - break; - case 'f': - obj = js_ValueToFunctionObject(cx, sp, 0); - if (!obj) - return JS_FALSE; - *va_arg(ap, JSFunction **) = (JSFunction *) JS_GetPrivate(cx, obj); - break; - case 'v': - *va_arg(ap, jsval *) = *sp; - break; - case '*': - break; - default: - format--; - if (!TryArgumentFormatter(cx, &format, JS_TRUE, &sp, - JS_ADDRESSOF_VA_LIST(ap))) { - return JS_FALSE; - } - /* NB: the formatter already updated sp, so we continue here. */ - continue; - } - sp++; - } - return JS_TRUE; -} - -JS_PUBLIC_API(jsval *) -JS_PushArguments(JSContext *cx, void **markp, const char *format, ...) -{ - va_list ap; - jsval *argv; - - va_start(ap, format); - argv = JS_PushArgumentsVA(cx, markp, format, ap); - va_end(ap); - return argv; -} - -JS_PUBLIC_API(jsval *) -JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap) -{ - uintN argc; - jsval *argv, *sp; - char c; - const char *cp; - JSString *str; - JSFunction *fun; - JSStackHeader *sh; - - CHECK_REQUEST(cx); - *markp = NULL; - argc = 0; - for (cp = format; (c = *cp) != '\0'; cp++) { - /* - * Count non-space non-star characters as individual jsval arguments. - * This may over-allocate stack, but we'll fix below. - */ - if (isspace(c) || c == '*') - continue; - argc++; - } - sp = js_AllocStack(cx, argc, markp); - if (!sp) - return NULL; - argv = sp; - while ((c = *format++) != '\0') { - if (isspace(c) || c == '*') - continue; - switch (c) { - case 'b': - *sp = BOOLEAN_TO_JSVAL((JSBool) va_arg(ap, int)); - break; - case 'c': - *sp = INT_TO_JSVAL((uint16) va_arg(ap, unsigned int)); - break; - case 'i': - case 'j': - if (!js_NewNumberValue(cx, (jsdouble) va_arg(ap, int32), sp)) - goto bad; - break; - case 'u': - if (!js_NewNumberValue(cx, (jsdouble) va_arg(ap, uint32), sp)) - goto bad; - break; - case 'd': - case 'I': - if (!js_NewDoubleValue(cx, va_arg(ap, jsdouble), sp)) - goto bad; - break; - case 's': - str = JS_NewStringCopyZ(cx, va_arg(ap, char *)); - if (!str) - goto bad; - *sp = STRING_TO_JSVAL(str); - break; - case 'W': - str = JS_NewUCStringCopyZ(cx, va_arg(ap, jschar *)); - if (!str) - goto bad; - *sp = STRING_TO_JSVAL(str); - break; - case 'S': - str = va_arg(ap, JSString *); - *sp = STRING_TO_JSVAL(str); - break; - case 'o': - *sp = OBJECT_TO_JSVAL(va_arg(ap, JSObject *)); - break; - case 'f': - fun = va_arg(ap, JSFunction *); - *sp = fun ? OBJECT_TO_JSVAL(fun->object) : JSVAL_NULL; - break; - case 'v': - *sp = va_arg(ap, jsval); - break; - default: - format--; - if (!TryArgumentFormatter(cx, &format, JS_FALSE, &sp, - JS_ADDRESSOF_VA_LIST(ap))) { - goto bad; - } - /* NB: the formatter already updated sp, so we continue here. */ - continue; - } - sp++; - } - - /* - * We may have overallocated stack due to a multi-character format code - * handled by a JSArgumentFormatter. Give back that stack space! - */ - JS_ASSERT(sp <= argv + argc); - if (sp < argv + argc) { - /* Return slots not pushed to the current stack arena. */ - cx->stackPool.current->avail = (jsuword)sp; - - /* Reduce the count of slots the GC will scan in this stack segment. */ - sh = cx->stackHeaders; - JS_ASSERT(JS_STACK_SEGMENT(sh) + sh->nslots == argv + argc); - sh->nslots -= argc - (sp - argv); - } - return argv; - -bad: - js_FreeStack(cx, *markp); - return NULL; -} - -JS_PUBLIC_API(void) -JS_PopArguments(JSContext *cx, void *mark) -{ - CHECK_REQUEST(cx); - js_FreeStack(cx, mark); -} - -JS_PUBLIC_API(JSBool) -JS_AddArgumentFormatter(JSContext *cx, const char *format, - JSArgumentFormatter formatter) -{ - size_t length; - JSArgumentFormatMap **mpp, *map; - - length = strlen(format); - mpp = &cx->argumentFormatMap; - while ((map = *mpp) != NULL) { - /* Insert before any shorter string to match before prefixes. */ - if (map->length < length) - break; - if (map->length == length && !strcmp(map->format, format)) - goto out; - mpp = &map->next; - } - map = (JSArgumentFormatMap *) JS_malloc(cx, sizeof *map); - if (!map) - return JS_FALSE; - map->format = format; - map->length = length; - map->next = *mpp; - *mpp = map; -out: - map->formatter = formatter; - return JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_RemoveArgumentFormatter(JSContext *cx, const char *format) -{ - size_t length; - JSArgumentFormatMap **mpp, *map; - - length = strlen(format); - mpp = &cx->argumentFormatMap; - while ((map = *mpp) != NULL) { - if (map->length == length && !strcmp(map->format, format)) { - *mpp = map->next; - JS_free(cx, map); - return; - } - mpp = &map->next; - } -} - -JS_PUBLIC_API(JSBool) -JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp) -{ - JSBool ok, b; - JSObject *obj; - JSString *str; - jsdouble d, *dp; - - CHECK_REQUEST(cx); - switch (type) { - case JSTYPE_VOID: - *vp = JSVAL_VOID; - ok = JS_TRUE; - break; - case JSTYPE_OBJECT: - ok = js_ValueToObject(cx, v, &obj); - if (ok) - *vp = OBJECT_TO_JSVAL(obj); - break; - case JSTYPE_FUNCTION: - *vp = v; - obj = js_ValueToFunctionObject(cx, vp, JSV2F_SEARCH_STACK); - ok = (obj != NULL); - break; - case JSTYPE_STRING: - str = js_ValueToString(cx, v); - ok = (str != NULL); - if (ok) - *vp = STRING_TO_JSVAL(str); - break; - case JSTYPE_NUMBER: - ok = js_ValueToNumber(cx, v, &d); - if (ok) { - dp = js_NewDouble(cx, d, 0); - ok = (dp != NULL); - if (ok) - *vp = DOUBLE_TO_JSVAL(dp); - } - break; - case JSTYPE_BOOLEAN: - ok = js_ValueToBoolean(cx, v, &b); - if (ok) - *vp = BOOLEAN_TO_JSVAL(b); - break; - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", (int)type); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_TYPE, - numBuf); - ok = JS_FALSE; - break; - } - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp) -{ - CHECK_REQUEST(cx); - return js_ValueToObject(cx, v, objp); -} - -JS_PUBLIC_API(JSFunction *) -JS_ValueToFunction(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); -} - -JS_PUBLIC_API(JSFunction *) -JS_ValueToConstructor(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ValueToFunction(cx, &v, JSV2F_SEARCH_STACK); -} - -JS_PUBLIC_API(JSString *) -JS_ValueToString(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ValueToString(cx, v); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) -{ - CHECK_REQUEST(cx); - return js_ValueToNumber(cx, v, dp); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToECMAInt32(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToECMAUint32(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToInt32(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) -{ - CHECK_REQUEST(cx); - return js_ValueToUint16(cx, v, ip); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) -{ - CHECK_REQUEST(cx); - return js_ValueToBoolean(cx, v, bp); -} - -JS_PUBLIC_API(JSType) -JS_TypeOfValue(JSContext *cx, jsval v) -{ - JSType type; - JSObject *obj; - JSObjectOps *ops; - JSClass *clasp; - - CHECK_REQUEST(cx); - if (JSVAL_IS_OBJECT(v)) { - type = JSTYPE_OBJECT; /* XXXbe JSTYPE_NULL for JS2 */ - obj = JSVAL_TO_OBJECT(v); - if (obj) { - ops = obj->map->ops; -#if JS_HAS_XML_SUPPORT - if (ops == &js_XMLObjectOps.base) { - type = JSTYPE_XML; - } else -#endif - { - /* - * ECMA 262, 11.4.3 says that any native object that implements - * [[Call]] should be of type "function". Note that RegExp and - * Script are both of type "function" for compatibility with - * older SpiderMonkeys. - */ - clasp = OBJ_GET_CLASS(cx, obj); - if ((ops == &js_ObjectOps) - ? (clasp->call - ? (clasp == &js_RegExpClass || clasp == &js_ScriptClass) - : clasp == &js_FunctionClass) - : ops->call != NULL) { - type = JSTYPE_FUNCTION; - } else { -#ifdef NARCISSUS - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .callAtom), - &v)) { - JS_ClearPendingException(cx); - } else if (VALUE_IS_FUNCTION(cx, v)) { - type = JSTYPE_FUNCTION; - } -#endif - } - } - } - } else if (JSVAL_IS_NUMBER(v)) { - type = JSTYPE_NUMBER; - } else if (JSVAL_IS_STRING(v)) { - type = JSTYPE_STRING; - } else if (JSVAL_IS_BOOLEAN(v)) { - type = JSTYPE_BOOLEAN; - } else { - type = JSTYPE_VOID; - } - return type; -} - -JS_PUBLIC_API(const char *) -JS_GetTypeName(JSContext *cx, JSType type) -{ - if ((uintN)type >= (uintN)JSTYPE_LIMIT) - return NULL; - return js_type_strs[type]; -} - -/************************************************************************/ - -JS_PUBLIC_API(JSRuntime *) -JS_NewRuntime(uint32 maxbytes) -{ - JSRuntime *rt; - -#ifdef DEBUG - static JSBool didFirstChecks; - - if (!didFirstChecks) { - /* - * This code asserts that the numbers associated with the error names - * in jsmsg.def are monotonically increasing. It uses values for the - * error names enumerated in jscntxt.c. It's not a compile-time check - * but it's better than nothing. - */ - int errorNumber = 0; -#define MSG_DEF(name, number, count, exception, format) \ - JS_ASSERT(name == errorNumber++); -#include "js.msg" -#undef MSG_DEF - -#define MSG_DEF(name, number, count, exception, format) \ - JS_BEGIN_MACRO \ - uintN numfmtspecs = 0; \ - const char *fmt; \ - for (fmt = format; *fmt != '\0'; fmt++) { \ - if (*fmt == '{' && isdigit(fmt[1])) \ - ++numfmtspecs; \ - } \ - JS_ASSERT(count == numfmtspecs); \ - JS_END_MACRO; -#include "js.msg" -#undef MSG_DEF - - didFirstChecks = JS_TRUE; - } -#endif /* DEBUG */ - - rt = (JSRuntime *) malloc(sizeof(JSRuntime)); - if (!rt) - return NULL; - - /* Initialize infallibly first, so we can goto bad and JS_DestroyRuntime. */ - memset(rt, 0, sizeof(JSRuntime)); - JS_INIT_CLIST(&rt->contextList); - JS_INIT_CLIST(&rt->trapList); - JS_INIT_CLIST(&rt->watchPointList); - - if (!js_InitGC(rt, maxbytes)) - goto bad; -#ifdef JS_THREADSAFE - if (PR_FAILURE == PR_NewThreadPrivateIndex(&rt->threadTPIndex, - js_ThreadDestructorCB)) { - goto bad; - } - rt->gcLock = JS_NEW_LOCK(); - if (!rt->gcLock) - goto bad; - rt->gcDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->gcDone) - goto bad; - rt->requestDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->requestDone) - goto bad; - /* this is asymmetric with JS_ShutDown: */ - if (!js_SetupLocks(8, 16)) - goto bad; - rt->rtLock = JS_NEW_LOCK(); - if (!rt->rtLock) - goto bad; - rt->stateChange = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->stateChange) - goto bad; - rt->setSlotLock = JS_NEW_LOCK(); - if (!rt->setSlotLock) - goto bad; - rt->setSlotDone = JS_NEW_CONDVAR(rt->setSlotLock); - if (!rt->setSlotDone) - goto bad; - rt->scopeSharingDone = JS_NEW_CONDVAR(rt->gcLock); - if (!rt->scopeSharingDone) - goto bad; - rt->scopeSharingTodo = NO_SCOPE_SHARING_TODO; -#endif - rt->propertyCache.empty = JS_TRUE; - if (!js_InitPropertyTree(rt)) - goto bad; - return rt; - -bad: - JS_DestroyRuntime(rt); - return NULL; -} - -JS_PUBLIC_API(void) -JS_DestroyRuntime(JSRuntime *rt) -{ -#ifdef DEBUG - /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */ - if (!JS_CLIST_IS_EMPTY(&rt->contextList)) { - JSContext *cx, *iter = NULL; - uintN cxcount = 0; - while ((cx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) - cxcount++; - fprintf(stderr, -"JS API usage error: %u contexts left in runtime upon JS_DestroyRuntime.\n", - cxcount); - } -#endif - - js_FreeRuntimeScriptState(rt); - js_FinishAtomState(&rt->atomState); - js_FinishGC(rt); -#ifdef JS_THREADSAFE - if (rt->gcLock) - JS_DESTROY_LOCK(rt->gcLock); - if (rt->gcDone) - JS_DESTROY_CONDVAR(rt->gcDone); - if (rt->requestDone) - JS_DESTROY_CONDVAR(rt->requestDone); - if (rt->rtLock) - JS_DESTROY_LOCK(rt->rtLock); - if (rt->stateChange) - JS_DESTROY_CONDVAR(rt->stateChange); - if (rt->setSlotLock) - JS_DESTROY_LOCK(rt->setSlotLock); - if (rt->setSlotDone) - JS_DESTROY_CONDVAR(rt->setSlotDone); - if (rt->scopeSharingDone) - JS_DESTROY_CONDVAR(rt->scopeSharingDone); -#else - GSN_CACHE_CLEAR(&rt->gsnCache); -#endif - js_FinishPropertyTree(rt); - free(rt); -} - -JS_PUBLIC_API(void) -JS_ShutDown(void) -{ - js_FinishDtoa(); -#ifdef JS_THREADSAFE - js_CleanupLocks(); -#endif -} - -JS_PUBLIC_API(void *) -JS_GetRuntimePrivate(JSRuntime *rt) -{ - return rt->data; -} - -JS_PUBLIC_API(void) -JS_SetRuntimePrivate(JSRuntime *rt, void *data) -{ - rt->data = data; -} - -#ifdef JS_THREADSAFE - -JS_PUBLIC_API(void) -JS_BeginRequest(JSContext *cx) -{ - JSRuntime *rt; - - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); - if (!cx->requestDepth) { - /* Wait until the GC is finished. */ - rt = cx->runtime; - JS_LOCK_GC(rt); - - /* NB: we use cx->thread here, not js_GetCurrentThread(). */ - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - } - - /* Indicate that a request is running. */ - rt->requestCount++; - cx->requestDepth = 1; - JS_UNLOCK_GC(rt); - return; - } - cx->requestDepth++; -} - -JS_PUBLIC_API(void) -JS_EndRequest(JSContext *cx) -{ - JSRuntime *rt; - JSScope *scope, **todop; - uintN nshares; - - CHECK_REQUEST(cx); - JS_ASSERT(cx->requestDepth > 0); - if (cx->requestDepth == 1) { - /* Lock before clearing to interlock with ClaimScope, in jslock.c. */ - rt = cx->runtime; - JS_LOCK_GC(rt); - cx->requestDepth = 0; - - /* See whether cx has any single-threaded scopes to start sharing. */ - todop = &rt->scopeSharingTodo; - nshares = 0; - while ((scope = *todop) != NO_SCOPE_SHARING_TODO) { - if (scope->ownercx != cx) { - todop = &scope->u.link; - continue; - } - *todop = scope->u.link; - scope->u.link = NULL; /* null u.link for sanity ASAP */ - - /* - * If js_DropObjectMap returns null, we held the last ref to scope. - * The waiting thread(s) must have been killed, after which the GC - * collected the object that held this scope. Unlikely, because it - * requires that the GC ran (e.g., from a branch callback) during - * this request, but possible. - */ - if (js_DropObjectMap(cx, &scope->map, NULL)) { - js_InitLock(&scope->lock); - scope->u.count = 0; /* NULL may not pun as 0 */ - js_FinishSharingScope(rt, scope); /* set ownercx = NULL */ - nshares++; - } - } - if (nshares) - JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); - - /* Give the GC a chance to run if this was the last request running. */ - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - - JS_UNLOCK_GC(rt); - return; - } - - cx->requestDepth--; -} - -/* Yield to pending GC operations, regardless of request depth */ -JS_PUBLIC_API(void) -JS_YieldRequest(JSContext *cx) -{ - JSRuntime *rt; - - JS_ASSERT(cx->thread); - CHECK_REQUEST(cx); - - rt = cx->runtime; - JS_LOCK_GC(rt); - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - JS_UNLOCK_GC(rt); - /* XXXbe give the GC or another request calling it a chance to run here? - Assumes FIFO scheduling */ - JS_LOCK_GC(rt); - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - } - rt->requestCount++; - JS_UNLOCK_GC(rt); -} - -JS_PUBLIC_API(jsrefcount) -JS_SuspendRequest(JSContext *cx) -{ - jsrefcount saveDepth = cx->requestDepth; - - while (cx->requestDepth) - JS_EndRequest(cx); - return saveDepth; -} - -JS_PUBLIC_API(void) -JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth) -{ - JS_ASSERT(!cx->requestDepth); - while (--saveDepth >= 0) - JS_BeginRequest(cx); -} - -#endif /* JS_THREADSAFE */ - -JS_PUBLIC_API(void) -JS_Lock(JSRuntime *rt) -{ - JS_LOCK_RUNTIME(rt); -} - -JS_PUBLIC_API(void) -JS_Unlock(JSRuntime *rt) -{ - JS_UNLOCK_RUNTIME(rt); -} - -JS_PUBLIC_API(JSContextCallback) -JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback) -{ - JSContextCallback old; - - old = rt->cxCallback; - rt->cxCallback = cxCallback; - return old; -} - -JS_PUBLIC_API(JSContext *) -JS_NewContext(JSRuntime *rt, size_t stackChunkSize) -{ - return js_NewContext(rt, stackChunkSize); -} - -JS_PUBLIC_API(void) -JS_DestroyContext(JSContext *cx) -{ - js_DestroyContext(cx, JSDCM_FORCE_GC); -} - -JS_PUBLIC_API(void) -JS_DestroyContextNoGC(JSContext *cx) -{ - js_DestroyContext(cx, JSDCM_NO_GC); -} - -JS_PUBLIC_API(void) -JS_DestroyContextMaybeGC(JSContext *cx) -{ - js_DestroyContext(cx, JSDCM_MAYBE_GC); -} - -JS_PUBLIC_API(void *) -JS_GetContextPrivate(JSContext *cx) -{ - return cx->data; -} - -JS_PUBLIC_API(void) -JS_SetContextPrivate(JSContext *cx, void *data) -{ - cx->data = data; -} - -JS_PUBLIC_API(JSRuntime *) -JS_GetRuntime(JSContext *cx) -{ - return cx->runtime; -} - -JS_PUBLIC_API(JSContext *) -JS_ContextIterator(JSRuntime *rt, JSContext **iterp) -{ - return js_ContextIterator(rt, JS_TRUE, iterp); -} - -JS_PUBLIC_API(JSVersion) -JS_GetVersion(JSContext *cx) -{ - return cx->version & JSVERSION_MASK; -} - -JS_PUBLIC_API(JSVersion) -JS_SetVersion(JSContext *cx, JSVersion version) -{ - JSVersion oldVersion; - - JS_ASSERT(version != JSVERSION_UNKNOWN); - JS_ASSERT((version & ~JSVERSION_MASK) == 0); - - oldVersion = cx->version & JSVERSION_MASK; - if (version == oldVersion) - return oldVersion; - - /* We no longer support 1.4 or below. */ - if (version != JSVERSION_DEFAULT && version <= JSVERSION_1_4) - return oldVersion; - - cx->version = (cx->version & ~JSVERSION_MASK) | version; - js_OnVersionChange(cx); - return oldVersion; -} - -static struct v2smap { - JSVersion version; - const char *string; -} v2smap[] = { - {JSVERSION_1_0, "1.0"}, - {JSVERSION_1_1, "1.1"}, - {JSVERSION_1_2, "1.2"}, - {JSVERSION_1_3, "1.3"}, - {JSVERSION_1_4, "1.4"}, - {JSVERSION_ECMA_3, "ECMAv3"}, - {JSVERSION_1_5, "1.5"}, - {JSVERSION_1_6, "1.6"}, - {JSVERSION_1_7, "1.7"}, - {JSVERSION_DEFAULT, js_default_str}, - {JSVERSION_UNKNOWN, NULL}, /* must be last, NULL is sentinel */ -}; - -JS_PUBLIC_API(const char *) -JS_VersionToString(JSVersion version) -{ - int i; - - for (i = 0; v2smap[i].string; i++) - if (v2smap[i].version == version) - return v2smap[i].string; - return "unknown"; -} - -JS_PUBLIC_API(JSVersion) -JS_StringToVersion(const char *string) -{ - int i; - - for (i = 0; v2smap[i].string; i++) - if (strcmp(v2smap[i].string, string) == 0) - return v2smap[i].version; - return JSVERSION_UNKNOWN; -} - -JS_PUBLIC_API(uint32) -JS_GetOptions(JSContext *cx) -{ - return cx->options; -} - -#define SYNC_OPTIONS_TO_VERSION(cx) \ - JS_BEGIN_MACRO \ - if ((cx)->options & JSOPTION_XML) \ - (cx)->version |= JSVERSION_HAS_XML; \ - else \ - (cx)->version &= ~JSVERSION_HAS_XML; \ - JS_END_MACRO - -JS_PUBLIC_API(uint32) -JS_SetOptions(JSContext *cx, uint32 options) -{ - uint32 oldopts = cx->options; - cx->options = options; - SYNC_OPTIONS_TO_VERSION(cx); - return oldopts; -} - -JS_PUBLIC_API(uint32) -JS_ToggleOptions(JSContext *cx, uint32 options) -{ - uint32 oldopts = cx->options; - cx->options ^= options; - SYNC_OPTIONS_TO_VERSION(cx); - return oldopts; -} - -JS_PUBLIC_API(const char *) -JS_GetImplementationVersion(void) -{ - return "JavaScript-C 1.7.0 2007-10-03"; -} - - -JS_PUBLIC_API(JSObject *) -JS_GetGlobalObject(JSContext *cx) -{ - return cx->globalObject; -} - -JS_PUBLIC_API(void) -JS_SetGlobalObject(JSContext *cx, JSObject *obj) -{ - cx->globalObject = obj; - -#if JS_HAS_XML_SUPPORT - cx->xmlSettingFlags = 0; -#endif -} - -JSObject * -js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj) -{ - JSDHashTable *table; - JSBool resolving; - JSRuntime *rt; - JSResolvingKey key; - JSResolvingEntry *entry; - JSObject *fun_proto, *obj_proto; - - /* If cx has no global object, use obj so prototypes can be found. */ - if (!cx->globalObject) - JS_SetGlobalObject(cx, obj); - - /* Record Function and Object in cx->resolvingTable, if we are resolving. */ - table = cx->resolvingTable; - resolving = (table && table->entryCount); - rt = cx->runtime; - key.obj = obj; - if (resolving) { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, &key, JS_DHASH_ADD); - if (entry && entry->key.obj && (entry->flags & JSRESFLAG_LOOKUP)) { - /* Already resolving Function, record Object too. */ - JS_ASSERT(entry->key.obj == obj); - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, &key, JS_DHASH_ADD); - } - if (!entry) { - JS_ReportOutOfMemory(cx); - return NULL; - } - JS_ASSERT(!entry->key.obj && entry->flags == 0); - entry->key = key; - entry->flags = JSRESFLAG_LOOKUP; - } else { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) - return NULL; - - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function]); - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - return NULL; - } - - table = cx->resolvingTable; - } - - /* Initialize the function class first so constructors can be made. */ - fun_proto = js_InitFunctionClass(cx, obj); - if (!fun_proto) - goto out; - - /* Initialize the object class next so Object.prototype works. */ - obj_proto = js_InitObjectClass(cx, obj); - if (!obj_proto) { - fun_proto = NULL; - goto out; - } - - /* Function.prototype and the global object delegate to Object.prototype. */ - OBJ_SET_PROTO(cx, fun_proto, obj_proto); - if (!OBJ_GET_PROTO(cx, obj)) - OBJ_SET_PROTO(cx, obj, obj_proto); - -out: - /* If resolving, remove the other entry (Object or Function) from table. */ - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - if (!resolving) { - /* If not resolving, remove the first entry added above, for Object. */ - JS_ASSERT(key.id == \ - ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Function])); - key.id = ATOM_TO_JSID(rt->atomState.classAtoms[JSProto_Object]); - JS_DHashTableOperate(table, &key, JS_DHASH_REMOVE); - } - return fun_proto; -} - -JS_PUBLIC_API(JSBool) -JS_InitStandardClasses(JSContext *cx, JSObject *obj) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - - /* Define a top-level property 'undefined' with the undefined value. */ - atom = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; - if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, - NULL, NULL, JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - - /* Function and Object require cooperative bootstrapping magic. */ - if (!js_InitFunctionAndObjectClasses(cx, obj)) - return JS_FALSE; - - /* Initialize the rest of the standard objects and functions. */ - return js_InitArrayClass(cx, obj) && - js_InitBlockClass(cx, obj) && - js_InitBooleanClass(cx, obj) && - js_InitCallClass(cx, obj) && - js_InitExceptionClasses(cx, obj) && - js_InitMathClass(cx, obj) && - js_InitNumberClass(cx, obj) && - js_InitRegExpClass(cx, obj) && - js_InitStringClass(cx, obj) && -#if JS_HAS_SCRIPT_OBJECT - js_InitScriptClass(cx, obj) && -#endif -#if JS_HAS_XML_SUPPORT - js_InitXMLClasses(cx, obj) && -#endif -#if JS_HAS_FILE_OBJECT - js_InitFileClass(cx, obj) && -#endif -#if JS_HAS_GENERATORS - js_InitIteratorClasses(cx, obj) && -#endif - js_InitDateClass(cx, obj); -} - -#define ATOM_OFFSET(name) offsetof(JSAtomState,name##Atom) -#define CLASS_ATOM_OFFSET(name) offsetof(JSAtomState,classAtoms[JSProto_##name]) -#define OFFSET_TO_ATOM(rt,off) (*(JSAtom **)((char*)&(rt)->atomState + (off))) -#define CLASP(name) (JSClass *)&js_##name##Class - -#define EAGER_ATOM(name) ATOM_OFFSET(name), NULL -#define EAGER_CLASS_ATOM(name) CLASS_ATOM_OFFSET(name), NULL -#define EAGER_ATOM_AND_CLASP(name) EAGER_CLASS_ATOM(name), CLASP(name) -#define LAZY_ATOM(name) ATOM_OFFSET(lazy.name), js_##name##_str - -typedef struct JSStdName { - JSObjectOp init; - size_t atomOffset; /* offset of atom pointer in JSAtomState */ - const char *name; /* null if atom is pre-pinned, else name */ - JSClass *clasp; -} JSStdName; - -static JSAtom * -StdNameToAtom(JSContext *cx, JSStdName *stdn) -{ - size_t offset; - JSAtom *atom; - const char *name; - - offset = stdn->atomOffset; - atom = OFFSET_TO_ATOM(cx->runtime, offset); - if (!atom) { - name = stdn->name; - if (name) { - atom = js_Atomize(cx, name, strlen(name), ATOM_PINNED); - OFFSET_TO_ATOM(cx->runtime, offset) = atom; - } - } - return atom; -} - -/* - * Table of class initializers and their atom offsets in rt->atomState. - * If you add a "standard" class, remember to update this table. - */ -static JSStdName standard_class_atoms[] = { - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Function)}, - {js_InitFunctionAndObjectClasses, EAGER_ATOM_AND_CLASP(Object)}, - {js_InitArrayClass, EAGER_ATOM_AND_CLASP(Array)}, - {js_InitBlockClass, EAGER_ATOM_AND_CLASP(Block)}, - {js_InitBooleanClass, EAGER_ATOM_AND_CLASP(Boolean)}, - {js_InitDateClass, EAGER_ATOM_AND_CLASP(Date)}, - {js_InitMathClass, EAGER_ATOM_AND_CLASP(Math)}, - {js_InitNumberClass, EAGER_ATOM_AND_CLASP(Number)}, - {js_InitStringClass, EAGER_ATOM_AND_CLASP(String)}, - {js_InitCallClass, EAGER_ATOM_AND_CLASP(Call)}, - {js_InitExceptionClasses, EAGER_ATOM_AND_CLASP(Error)}, - {js_InitRegExpClass, EAGER_ATOM_AND_CLASP(RegExp)}, -#if JS_HAS_SCRIPT_OBJECT - {js_InitScriptClass, EAGER_ATOM_AND_CLASP(Script)}, -#endif -#if JS_HAS_XML_SUPPORT - {js_InitXMLClass, EAGER_ATOM_AND_CLASP(XML)}, - {js_InitNamespaceClass, EAGER_ATOM_AND_CLASP(Namespace)}, - {js_InitQNameClass, EAGER_ATOM_AND_CLASP(QName)}, -#endif -#if JS_HAS_FILE_OBJECT - {js_InitFileClass, EAGER_ATOM_AND_CLASP(File)}, -#endif -#if JS_HAS_GENERATORS - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(StopIteration)}, -#endif - {NULL, 0, NULL, NULL} -}; - -/* - * Table of top-level function and constant names and their init functions. - * If you add a "standard" global function or property, remember to update - * this table. - */ -static JSStdName standard_class_names[] = { - /* ECMA requires that eval be a direct property of the global object. */ - {js_InitObjectClass, EAGER_ATOM(eval), NULL}, - - /* Global properties and functions defined by the Number class. */ - {js_InitNumberClass, LAZY_ATOM(NaN), NULL}, - {js_InitNumberClass, LAZY_ATOM(Infinity), NULL}, - {js_InitNumberClass, LAZY_ATOM(isNaN), NULL}, - {js_InitNumberClass, LAZY_ATOM(isFinite), NULL}, - {js_InitNumberClass, LAZY_ATOM(parseFloat), NULL}, - {js_InitNumberClass, LAZY_ATOM(parseInt), NULL}, - - /* String global functions. */ - {js_InitStringClass, LAZY_ATOM(escape), NULL}, - {js_InitStringClass, LAZY_ATOM(unescape), NULL}, - {js_InitStringClass, LAZY_ATOM(decodeURI), NULL}, - {js_InitStringClass, LAZY_ATOM(encodeURI), NULL}, - {js_InitStringClass, LAZY_ATOM(decodeURIComponent), NULL}, - {js_InitStringClass, LAZY_ATOM(encodeURIComponent), NULL}, -#if JS_HAS_UNEVAL - {js_InitStringClass, LAZY_ATOM(uneval), NULL}, -#endif - - /* Exception constructors. */ - {js_InitExceptionClasses, EAGER_CLASS_ATOM(Error), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(InternalError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(EvalError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(RangeError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(TypeError), CLASP(Error)}, - {js_InitExceptionClasses, EAGER_CLASS_ATOM(URIError), CLASP(Error)}, - -#if JS_HAS_XML_SUPPORT - {js_InitAnyNameClass, EAGER_ATOM_AND_CLASP(AnyName)}, - {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)}, - {js_InitXMLClass, LAZY_ATOM(XMLList), &js_XMLClass}, - {js_InitXMLClass, LAZY_ATOM(isXMLName), NULL}, -#endif - -#if JS_HAS_GENERATORS - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)}, - {js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)}, -#endif - - {NULL, 0, NULL, NULL} -}; - -static JSStdName object_prototype_names[] = { - /* Object.prototype properties (global delegates to Object.prototype). */ - {js_InitObjectClass, EAGER_ATOM(proto), NULL}, - {js_InitObjectClass, EAGER_ATOM(parent), NULL}, - {js_InitObjectClass, EAGER_ATOM(count), NULL}, -#if JS_HAS_TOSOURCE - {js_InitObjectClass, EAGER_ATOM(toSource), NULL}, -#endif - {js_InitObjectClass, EAGER_ATOM(toString), NULL}, - {js_InitObjectClass, EAGER_ATOM(toLocaleString), NULL}, - {js_InitObjectClass, EAGER_ATOM(valueOf), NULL}, -#if JS_HAS_OBJ_WATCHPOINT - {js_InitObjectClass, LAZY_ATOM(watch), NULL}, - {js_InitObjectClass, LAZY_ATOM(unwatch), NULL}, -#endif - {js_InitObjectClass, LAZY_ATOM(hasOwnProperty), NULL}, - {js_InitObjectClass, LAZY_ATOM(isPrototypeOf), NULL}, - {js_InitObjectClass, LAZY_ATOM(propertyIsEnumerable), NULL}, -#if JS_HAS_GETTER_SETTER - {js_InitObjectClass, LAZY_ATOM(defineGetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(defineSetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(lookupGetter), NULL}, - {js_InitObjectClass, LAZY_ATOM(lookupSetter), NULL}, -#endif - - {NULL, 0, NULL, NULL} -}; - -JS_PUBLIC_API(JSBool) -JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, - JSBool *resolved) -{ - JSString *idstr; - JSRuntime *rt; - JSAtom *atom; - JSStdName *stdnm; - uintN i; - - CHECK_REQUEST(cx); - *resolved = JS_FALSE; - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - idstr = JSVAL_TO_STRING(id); - rt = cx->runtime; - - /* Check whether we're resolving 'undefined', and define it if so. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - if (idstr == ATOM_TO_STRING(atom)) { - *resolved = JS_TRUE; - return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, - NULL, NULL, JSPROP_PERMANENT, NULL); - } - - /* Try for class constructors/prototypes named by well-known atoms. */ - stdnm = NULL; - for (i = 0; standard_class_atoms[i].init; i++) { - atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); - if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_atoms[i]; - break; - } - } - - if (!stdnm) { - /* Try less frequently used top-level functions and constants. */ - for (i = 0; standard_class_names[i].init; i++) { - atom = StdNameToAtom(cx, &standard_class_names[i]); - if (!atom) - return JS_FALSE; - if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_names[i]; - break; - } - } - - if (!stdnm && !OBJ_GET_PROTO(cx, obj)) { - /* - * Try even less frequently used names delegated from the global - * object to Object.prototype, but only if the Object class hasn't - * yet been initialized. - */ - for (i = 0; object_prototype_names[i].init; i++) { - atom = StdNameToAtom(cx, &object_prototype_names[i]); - if (!atom) - return JS_FALSE; - if (idstr == ATOM_TO_STRING(atom)) { - stdnm = &standard_class_names[i]; - break; - } - } - } - } - - if (stdnm) { - /* - * If this standard class is anonymous and obj advertises itself as a - * global object (in order to reserve slots for standard class object - * pointers), then we don't want to resolve by name. - * - * If inversely, either id does not name a class, or id does not name - * an anonymous class, or the global does not reserve slots for class - * objects, then we must call the init hook here. - */ - if (stdnm->clasp && - (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS) && - (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { - return JS_TRUE; - } - - if (!stdnm->init(cx, obj)) - return JS_FALSE; - *resolved = JS_TRUE; - } - return JS_TRUE; -} - -static JSBool -AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom) -{ - JSScopeProperty *sprop; - JSScope *scope; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); - JS_UNLOCK_SCOPE(cx, scope); - return sprop != NULL; -} - -JS_PUBLIC_API(JSBool) -JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSAtom *atom; - uintN i; - - CHECK_REQUEST(cx); - rt = cx->runtime; - - /* Check whether we need to bind 'undefined' and define it if so. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - if (!AlreadyHasOwnProperty(cx, obj, atom) && - !OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID, - NULL, NULL, JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - - /* Initialize any classes that have not been resolved yet. */ - for (i = 0; standard_class_atoms[i].init; i++) { - atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset); - if (!AlreadyHasOwnProperty(cx, obj, atom) && - !standard_class_atoms[i].init(cx, obj)) { - return JS_FALSE; - } - } - - return JS_TRUE; -} - -static JSIdArray * -AddAtomToArray(JSContext *cx, JSAtom *atom, JSIdArray *ida, jsint *ip) -{ - jsint i, length; - - i = *ip; - length = ida->length; - if (i >= length) { - ida = js_SetIdArrayLength(cx, ida, JS_MAX(length * 2, 8)); - if (!ida) - return NULL; - JS_ASSERT(i < ida->length); - } - ida->vector[i] = ATOM_TO_JSID(atom); - *ip = i + 1; - return ida; -} - -static JSIdArray * -EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida, - jsint *ip, JSBool *foundp) -{ - *foundp = AlreadyHasOwnProperty(cx, obj, atom); - if (*foundp) - ida = AddAtomToArray(cx, atom, ida, ip); - return ida; -} - -JS_PUBLIC_API(JSIdArray *) -JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, - JSIdArray *ida) -{ - JSRuntime *rt; - jsint i, j, k; - JSAtom *atom; - JSBool found; - JSObjectOp init; - - CHECK_REQUEST(cx); - rt = cx->runtime; - if (ida) { - i = ida->length; - } else { - ida = js_NewIdArray(cx, 8); - if (!ida) - return NULL; - i = 0; - } - - /* Check whether 'undefined' has been resolved and enumerate it if so. */ - atom = rt->atomState.typeAtoms[JSTYPE_VOID]; - ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); - if (!ida) - return NULL; - - /* Enumerate only classes that *have* been resolved. */ - for (j = 0; standard_class_atoms[j].init; j++) { - atom = OFFSET_TO_ATOM(rt, standard_class_atoms[j].atomOffset); - ida = EnumerateIfResolved(cx, obj, atom, ida, &i, &found); - if (!ida) - return NULL; - - if (found) { - init = standard_class_atoms[j].init; - - for (k = 0; standard_class_names[k].init; k++) { - if (standard_class_names[k].init == init) { - atom = StdNameToAtom(cx, &standard_class_names[k]); - ida = AddAtomToArray(cx, atom, ida, &i); - if (!ida) - return NULL; - } - } - - if (init == js_InitObjectClass) { - for (k = 0; object_prototype_names[k].init; k++) { - atom = StdNameToAtom(cx, &object_prototype_names[k]); - ida = AddAtomToArray(cx, atom, ida, &i); - if (!ida) - return NULL; - } - } - } - } - - /* Trim to exact length via js_SetIdArrayLength. */ - return js_SetIdArrayLength(cx, ida, i); -} - -#undef ATOM_OFFSET -#undef CLASS_ATOM_OFFSET -#undef OFFSET_TO_ATOM -#undef CLASP - -#undef EAGER_ATOM -#undef EAGER_CLASS_ATOM -#undef EAGER_ATOM_CLASP -#undef LAZY_ATOM - -JS_PUBLIC_API(JSBool) -JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp) -{ - CHECK_REQUEST(cx); - return js_GetClassObject(cx, obj, key, objp); -} - -JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx) -{ - JSStackFrame *fp; - - fp = cx->fp; - if (!fp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE); - return NULL; - } - return js_GetScopeChain(cx, fp); -} - -JS_PUBLIC_API(void *) -JS_malloc(JSContext *cx, size_t nbytes) -{ - void *p; - - JS_ASSERT(nbytes != 0); - if (nbytes == 0) - nbytes = 1; - - p = malloc(nbytes); - if (!p) { - JS_ReportOutOfMemory(cx); - return NULL; - } - js_UpdateMallocCounter(cx, nbytes); - - return p; -} - -JS_PUBLIC_API(void *) -JS_realloc(JSContext *cx, void *p, size_t nbytes) -{ - p = realloc(p, nbytes); - if (!p) - JS_ReportOutOfMemory(cx); - return p; -} - -JS_PUBLIC_API(void) -JS_free(JSContext *cx, void *p) -{ - if (p) - free(p); -} - -JS_PUBLIC_API(char *) -JS_strdup(JSContext *cx, const char *s) -{ - size_t n; - void *p; - - n = strlen(s) + 1; - p = JS_malloc(cx, n); - if (!p) - return NULL; - return (char *)memcpy(p, s, n); -} - -JS_PUBLIC_API(jsdouble *) -JS_NewDouble(JSContext *cx, jsdouble d) -{ - CHECK_REQUEST(cx); - return js_NewDouble(cx, d, 0); -} - -JS_PUBLIC_API(JSBool) -JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) -{ - CHECK_REQUEST(cx); - return js_NewDoubleValue(cx, d, rval); -} - -JS_PUBLIC_API(JSBool) -JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) -{ - CHECK_REQUEST(cx); - return js_NewNumberValue(cx, d, rval); -} - -#undef JS_AddRoot -JS_PUBLIC_API(JSBool) -JS_AddRoot(JSContext *cx, void *rp) -{ - CHECK_REQUEST(cx); - return js_AddRoot(cx, rp, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name) -{ - return js_AddRootRT(rt, rp, name); -} - -JS_PUBLIC_API(JSBool) -JS_RemoveRoot(JSContext *cx, void *rp) -{ - CHECK_REQUEST(cx); - return js_RemoveRoot(cx->runtime, rp); -} - -JS_PUBLIC_API(JSBool) -JS_RemoveRootRT(JSRuntime *rt, void *rp) -{ - return js_RemoveRoot(rt, rp); -} - -JS_PUBLIC_API(JSBool) -JS_AddNamedRoot(JSContext *cx, void *rp, const char *name) -{ - CHECK_REQUEST(cx); - return js_AddRoot(cx, rp, name); -} - -JS_PUBLIC_API(void) -JS_ClearNewbornRoots(JSContext *cx) -{ - JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); -} - -JS_PUBLIC_API(JSBool) -JS_EnterLocalRootScope(JSContext *cx) -{ - CHECK_REQUEST(cx); - return js_EnterLocalRootScope(cx); -} - -JS_PUBLIC_API(void) -JS_LeaveLocalRootScope(JSContext *cx) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScope(cx); -} - -JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - CHECK_REQUEST(cx); - js_LeaveLocalRootScopeWithResult(cx, rval); -} - -JS_PUBLIC_API(void) -JS_ForgetLocalRoot(JSContext *cx, void *thing) -{ - CHECK_REQUEST(cx); - js_ForgetLocalRoot(cx, (jsval) thing); -} - -#ifdef DEBUG - -JS_PUBLIC_API(void) -JS_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data) -{ - js_DumpNamedRoots(rt, dump, data); -} - -#endif /* DEBUG */ - -JS_PUBLIC_API(uint32) -JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) -{ - return js_MapGCRoots(rt, map, data); -} - -JS_PUBLIC_API(JSBool) -JS_LockGCThing(JSContext *cx, void *thing) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_LockGCThing(cx, thing); - if (!ok) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_LOCK); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LockGCThingRT(JSRuntime *rt, void *thing) -{ - return js_LockGCThingRT(rt, thing); -} - -JS_PUBLIC_API(JSBool) -JS_UnlockGCThing(JSContext *cx, void *thing) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_UnlockGCThingRT(cx->runtime, thing); - if (!ok) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_UnlockGCThingRT(JSRuntime *rt, void *thing) -{ - return js_UnlockGCThingRT(rt, thing); -} - -JS_PUBLIC_API(void) -JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg) -{ - JS_ASSERT(cx->runtime->gcLevel > 0); -#ifdef JS_THREADSAFE - JS_ASSERT(cx->runtime->gcThread->id == js_CurrentThreadId()); -#endif - - GC_MARK(cx, thing, name); -} - -JS_PUBLIC_API(void) -JS_GC(JSContext *cx) -{ -#if JS_HAS_GENERATORS - /* Run previously scheduled but delayed close hooks. */ - js_RunCloseHooks(cx); -#endif - - /* Don't nuke active arenas if executing or compiling. */ - if (cx->stackPool.current == &cx->stackPool.first) - JS_FinishArenaPool(&cx->stackPool); - if (cx->tempPool.current == &cx->tempPool.first) - JS_FinishArenaPool(&cx->tempPool); - js_GC(cx, GC_NORMAL); - -#if JS_HAS_GENERATORS - /* - * Run close hooks for objects that became unreachable after the last GC. - */ - js_RunCloseHooks(cx); -#endif -} - -JS_PUBLIC_API(void) -JS_MaybeGC(JSContext *cx) -{ -#ifdef WAY_TOO_MUCH_GC - JS_GC(cx); -#else - JSRuntime *rt; - uint32 bytes, lastBytes; - - rt = cx->runtime; - bytes = rt->gcBytes; - lastBytes = rt->gcLastBytes; - - /* - * We run the GC if we used all available free GC cells and had to - * allocate extra 1/5 of GC arenas since the last run of GC, or if - * we have malloc'd more bytes through JS_malloc than we were told - * to allocate by JS_NewRuntime. - * - * The reason for - * bytes > 6/5 lastBytes - * condition is the following. Bug 312238 changed bytes and lastBytes - * to mean the total amount of memory that the GC uses now and right - * after the last GC. - * - * Before the bug the variables meant the size of allocated GC things - * now and right after the last GC. That size did not include the - * memory taken by free GC cells and the condition was - * bytes > 3/2 lastBytes. - * That is, we run the GC if we have half again as many bytes of - * GC-things as the last time we GC'd. To be compatible we need to - * express that condition through the new meaning of bytes and - * lastBytes. - * - * We write the original condition as - * B*(1-F) > 3/2 Bl*(1-Fl) - * where B is the total memory size allocated by GC and F is the free - * cell density currently and Sl and Fl are the size and the density - * right after GC. The density by definition is memory taken by free - * cells divided by total amount of memory. In other words, B and Bl - * are bytes and lastBytes with the new meaning and B*(1-F) and - * Bl*(1-Fl) are bytes and lastBytes with the original meaning. - * - * Our task is to exclude F and Fl from the last statement. According - * the stats from bug 331770 Fl is about 20-30% for GC allocations - * that contribute to S and Sl for a typical run of the browser. It - * means that the original condition implied that we did not run GC - * unless we exhausted the pool of free cells. Indeed if we still - * have free cells, then B == Bl since we did not yet allocated any - * new arenas and the condition means - * 1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F - * That implies 3/2 Fl > 1/2 or Fl > 1/3. That can not be fulfilled - * for the state described by the stats. So we can write the original - * condition as: - * F == 0 && B > 3/2 Bl(1-Fl) - * Again using the stats we see that Fl is about 20% when the browser - * starts up and when we are far from hitting rt->gcMaxBytes. With - * this F we have - * F == 0 && B > 3/2 Bl(1-0.8) or just B > 6/5 Bl. - */ - if ((bytes > 8192 && bytes > lastBytes + lastBytes / 5) || - rt->gcMallocBytes >= rt->gcMaxMallocBytes) { - JS_GC(cx); - } -#if JS_HAS_GENERATORS - else { - /* Run scheduled but not yet executed close hooks. */ - js_RunCloseHooks(cx); - } -#endif -#endif -} - -JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallback(JSContext *cx, JSGCCallback cb) -{ - return JS_SetGCCallbackRT(cx->runtime, cb); -} - -JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb) -{ - JSGCCallback oldcb; - - oldcb = rt->gcCallback; - rt->gcCallback = cb; - return oldcb; -} - -JS_PUBLIC_API(JSBool) -JS_IsAboutToBeFinalized(JSContext *cx, void *thing) -{ - JS_ASSERT(thing); - return js_IsAboutToBeFinalized(cx, thing); -} - -JS_PUBLIC_API(void) -JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value) -{ - switch (key) { - case JSGC_MAX_BYTES: - rt->gcMaxBytes = value; - break; - case JSGC_MAX_MALLOC_BYTES: - rt->gcMaxMallocBytes = value; - break; - } -} - -JS_PUBLIC_API(intN) -JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer) -{ - return js_ChangeExternalStringFinalizer(NULL, finalizer); -} - -JS_PUBLIC_API(intN) -JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer) -{ - return js_ChangeExternalStringFinalizer(finalizer, NULL); -} - -JS_PUBLIC_API(JSString *) -JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type) -{ - JSString *str; - - CHECK_REQUEST(cx); - JS_ASSERT(GCX_EXTERNAL_STRING <= type && type < (intN) GCX_NTYPES); - - str = (JSString *) js_NewGCThing(cx, (uintN) type, sizeof(JSString)); - if (!str) - return NULL; - str->length = length; - str->chars = chars; - return str; -} - -JS_PUBLIC_API(intN) -JS_GetExternalStringGCType(JSRuntime *rt, JSString *str) -{ - uint8 type = (uint8) (*js_GetGCThingFlags(str) & GCF_TYPEMASK); - - if (type >= GCX_EXTERNAL_STRING) - return (intN)type; - JS_ASSERT(type == GCX_STRING || type == GCX_MUTABLE_STRING); - return -1; -} - -JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr) -{ -#if JS_STACK_GROWTH_DIRECTION > 0 - if (limitAddr == 0) - limitAddr = (jsuword)-1; -#endif - cx->stackLimit = limitAddr; -} - -/************************************************************************/ - -JS_PUBLIC_API(void) -JS_DestroyIdArray(JSContext *cx, JSIdArray *ida) -{ - JS_free(cx, ida); -} - -JS_PUBLIC_API(JSBool) -JS_ValueToId(JSContext *cx, jsval v, jsid *idp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - if (JSVAL_IS_INT(v)) { - *idp = INT_JSVAL_TO_JSID(v); - } else { -#if JS_HAS_XML_SUPPORT - if (JSVAL_IS_OBJECT(v)) { - *idp = OBJECT_JSVAL_TO_JSID(v); - return JS_TRUE; - } -#endif - atom = js_ValueToStringAtom(cx, v); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_IdToValue(JSContext *cx, jsid id, jsval *vp) -{ - CHECK_REQUEST(cx); - *vp = ID_TO_VALUE(id); - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_EnumerateStub(JSContext *cx, JSObject *obj) -{ - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id) -{ - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - return js_TryValueOf(cx, obj, type, vp); -} - -JS_PUBLIC_API(void) -JS_FinalizeStub(JSContext *cx, JSObject *obj) -{ -} - -JS_PUBLIC_API(JSObject *) -JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, - JSClass *clasp, JSNative constructor, uintN nargs, - JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs) -{ - JSAtom *atom; - JSProtoKey key; - JSObject *proto, *ctor; - JSTempValueRooter tvr; - jsval cval, rval; - JSBool named; - JSFunction *fun; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return NULL; - - /* - * When initializing a standard class, if no parent_proto (grand-proto of - * instances of the class, parent-proto of the class's prototype object) - * is given, we must use Object.prototype if it is available. Otherwise, - * we could look up the wrong binding for a class name in obj. Example: - * - * String = Array; - * print("hi there".join); - * - * should print undefined, not Array.prototype.join. This is required by - * ECMA-262, alas. It might have been better to make String readonly and - * permanent in the global object, instead -- but that's too big a change - * to swallow at this point. - */ - key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null && - !parent_proto && - !js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), - &parent_proto)) { - return NULL; - } - - /* Create a prototype object for this class. */ - proto = js_NewObject(cx, clasp, parent_proto, obj); - if (!proto) - return NULL; - - /* After this point, control must exit via label bad or out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr); - - if (!constructor) { - /* - * Lacking a constructor, name the prototype (e.g., Math) unless this - * class (a) is anonymous, i.e. for internal use only; (b) the class - * of obj (the global object) is has a reserved slot indexed by key; - * and (c) key is not the null key. - */ - if ((clasp->flags & JSCLASS_IS_ANONYMOUS) && - (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) && - key != JSProto_Null) { - named = JS_FALSE; - } else { - named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), - OBJECT_TO_JSVAL(proto), - NULL, NULL, - (clasp->flags & JSCLASS_IS_ANONYMOUS) - ? JSPROP_READONLY | JSPROP_PERMANENT - : 0, - NULL); - if (!named) - goto bad; - } - - ctor = proto; - } else { - /* Define the constructor function in obj's scope. */ - fun = js_DefineFunction(cx, obj, atom, constructor, nargs, 0); - named = (fun != NULL); - if (!fun) - goto bad; - - /* - * Remember the class this function is a constructor for so that - * we know to create an object of this class when we call the - * constructor. - */ - fun->clasp = clasp; - - /* - * Optionally construct the prototype object, before the class has - * been fully initialized. Allow the ctor to replace proto with a - * different object, as is done for operator new -- and as at least - * XML support requires. - */ - ctor = fun->object; - if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { - cval = OBJECT_TO_JSVAL(ctor); - if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) - goto bad; - if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) - proto = JSVAL_TO_OBJECT(rval); - } - - /* Connect constructor and prototype by named properties. */ - if (!js_SetClassPrototype(cx, ctor, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { - goto bad; - } - - /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ - if (OBJ_GET_CLASS(cx, ctor) == clasp) { - JS_ASSERT(!OBJ_GET_PROTO(cx, ctor)); - OBJ_SET_PROTO(cx, ctor, proto); - } - } - - /* Add properties and methods to the prototype and the constructor. */ - if ((ps && !JS_DefineProperties(cx, proto, ps)) || - (fs && !JS_DefineFunctions(cx, proto, fs)) || - (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || - (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { - goto bad; - } - - /* If this is a standard class, cache its prototype. */ - if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) - goto bad; - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return proto; - -bad: - if (named) - (void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); - proto = NULL; - goto out; -} - -#ifdef JS_THREADSAFE -JS_PUBLIC_API(JSClass *) -JS_GetClass(JSContext *cx, JSObject *obj) -{ - return (JSClass *) - JSVAL_TO_PRIVATE(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_CLASS)); -} -#else -JS_PUBLIC_API(JSClass *) -JS_GetClass(JSObject *obj) -{ - return LOCKED_OBJ_GET_CLASS(obj); -} -#endif - -JS_PUBLIC_API(JSBool) -JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv) -{ - JSFunction *fun; - - CHECK_REQUEST(cx); - if (OBJ_GET_CLASS(cx, obj) == clasp) - return JS_TRUE; - if (argv) { - fun = js_ValueToFunction(cx, &argv[-2], 0); - if (fun) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - clasp->name, JS_GetFunctionName(fun), - OBJ_GET_CLASS(cx, obj)->name); - } - } - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - return js_HasInstance(cx, obj, v, bp); -} - -JS_PUBLIC_API(void *) -JS_GetPrivate(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); - v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_INT(v)) - return NULL; - return JSVAL_TO_PRIVATE(v); -} - -JS_PUBLIC_API(JSBool) -JS_SetPrivate(JSContext *cx, JSObject *obj, void *data) -{ - JS_ASSERT(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE); - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(data)); - return JS_TRUE; -} - -JS_PUBLIC_API(void *) -JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, - jsval *argv) -{ - if (!JS_InstanceOf(cx, obj, clasp, argv)) - return NULL; - return JS_GetPrivate(cx, obj); -} - -JS_PUBLIC_API(JSObject *) -JS_GetPrototype(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - CHECK_REQUEST(cx); - proto = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PROTO)); - - /* Beware ref to dead object (we may be called from obj's finalizer). */ - return proto && proto->map ? proto : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto) -{ - CHECK_REQUEST(cx); - if (obj->map->ops->setProto) - return obj->map->ops->setProto(cx, obj, JSSLOT_PROTO, proto); - OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)); - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_GetParent(JSContext *cx, JSObject *obj) -{ - JSObject *parent; - - parent = JSVAL_TO_OBJECT(GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PARENT)); - - /* Beware ref to dead object (we may be called from obj's finalizer). */ - return parent && parent->map ? parent : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent) -{ - CHECK_REQUEST(cx); - if (obj->map->ops->setParent) - return obj->map->ops->setParent(cx, obj, JSSLOT_PARENT, parent); - OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)); - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_GetConstructor(JSContext *cx, JSObject *proto) -{ - jsval cval; - - CHECK_REQUEST(cx); - if (!OBJ_GET_PROPERTY(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - &cval)) { - return NULL; - } - if (!VALUE_IS_FUNCTION(cx, cval)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, - OBJ_GET_CLASS(cx, proto)->name); - return NULL; - } - return JSVAL_TO_OBJECT(cval); -} - -JS_PUBLIC_API(JSBool) -JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp) -{ - JS_ASSERT(((jsid)obj & JSID_TAGMASK) == 0); - *idp = OBJECT_TO_JSID(obj); - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) -{ - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_NewObject(cx, clasp, proto, parent); -} - -JS_PUBLIC_API(JSBool) -JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) -{ - JSScope *scope; - JSIdArray *ida; - uint32 nslots; - jsval v, *vp, *end; - - if (!OBJ_IS_NATIVE(obj)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SEAL_OBJECT, - OBJ_GET_CLASS(cx, obj)->name); - return JS_FALSE; - } - - scope = OBJ_SCOPE(obj); - -#if defined JS_THREADSAFE && defined DEBUG - /* Insist on scope being used exclusively by cx's thread. */ - if (scope->ownercx != cx) { - JS_LOCK_OBJ(cx, obj); - JS_ASSERT(OBJ_SCOPE(obj) == scope); - JS_ASSERT(scope->ownercx == cx); - JS_UNLOCK_SCOPE(cx, scope); - } -#endif - - /* Nothing to do if obj's scope is already sealed. */ - if (SCOPE_IS_SEALED(scope)) - return JS_TRUE; - - /* XXX Enumerate lazy properties now, as they can't be added later. */ - ida = JS_Enumerate(cx, obj); - if (!ida) - return JS_FALSE; - JS_DestroyIdArray(cx, ida); - - /* Ensure that obj has its own, mutable scope, and seal that scope. */ - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (scope) - SCOPE_SET_SEALED(scope); - JS_UNLOCK_OBJ(cx, obj); - if (!scope) - return JS_FALSE; - - /* If we are not sealing an entire object graph, we're done. */ - if (!deep) - return JS_TRUE; - - /* Walk obj->slots and if any value is a non-null object, seal it. */ - nslots = JS_MIN(scope->map.freeslot, scope->map.nslots); - for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { - v = *vp; - if (JSVAL_IS_PRIMITIVE(v)) - continue; - if (!JS_SealObject(cx, JSVAL_TO_OBJECT(v), deep)) - return JS_FALSE; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSObject *) -JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent) -{ - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_ConstructObject(cx, clasp, proto, parent, 0, NULL); -} - -JS_PUBLIC_API(JSObject *) -JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv) -{ - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - return js_ConstructObject(cx, clasp, proto, parent, argc, argv); -} - -static JSBool -DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN tinyid) -{ - jsid id; - JSAtom *atom; - - if (attrs & JSPROP_INDEX) { - id = INT_TO_JSID(JS_PTR_TO_INT32(name)); - atom = NULL; - attrs &= ~JSPROP_INDEX; - } else { - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - id = ATOM_TO_JSID(atom); - } - if (flags != 0 && OBJ_IS_NATIVE(obj)) { - return js_DefineNativeProperty(cx, obj, id, value, getter, setter, - attrs, flags, tinyid, NULL); - } - return OBJ_DEFINE_PROPERTY(cx, obj, id, value, getter, setter, attrs, - NULL); -} - -#define AUTO_NAMELEN(s,n) (((n) == (size_t)-1) ? js_strlen(s) : (n)) - -static JSBool -DefineUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN tinyid) -{ - JSAtom *atom; - - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - if (flags != 0 && OBJ_IS_NATIVE(obj)) { - return js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, flags, tinyid, - NULL); - } - return OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, NULL); -} - -JS_PUBLIC_API(JSObject *) -JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, - JSObject *proto, uintN attrs) -{ - JSObject *nobj; - - CHECK_REQUEST(cx); - if (!clasp) - clasp = &js_ObjectClass; /* default class is Object */ - nobj = js_NewObject(cx, clasp, proto, obj); - if (!nobj) - return NULL; - if (!DefineProperty(cx, obj, name, OBJECT_TO_JSVAL(nobj), NULL, NULL, attrs, - 0, 0)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - return nobj; -} - -JS_PUBLIC_API(JSBool) -JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds) -{ - JSBool ok; - jsval value; - uintN flags; - - CHECK_REQUEST(cx); - for (ok = JS_TRUE; cds->name; cds++) { - ok = js_NewNumberValue(cx, cds->dval, &value); - if (!ok) - break; - flags = cds->flags; - if (!flags) - flags = JSPROP_READONLY | JSPROP_PERMANENT; - ok = DefineProperty(cx, obj, cds->name, value, NULL, NULL, flags, 0, 0); - if (!ok) - break; - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps) -{ - JSBool ok; - - CHECK_REQUEST(cx); - for (ok = JS_TRUE; ps->name; ps++) { - ok = DefineProperty(cx, obj, ps->name, JSVAL_VOID, - ps->getter, ps->setter, ps->flags, - SPROP_HAS_SHORTID, ps->tinyid); - if (!ok) - break; - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineProperty(cx, obj, name, value, getter, setter, attrs, 0, 0); -} - -JS_PUBLIC_API(JSBool) -JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineProperty(cx, obj, name, value, getter, setter, attrs, - SPROP_HAS_SHORTID, tinyid); -} - -static JSBool -LookupProperty(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - JSProperty **propp) -{ - JSAtom *atom; - - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); -} - -static JSBool -LookupUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - JSObject **objp, JSProperty **propp) -{ - JSAtom *atom; - - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), objp, propp); -} - -JS_PUBLIC_API(JSBool) -JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, - const char *alias) -{ - JSObject *obj2; - JSProperty *prop; - JSAtom *atom; - JSBool ok; - JSScopeProperty *sprop; - - CHECK_REQUEST(cx); - if (!LookupProperty(cx, obj, name, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - js_ReportIsNotDefined(cx, name); - return JS_FALSE; - } - if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, - alias, name, OBJ_GET_CLASS(cx, obj2)->name); - return JS_FALSE; - } - atom = js_Atomize(cx, alias, strlen(alias), 0); - if (!atom) { - ok = JS_FALSE; - } else { - sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, ATOM_TO_JSID(atom), - sprop->getter, sprop->setter, sprop->slot, - sprop->attrs, sprop->flags | SPROP_IS_ALIAS, - sprop->shortid) - != NULL); - } - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -static jsval -LookupResult(JSContext *cx, JSObject *obj, JSObject *obj2, JSProperty *prop) -{ - JSScopeProperty *sprop; - jsval rval; - - if (!prop) { - /* XXX bad API: no way to tell "not defined" from "void value" */ - return JSVAL_VOID; - } - if (OBJ_IS_NATIVE(obj2)) { - /* Peek at the native property's slot value, without doing a Get. */ - sprop = (JSScopeProperty *)prop; - rval = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)) - ? LOCKED_OBJ_GET_SLOT(obj2, sprop->slot) - : JSVAL_TRUE; - } else { - /* XXX bad API: no way to return "defined but value unknown" */ - rval = JSVAL_TRUE; - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - return rval; -} - -static JSBool -GetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, JSPropertyOp *setterp) -{ - JSObject *obj2; - JSProperty *prop; - JSBool ok; - - if (!atom) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - - if (!prop || obj != obj2) { - *attrsp = 0; - *foundp = JS_FALSE; - if (getterp) - *getterp = NULL; - if (setterp) - *setterp = NULL; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; - } - - *foundp = JS_TRUE; - ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, attrsp); - if (ok && OBJ_IS_NATIVE(obj)) { - JSScopeProperty *sprop = (JSScopeProperty *) prop; - - if (getterp) - *getterp = sprop->getter; - if (setterp) - *setterp = sprop->setter; - } - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -static JSBool -SetPropertyAttributes(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN attrs, JSBool *foundp) -{ - JSObject *obj2; - JSProperty *prop; - JSBool ok; - - if (!atom) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - if (!prop || obj != obj2) { - *foundp = JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; - } - - *foundp = JS_TRUE; - ok = OBJ_SET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, &attrs); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN *attrsp, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_Atomize(cx, name, strlen(name), 0), - attrsp, foundp, NULL, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const char *name, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_Atomize(cx, name, strlen(name), 0), - attrsp, foundp, getterp, setterp); -} - -JS_PUBLIC_API(JSBool) -JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN attrs, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return SetPropertyAttributes(cx, obj, - js_Atomize(cx, name, strlen(name), 0), - attrs, foundp); -} - -JS_PUBLIC_API(JSBool) -JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupProperty(cx, obj, name, &obj2, &prop); - if (ok) { - *foundp = (prop != NULL); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupProperty(cx, obj, name, &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, - uintN flags, jsval *vp) -{ - JSAtom *atom; - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - ok = OBJ_IS_NATIVE(obj) - ? js_LookupPropertyWithFlags(cx, obj, ATOM_TO_JSID(atom), flags, - &obj2, &prop) - : OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - jsval *vp) -{ - CHECK_REQUEST(cx); - -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - obj = ops->getMethod(cx, obj, id, vp); - if (!obj) - return JS_FALSE; - } else -#endif - { - if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) - return JS_FALSE; - } - - *objp = obj; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - jsval *vp) -{ - JSAtom *atom; - - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), objp, vp); -} - -JS_PUBLIC_API(JSBool) -JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name) -{ - jsval junk; - - CHECK_REQUEST(cx); - return JS_DeleteProperty2(cx, obj, name, &junk); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, - jsval *rval) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); -} - -JS_PUBLIC_API(JSBool) -JS_DefineUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, - attrs, 0, 0); -} - -JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrsp, foundp, NULL, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp) -{ - CHECK_REQUEST(cx); - return GetPropertyAttributes(cx, obj, - js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrsp, foundp, getterp, setterp); -} - -JS_PUBLIC_API(JSBool) -JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN attrs, JSBool *foundp) -{ - CHECK_REQUEST(cx); - return SetPropertyAttributes(cx, obj, - js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0), - attrs, foundp); -} - -JS_PUBLIC_API(JSBool) -JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs) -{ - CHECK_REQUEST(cx); - return DefineUCProperty(cx, obj, name, namelen, value, getter, setter, - attrs, SPROP_HAS_SHORTID, tinyid); -} - -JS_PUBLIC_API(JSBool) -JS_HasUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - JSBool *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); - if (ok) { - *vp = (prop != NULL); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = LookupUCProperty(cx, obj, name, namelen, &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_SetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_SET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *rval) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return JS_FALSE; - return OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval); -} - -JS_PUBLIC_API(JSObject *) -JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector) -{ - CHECK_REQUEST(cx); - /* NB: jsuint cast does ToUint32. */ - return js_NewArrayObject(cx, (jsuint)length, vector); -} - -JS_PUBLIC_API(JSBool) -JS_IsArrayObject(JSContext *cx, JSObject *obj) -{ - return OBJ_GET_CLASS(cx, obj) == &js_ArrayClass; -} - -JS_PUBLIC_API(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - CHECK_REQUEST(cx); - return js_GetLengthProperty(cx, obj, lengthp); -} - -JS_PUBLIC_API(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) -{ - CHECK_REQUEST(cx); - return js_SetLengthProperty(cx, obj, length); -} - -JS_PUBLIC_API(JSBool) -JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - CHECK_REQUEST(cx); - return js_HasLengthProperty(cx, obj, lengthp); -} - -JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs) -{ - CHECK_REQUEST(cx); - return OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(index), value, - getter, setter, attrs, NULL); -} - -JS_PUBLIC_API(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias) -{ - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - JSBool ok; - - CHECK_REQUEST(cx); - if (!LookupProperty(cx, obj, name, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - js_ReportIsNotDefined(cx, name); - return JS_FALSE; - } - if (obj2 != obj || !OBJ_IS_NATIVE(obj)) { - char numBuf[12]; - OBJ_DROP_PROPERTY(cx, obj2, prop); - JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)alias); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_ALIAS, - numBuf, name, OBJ_GET_CLASS(cx, obj2)->name); - return JS_FALSE; - } - sprop = (JSScopeProperty *)prop; - ok = (js_AddNativeProperty(cx, obj, INT_TO_JSID(alias), - sprop->getter, sprop->setter, sprop->slot, - sprop->attrs, sprop->flags | SPROP_IS_ALIAS, - sprop->shortid) - != NULL); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); - if (ok) { - *foundp = (prop != NULL); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) -{ - JSBool ok; - JSObject *obj2; - JSProperty *prop; - - CHECK_REQUEST(cx); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, INT_TO_JSID(index), &obj2, &prop); - if (ok) - *vp = LookupResult(cx, obj, obj2, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) -{ - CHECK_REQUEST(cx); - return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); -} - -JS_PUBLIC_API(JSBool) -JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp) -{ - CHECK_REQUEST(cx); - return OBJ_SET_PROPERTY(cx, obj, INT_TO_JSID(index), vp); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index) -{ - jsval junk; - - CHECK_REQUEST(cx); - return JS_DeleteElement2(cx, obj, index, &junk); -} - -JS_PUBLIC_API(JSBool) -JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval) -{ - CHECK_REQUEST(cx); - return OBJ_DELETE_PROPERTY(cx, obj, INT_TO_JSID(index), rval); -} - -JS_PUBLIC_API(void) -JS_ClearScope(JSContext *cx, JSObject *obj) -{ - CHECK_REQUEST(cx); - - if (obj->map->ops->clear) - obj->map->ops->clear(cx, obj); - - /* Clear cached class objects on the global object. */ - if (JS_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) { - JSProtoKey key; - - for (key = JSProto_Null; key < JSProto_LIMIT; key++) - JS_SetReservedSlot(cx, obj, key, JSVAL_VOID); - } -} - -JS_PUBLIC_API(JSIdArray *) -JS_Enumerate(JSContext *cx, JSObject *obj) -{ - jsint i, n; - jsval iter_state, num_properties; - jsid id; - JSIdArray *ida; - jsval *vector; - - CHECK_REQUEST(cx); - - ida = NULL; - iter_state = JSVAL_NULL; - - /* Get the number of properties to enumerate. */ - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties)) - goto error; - if (!JSVAL_IS_INT(num_properties)) { - JS_ASSERT(0); - goto error; - } - - /* Grow as needed if we don't know the exact amount ahead of time. */ - n = JSVAL_TO_INT(num_properties); - if (n <= 0) - n = 8; - - /* Create an array of jsids large enough to hold all the properties */ - ida = js_NewIdArray(cx, n); - if (!ida) - goto error; - - i = 0; - vector = &ida->vector[0]; - for (;;) { - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &id)) - goto error; - - /* No more jsid's to enumerate ? */ - if (iter_state == JSVAL_NULL) - break; - - if (i == ida->length) { - ida = js_SetIdArrayLength(cx, ida, ida->length * 2); - if (!ida) - goto error; - vector = &ida->vector[0]; - } - vector[i++] = id; - } - return js_SetIdArrayLength(cx, ida, i); - -error: - if (iter_state != JSVAL_NULL) - OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); - if (ida) - JS_DestroyIdArray(cx, ida); - return NULL; -} - -/* - * XXX reverse iterator for properties, unreverse and meld with jsinterp.c's - * prop_iterator_class somehow... - * + preserve the OBJ_ENUMERATE API while optimizing the native object case - * + native case here uses a JSScopeProperty *, but that iterates in reverse! - * + so we make non-native match, by reverse-iterating after JS_Enumerating - */ -#define JSSLOT_ITER_INDEX (JSSLOT_PRIVATE + 1) - -#if JSSLOT_ITER_INDEX >= JS_INITIAL_NSLOTS -# error "JSSLOT_ITER_INDEX botch!" -#endif - -static void -prop_iter_finalize(JSContext *cx, JSObject *obj) -{ - jsval v; - jsint i; - JSIdArray *ida; - - v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX); - if (JSVAL_IS_VOID(v)) - return; - - i = JSVAL_TO_INT(v); - if (i >= 0) { - /* Non-native case: destroy the ida enumerated when obj was created. */ - ida = (JSIdArray *) JS_GetPrivate(cx, obj); - if (ida) - JS_DestroyIdArray(cx, ida); - } -} - -static uint32 -prop_iter_mark(JSContext *cx, JSObject *obj, void *arg) -{ - jsval v; - jsint i, n; - JSScopeProperty *sprop; - JSIdArray *ida; - jsid id; - - v = GC_AWARE_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(!JSVAL_IS_VOID(v)); - - i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_INDEX)); - if (i < 0) { - /* Native case: just mark the next property to visit. */ - sprop = (JSScopeProperty *) JSVAL_TO_PRIVATE(v); - if (sprop) - MARK_SCOPE_PROPERTY(cx, sprop); - } else { - /* Non-native case: mark each id in the JSIdArray private. */ - ida = (JSIdArray *) JSVAL_TO_PRIVATE(v); - for (i = 0, n = ida->length; i < n; i++) { - id = ida->vector[i]; - MARK_ID(cx, id); - } - } - return 0; -} - -static JSClass prop_iter_class = { - "PropertyIterator", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iter_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, prop_iter_mark, NULL -}; - -JS_PUBLIC_API(JSObject *) -JS_NewPropertyIterator(JSContext *cx, JSObject *obj) -{ - JSObject *iterobj; - JSScope *scope; - void *pdata; - jsint index; - JSIdArray *ida; - - CHECK_REQUEST(cx); - iterobj = js_NewObject(cx, &prop_iter_class, NULL, obj); - if (!iterobj) - return NULL; - - if (OBJ_IS_NATIVE(obj)) { - /* Native case: start with the last property in obj's own scope. */ - scope = OBJ_SCOPE(obj); - pdata = (scope->object == obj) ? scope->lastProp : NULL; - index = -1; - } else { - JSTempValueRooter tvr; - - /* - * Non-native case: enumerate a JSIdArray and keep it via private. - * - * Note: we have to make sure that we root obj around the call to - * JS_Enumerate to protect against multiple allocations under it. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(iterobj), &tvr); - ida = JS_Enumerate(cx, obj); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ida) - goto bad; - pdata = ida; - index = ida->length; - } - - /* iterobj can not escape to other threads here. */ - iterobj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(pdata); - iterobj->slots[JSSLOT_ITER_INDEX] = INT_TO_JSVAL(index); - return iterobj; - - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -JS_PUBLIC_API(JSBool) -JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp) -{ - jsint i; - JSObject *obj; - JSScope *scope; - JSScopeProperty *sprop; - JSIdArray *ida; - - CHECK_REQUEST(cx); - i = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX)); - if (i < 0) { - /* Native case: private data is a property tree node pointer. */ - obj = OBJ_GET_PARENT(cx, iterobj); - JS_ASSERT(OBJ_IS_NATIVE(obj)); - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->object == obj); - sprop = (JSScopeProperty *) JS_GetPrivate(cx, iterobj); - - /* - * If the next property mapped by scope in the property tree ancestor - * line is not enumerable, or it's an alias, or one or more properties - * were deleted from the "middle" of the scope-mapped ancestor line - * and the next property was among those deleted, skip it and keep on - * trying to find an enumerable property that is still in scope. - */ - while (sprop && - (!(sprop->attrs & JSPROP_ENUMERATE) || - (sprop->flags & SPROP_IS_ALIAS) || - (SCOPE_HAD_MIDDLE_DELETE(scope) && - !SCOPE_HAS_PROPERTY(scope, sprop)))) { - sprop = sprop->parent; - } - - if (!sprop) { - *idp = JSVAL_VOID; - } else { - if (!JS_SetPrivate(cx, iterobj, sprop->parent)) - return JS_FALSE; - *idp = sprop->id; - } - } else { - /* Non-native case: use the ida enumerated when iterobj was created. */ - ida = (JSIdArray *) JS_GetPrivate(cx, iterobj); - JS_ASSERT(i <= ida->length); - if (i == 0) { - *idp = JSVAL_VOID; - } else { - *idp = ida->vector[--i]; - OBJ_SET_SLOT(cx, iterobj, JSSLOT_ITER_INDEX, INT_TO_JSVAL(i)); - } - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp) -{ - CHECK_REQUEST(cx); - return OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, attrsp); -} - -JS_PUBLIC_API(JSCheckAccessOp) -JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb) -{ - JSCheckAccessOp oldacb; - - oldacb = rt->checkObjectAccess; - rt->checkObjectAccess = acb; - return oldacb; -} - -static JSBool -ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp, - uint32 index, uint32 limit) -{ - /* Check the computed, possibly per-instance, upper bound. */ - if (clasp->reserveSlots) - JS_LOCK_OBJ_VOID(cx, obj, limit += clasp->reserveSlots(cx, obj)); - if (index >= limit) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_RESERVED_SLOT_RANGE); - return JS_FALSE; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp) -{ - JSClass *clasp; - uint32 limit, slot; - - CHECK_REQUEST(cx); - clasp = OBJ_GET_CLASS(cx, obj); - limit = JSCLASS_RESERVED_SLOTS(clasp); - if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) - return JS_FALSE; - slot = JSSLOT_START(clasp) + index; - *vp = OBJ_GET_REQUIRED_SLOT(cx, obj, slot); - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v) -{ - JSClass *clasp; - uint32 limit, slot; - - CHECK_REQUEST(cx); - clasp = OBJ_GET_CLASS(cx, obj); - limit = JSCLASS_RESERVED_SLOTS(clasp); - if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit)) - return JS_FALSE; - slot = JSSLOT_START(clasp) + index; - return OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); -} - -#ifdef JS_THREADSAFE -JS_PUBLIC_API(jsrefcount) -JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals) -{ - return JS_ATOMIC_INCREMENT(&principals->refcount); -} - -JS_PUBLIC_API(jsrefcount) -JS_DropPrincipals(JSContext *cx, JSPrincipals *principals) -{ - jsrefcount rc = JS_ATOMIC_DECREMENT(&principals->refcount); - if (rc == 0) - principals->destroy(cx, principals); - return rc; -} -#endif - -JS_PUBLIC_API(JSPrincipalsTranscoder) -JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px) -{ - JSPrincipalsTranscoder oldpx; - - oldpx = rt->principalsTranscoder; - rt->principalsTranscoder = px; - return oldpx; -} - -JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop) -{ - JSObjectPrincipalsFinder oldfop; - - oldfop = rt->findObjectPrincipals; - rt->findObjectPrincipals = fop; - return oldfop; -} - -JS_PUBLIC_API(JSFunction *) -JS_NewFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags, - JSObject *parent, const char *name) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - - if (!name) { - atom = NULL; - } else { - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return NULL; - } - return js_NewFunction(cx, NULL, native, nargs, flags, parent, atom); -} - -JS_PUBLIC_API(JSObject *) -JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) -{ - CHECK_REQUEST(cx); - if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) { - /* Indicate we cannot clone this object. */ - return funobj; - } - return js_CloneFunctionObject(cx, funobj, parent); -} - -JS_PUBLIC_API(JSObject *) -JS_GetFunctionObject(JSFunction *fun) -{ - return fun->object; -} - -JS_PUBLIC_API(const char *) -JS_GetFunctionName(JSFunction *fun) -{ - return fun->atom - ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) - : js_anonymous_str; -} - -JS_PUBLIC_API(JSString *) -JS_GetFunctionId(JSFunction *fun) -{ - return fun->atom ? ATOM_TO_STRING(fun->atom) : NULL; -} - -JS_PUBLIC_API(uintN) -JS_GetFunctionFlags(JSFunction *fun) -{ -#ifdef MOZILLA_1_8_BRANCH - uintN flags = fun->flags; - - return JSFUN_DISJOINT_FLAGS(flags) | - (JSFUN_GETTER_TEST(flags) ? JSFUN_GETTER : 0) | - (JSFUN_SETTER_TEST(flags) ? JSFUN_SETTER : 0) | - (JSFUN_BOUND_METHOD_TEST(flags) ? JSFUN_BOUND_METHOD : 0) | - (JSFUN_HEAVYWEIGHT_TEST(flags) ? JSFUN_HEAVYWEIGHT : 0); -#else - return fun->flags; -#endif -} - -JS_PUBLIC_API(uint16) -JS_GetFunctionArity(JSFunction *fun) -{ - return fun->nargs; -} - -JS_PUBLIC_API(JSBool) -JS_ObjectIsFunction(JSContext *cx, JSObject *obj) -{ - return OBJ_GET_CLASS(cx, obj) == &js_FunctionClass; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -js_generic_native_method_dispatcher(JSContext *cx, JSObject *obj, - uintN argc, jsval *argv, jsval *rval) -{ - jsval fsv; - JSFunctionSpec *fs; - JSObject *tmp; - - if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(argv[-2]), 0, &fsv)) - return JS_FALSE; - fs = (JSFunctionSpec *) JSVAL_TO_PRIVATE(fsv); - - /* - * We know that argv[0] is valid because JS_DefineFunctions, which is our - * only (indirect) referrer, defined us as requiring at least one argument - * (notice how it passes fs->nargs + 1 as the next-to-last argument to - * JS_DefineFunction). - */ - if (JSVAL_IS_PRIMITIVE(argv[0])) { - /* - * Make sure that this is an object or null, as required by the generic - * functions. - */ - if (!js_ValueToObject(cx, argv[0], &tmp)) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(tmp); - } - - /* - * Copy all actual (argc) and required but missing (fs->nargs + 1 - argc) - * args down over our |this| parameter, argv[-1], which is almost always - * the class constructor object, e.g. Array. Then call the corresponding - * prototype native method with our first argument passed as |this|. - */ - memmove(argv - 1, argv, JS_MAX(fs->nargs + 1U, argc) * sizeof(jsval)); - - /* - * Follow Function.prototype.apply and .call by using the global object as - * the 'this' param if no args. - */ - JS_ASSERT(cx->fp->argv == argv); - tmp = js_ComputeThis(cx, JSVAL_TO_OBJECT(argv[-1]), argv); - if (!tmp) - return JS_FALSE; - cx->fp->thisp = tmp; - - /* - * Protect against argc - 1 underflowing below. By calling js_ComputeThis, - * we made it as if the static was called with one parameter. - */ - if (argc == 0) - argc = 1; - - return fs->call(cx, JSVAL_TO_OBJECT(argv[-1]), argc - 1, argv, rval); -} - -JS_PUBLIC_API(JSBool) -JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs) -{ - uintN flags; - JSObject *ctor; - JSFunction *fun; - - CHECK_REQUEST(cx); - ctor = NULL; - for (; fs->name; fs++) { - - /* High bits of fs->extra are reserved. */ - JS_ASSERT((fs->extra & 0xFFFF0000) == 0); - flags = fs->flags; - - /* - * Define a generic arity N+1 static method for the arity N prototype - * method if flags contains JSFUN_GENERIC_NATIVE. - */ - if (flags & JSFUN_GENERIC_NATIVE) { - if (!ctor) { - ctor = JS_GetConstructor(cx, obj); - if (!ctor) - return JS_FALSE; - } - - flags &= ~JSFUN_GENERIC_NATIVE; - fun = JS_DefineFunction(cx, ctor, fs->name, - js_generic_native_method_dispatcher, - fs->nargs + 1, flags); - if (!fun) - return JS_FALSE; - fun->u.n.extra = (uint16)fs->extra; - - /* - * As jsapi.h notes, fs must point to storage that lives as long - * as fun->object lives. - */ - if (!JS_SetReservedSlot(cx, fun->object, 0, PRIVATE_TO_JSVAL(fs))) - return JS_FALSE; - } - - fun = JS_DefineFunction(cx, obj, fs->name, fs->call, fs->nargs, flags); - if (!fun) - return JS_FALSE; - fun->u.n.extra = (uint16)fs->extra; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSFunction *) -JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, - uintN nargs, uintN attrs) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return NULL; - return js_DefineFunction(cx, obj, atom, call, nargs, attrs); -} - -JS_PUBLIC_API(JSFunction *) -JS_DefineUCFunction(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, JSNative call, - uintN nargs, uintN attrs) -{ - JSAtom *atom; - - atom = js_AtomizeChars(cx, name, AUTO_NAMELEN(name, namelen), 0); - if (!atom) - return NULL; - return js_DefineFunction(cx, obj, atom, call, nargs, attrs); -} - -static JSScript * -CompileTokenStream(JSContext *cx, JSObject *obj, JSTokenStream *ts, - void *tempMark, JSBool *eofp) -{ - JSBool eof; - JSArenaPool codePool, notePool; - JSCodeGenerator cg; - JSScript *script; - - CHECK_REQUEST(cx); - eof = JS_FALSE; - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); - if (!js_InitCodeGenerator(cx, &cg, &codePool, ¬ePool, - ts->filename, ts->lineno, - ts->principals)) { - script = NULL; - } else if (!js_CompileTokenStream(cx, obj, ts, &cg)) { - script = NULL; - eof = (ts->flags & TSF_EOF) != 0; - } else { - script = js_NewScriptFromCG(cx, &cg, NULL); - } - if (eofp) - *eofp = eof; - if (!js_CloseTokenStream(cx, ts)) { - if (script) - js_DestroyScript(cx, script); - script = NULL; - } - cg.tempMark = tempMark; - js_FinishCodeGenerator(cx, &cg); - JS_FinishArenaPool(&codePool); - JS_FinishArenaPool(¬ePool); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileScript(JSContext *cx, JSObject *obj, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSScript *script; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - script = JS_CompileUCScript(cx, obj, chars, length, filename, lineno); - JS_free(cx, chars); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSScript *script; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - script = JS_CompileUCScriptForPrincipals(cx, obj, principals, - chars, length, filename, lineno); - JS_free(cx, chars); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - CHECK_REQUEST(cx); - return JS_CompileUCScriptForPrincipals(cx, obj, NULL, chars, length, - filename, lineno); -} - -#define LAST_FRAME_EXCEPTION_CHECK(cx,result) \ - JS_BEGIN_MACRO \ - if (!(result) && !((cx)->options & JSOPTION_DONT_REPORT_UNCAUGHT)) \ - js_ReportUncaughtException(cx); \ - JS_END_MACRO - -#define LAST_FRAME_CHECKS(cx,result) \ - JS_BEGIN_MACRO \ - if (!(cx)->fp) { \ - (cx)->weakRoots.lastInternalResult = JSVAL_NULL; \ - LAST_FRAME_EXCEPTION_CHECK(cx, result); \ - } \ - JS_END_MACRO - -JS_PUBLIC_API(JSScript *) -JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - void *mark; - JSTokenStream *ts; - JSScript *script; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); - if (!ts) - return NULL; - script = CompileTokenStream(cx, obj, ts, mark, NULL); - LAST_FRAME_CHECKS(cx, script); - return script; -} - -JS_PUBLIC_API(JSBool) -JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, - const char *bytes, size_t length) -{ - jschar *chars; - JSBool result; - JSExceptionState *exnState; - void *tempMark; - JSTokenStream *ts; - JSErrorReporter older; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return JS_TRUE; - - /* - * Return true on any out-of-memory error, so our caller doesn't try to - * collect more buffered source. - */ - result = JS_TRUE; - exnState = JS_SaveExceptionState(cx); - tempMark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, chars, length, NULL, 0, NULL); - if (ts) { - older = JS_SetErrorReporter(cx, NULL); - if (!js_ParseTokenStream(cx, obj, ts) && - (ts->flags & TSF_UNEXPECTED_EOF)) { - /* - * We ran into an error. If it was because we ran out of source, - * we return false, so our caller will know to try to collect more - * buffered source. - */ - result = JS_FALSE; - } - - JS_SetErrorReporter(cx, older); - js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, tempMark); - } - - JS_free(cx, chars); - JS_RestoreExceptionState(cx, exnState); - return result; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) -{ - void *mark; - JSTokenStream *ts; - JSScript *script; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewFileTokenStream(cx, filename, stdin); - if (!ts) - return NULL; - script = CompileTokenStream(cx, obj, ts, mark, NULL); - LAST_FRAME_CHECKS(cx, script); - return script; -} - -JS_PUBLIC_API(JSScript *) -JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, - FILE *file) -{ - return JS_CompileFileHandleForPrincipals(cx, obj, filename, file, NULL); -} - -JS_PUBLIC_API(JSScript *) -JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, - const char *filename, FILE *file, - JSPrincipals *principals) -{ - void *mark; - JSTokenStream *ts; - JSScript *script; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewFileTokenStream(cx, NULL, file); - if (!ts) - return NULL; - ts->filename = filename; - /* XXXshaver js_NewFileTokenStream should do this, because it drops */ - if (principals) { - ts->principals = principals; - JSPRINCIPALS_HOLD(cx, ts->principals); - } - script = CompileTokenStream(cx, obj, ts, mark, NULL); - LAST_FRAME_CHECKS(cx, script); - return script; -} - -JS_PUBLIC_API(JSObject *) -JS_NewScriptObject(JSContext *cx, JSScript *script) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return NULL; - - if (script) { - if (!JS_SetPrivate(cx, obj, script)) - return NULL; - script->object = obj; - } - return obj; -} - -JS_PUBLIC_API(JSObject *) -JS_GetScriptObject(JSScript *script) -{ - return script->object; -} - -JS_PUBLIC_API(void) -JS_DestroyScript(JSContext *cx, JSScript *script) -{ - CHECK_REQUEST(cx); - js_DestroyScript(cx, script); -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSFunction *fun; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - fun = JS_CompileUCFunction(cx, obj, name, nargs, argnames, chars, length, - filename, lineno); - JS_free(cx, chars); - return fun; -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno) -{ - jschar *chars; - JSFunction *fun; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - fun = JS_CompileUCFunctionForPrincipals(cx, obj, principals, name, - nargs, argnames, chars, length, - filename, lineno); - JS_free(cx, chars); - return fun; -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - CHECK_REQUEST(cx); - return JS_CompileUCFunctionForPrincipals(cx, obj, NULL, name, - nargs, argnames, - chars, length, - filename, lineno); -} - -JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno) -{ - void *mark; - JSTokenStream *ts; - JSFunction *fun; - JSAtom *funAtom, *argAtom; - uintN i; - - CHECK_REQUEST(cx); - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, chars, length, filename, lineno, principals); - if (!ts) { - fun = NULL; - goto out; - } - if (!name) { - funAtom = NULL; - } else { - funAtom = js_Atomize(cx, name, strlen(name), 0); - if (!funAtom) { - fun = NULL; - goto out; - } - } - fun = js_NewFunction(cx, NULL, NULL, nargs, 0, obj, funAtom); - if (!fun) - goto out; - if (nargs) { - for (i = 0; i < nargs; i++) { - argAtom = js_Atomize(cx, argnames[i], strlen(argnames[i]), 0); - if (!argAtom) - break; - if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(argAtom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - SPROP_HAS_SHORTID, i)) { - break; - } - } - if (i < nargs) { - fun = NULL; - goto out; - } - } - if (!js_CompileFunctionBody(cx, ts, fun)) { - fun = NULL; - goto out; - } - if (obj && funAtom) { - if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(funAtom), - OBJECT_TO_JSVAL(fun->object), - NULL, NULL, JSPROP_ENUMERATE, NULL)) { - return NULL; - } - } -out: - if (ts) - js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, mark); - LAST_FRAME_CHECKS(cx, fun); - return fun; -} - -JS_PUBLIC_API(JSString *) -JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, - uintN indent) -{ - JSPrinter *jp; - JSString *str; - - CHECK_REQUEST(cx); - jp = js_NewPrinter(cx, name, - indent & ~JS_DONT_PRETTY_PRINT, - !(indent & JS_DONT_PRETTY_PRINT)); - if (!jp) - return NULL; - if (js_DecompileScript(jp, script)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent) -{ - JSPrinter *jp; - JSString *str; - - CHECK_REQUEST(cx); - jp = js_NewPrinter(cx, JS_GetFunctionName(fun), - indent & ~JS_DONT_PRETTY_PRINT, - !(indent & JS_DONT_PRETTY_PRINT)); - if (!jp) - return NULL; - if (js_DecompileFunction(jp, fun)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent) -{ - JSPrinter *jp; - JSString *str; - - CHECK_REQUEST(cx); - jp = js_NewPrinter(cx, JS_GetFunctionName(fun), - indent & ~JS_DONT_PRETTY_PRINT, - !(indent & JS_DONT_PRETTY_PRINT)); - if (!jp) - return NULL; - if (js_DecompileFunctionBody(jp, fun)) - str = js_GetPrinterOutput(jp); - else - str = NULL; - js_DestroyPrinter(jp); - return str; -} - -JS_PUBLIC_API(JSBool) -JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_Execute(cx, obj, script, NULL, 0, rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, - JSExecPart part, jsval *rval) -{ - JSScript tmp; - JSRuntime *rt; - JSBool ok; - - /* Make a temporary copy of the JSScript structure and farble it a bit. */ - tmp = *script; - if (part == JSEXEC_PROLOG) { - tmp.length = PTRDIFF(tmp.main, tmp.code, jsbytecode); - } else { - tmp.length -= PTRDIFF(tmp.main, tmp.code, jsbytecode); - tmp.code = tmp.main; - } - - /* Tell the debugger about our temporary copy of the script structure. */ - rt = cx->runtime; - if (rt->newScriptHook) { - rt->newScriptHook(cx, tmp.filename, tmp.lineno, &tmp, NULL, - rt->newScriptHookData); - } - - /* Execute the farbled struct and tell the debugger to forget about it. */ - ok = JS_ExecuteScript(cx, obj, &tmp, rval); - if (rt->destroyScriptHook) - rt->destroyScriptHook(cx, &tmp, rt->destroyScriptHookData); - return ok; -} - -/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ -JS_PUBLIC_API(JSBool) -JS_EvaluateScript(JSContext *cx, JSObject *obj, - const char *bytes, uintN nbytes, - const char *filename, uintN lineno, - jsval *rval) -{ - size_t length = nbytes; - jschar *chars; - JSBool ok; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return JS_FALSE; - ok = JS_EvaluateUCScript(cx, obj, chars, length, filename, lineno, rval); - JS_free(cx, chars); - return ok; -} - -/* Ancient uintN nbytes is part of API/ABI, so use size_t length local. */ -JS_PUBLIC_API(JSBool) -JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, uintN nbytes, - const char *filename, uintN lineno, - jsval *rval) -{ - size_t length = nbytes; - jschar *chars; - JSBool ok; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return JS_FALSE; - ok = JS_EvaluateUCScriptForPrincipals(cx, obj, principals, chars, length, - filename, lineno, rval); - JS_free(cx, chars); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_EvaluateUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - CHECK_REQUEST(cx); - return JS_EvaluateUCScriptForPrincipals(cx, obj, NULL, chars, length, - filename, lineno, rval); -} - -JS_PUBLIC_API(JSBool) -JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - uint32 options; - JSScript *script; - JSBool ok; - - CHECK_REQUEST(cx); - options = cx->options; - cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, - filename, lineno); - cx->options = options; - if (!script) - return JS_FALSE; - ok = js_Execute(cx, obj, script, NULL, 0, rval); - LAST_FRAME_CHECKS(cx, ok); - JS_DestroyScript(cx, script); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, - jsval *argv, jsval *rval) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(fun->object), argc, argv, - rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, - jsval *argv, jsval *rval) -{ - JSBool ok; - jsval fval; - - CHECK_REQUEST(cx); -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - JSAtom *atom; - - ops = (JSXMLObjectOps *) obj->map->ops; - atom = js_Atomize(cx, name, strlen(name), 0); - if (!atom) - return JS_FALSE; - obj = ops->getMethod(cx, obj, ATOM_TO_JSID(atom), &fval); - if (!obj) - return JS_FALSE; - } else -#endif - if (!JS_GetProperty(cx, obj, name, &fval)) - return JS_FALSE; - ok = js_InternalCall(cx, obj, fval, argc, argv, rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, - jsval *argv, jsval *rval) -{ - JSBool ok; - - CHECK_REQUEST(cx); - ok = js_InternalCall(cx, obj, fval, argc, argv, rval); - LAST_FRAME_CHECKS(cx, ok); - return ok; -} - -JS_PUBLIC_API(JSBranchCallback) -JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb) -{ - JSBranchCallback oldcb; - - oldcb = cx->branchCallback; - cx->branchCallback = cb; - return oldcb; -} - -JS_PUBLIC_API(JSBool) -JS_IsRunning(JSContext *cx) -{ - return cx->fp != NULL; -} - -JS_PUBLIC_API(JSBool) -JS_IsConstructing(JSContext *cx) -{ - return cx->fp && (cx->fp->flags & JSFRAME_CONSTRUCTING); -} - -JS_FRIEND_API(JSBool) -JS_IsAssigning(JSContext *cx) -{ - JSStackFrame *fp; - jsbytecode *pc; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - if (!fp || !(pc = fp->pc)) - return JS_FALSE; - return (js_CodeSpec[*pc].format & JOF_ASSIGNING) != 0; -} - -JS_PUBLIC_API(void) -JS_SetCallReturnValue2(JSContext *cx, jsval v) -{ -#if JS_HAS_LVALUE_RETURN - cx->rval2 = v; - cx->rval2set = JS_TRUE; -#endif -} - -JS_PUBLIC_API(JSStackFrame *) -JS_SaveFrameChain(JSContext *cx) -{ - JSStackFrame *fp; - - fp = cx->fp; - if (!fp) - return fp; - - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - cx->fp = NULL; - return fp; -} - -JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp) -{ - JS_ASSERT(!cx->fp); - if (!fp) - return; - - JS_ASSERT(cx->dormantFrameChain == fp); - cx->fp = fp; - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; -} - -/************************************************************************/ - -JS_PUBLIC_API(JSString *) -JS_NewString(JSContext *cx, char *bytes, size_t nbytes) -{ - size_t length = nbytes; - jschar *chars; - JSString *str; - - CHECK_REQUEST(cx); - - /* Make a UTF-16 vector from the 8-bit char codes in bytes. */ - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - - /* Free chars (but not bytes, which caller frees on error) if we fail. */ - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return NULL; - } - - /* Hand off bytes to the deflated string cache, if possible. */ - if (!js_SetStringBytes(cx->runtime, str, bytes, nbytes)) - JS_free(cx, bytes); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_NewStringCopyN(JSContext *cx, const char *s, size_t n) -{ - jschar *js; - JSString *str; - - CHECK_REQUEST(cx); - js = js_InflateString(cx, s, &n); - if (!js) - return NULL; - str = js_NewString(cx, js, n, 0); - if (!str) - JS_free(cx, js); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_NewStringCopyZ(JSContext *cx, const char *s) -{ - size_t n; - jschar *js; - JSString *str; - - CHECK_REQUEST(cx); - if (!s) - return cx->runtime->emptyString; - n = strlen(s); - js = js_InflateString(cx, s, &n); - if (!js) - return NULL; - str = js_NewString(cx, js, n, 0); - if (!str) - JS_free(cx, js); - return str; -} - -JS_PUBLIC_API(JSString *) -JS_InternString(JSContext *cx, const char *s) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_Atomize(cx, s, strlen(s), ATOM_INTERNED); - if (!atom) - return NULL; - return ATOM_TO_STRING(atom); -} - -JS_PUBLIC_API(JSString *) -JS_NewUCString(JSContext *cx, jschar *chars, size_t length) -{ - CHECK_REQUEST(cx); - return js_NewString(cx, chars, length, 0); -} - -JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n) -{ - CHECK_REQUEST(cx); - return js_NewStringCopyN(cx, s, n, 0); -} - -JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyZ(JSContext *cx, const jschar *s) -{ - CHECK_REQUEST(cx); - if (!s) - return cx->runtime->emptyString; - return js_NewStringCopyZ(cx, s, 0); -} - -JS_PUBLIC_API(JSString *) -JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length) -{ - JSAtom *atom; - - CHECK_REQUEST(cx); - atom = js_AtomizeChars(cx, s, length, ATOM_INTERNED); - if (!atom) - return NULL; - return ATOM_TO_STRING(atom); -} - -JS_PUBLIC_API(JSString *) -JS_InternUCString(JSContext *cx, const jschar *s) -{ - return JS_InternUCStringN(cx, s, js_strlen(s)); -} - -JS_PUBLIC_API(char *) -JS_GetStringBytes(JSString *str) -{ - JSRuntime *rt; - char *bytes; - - rt = js_GetGCStringRuntime(str); - bytes = js_GetStringBytes(rt, str); - return bytes ? bytes : ""; -} - -JS_PUBLIC_API(jschar *) -JS_GetStringChars(JSString *str) -{ - /* - * API botch (again, shades of JS_GetStringBytes): we have no cx to pass - * to js_UndependString (called by js_GetStringChars) for out-of-memory - * error reports, so js_UndependString passes NULL and suppresses errors. - * If it fails to convert a dependent string into an independent one, our - * caller will not be guaranteed a \u0000 terminator as a backstop. This - * may break some clients who already misbehave on embedded NULs. - * - * The gain of dependent strings, which cure quadratic and cubic growth - * rate bugs in string concatenation, is worth this slight loss in API - * compatibility. - */ - jschar *chars; - - chars = js_GetStringChars(str); - return chars ? chars : JSSTRING_CHARS(str); -} - -JS_PUBLIC_API(size_t) -JS_GetStringLength(JSString *str) -{ - return JSSTRING_LENGTH(str); -} - -JS_PUBLIC_API(intN) -JS_CompareStrings(JSString *str1, JSString *str2) -{ - return js_CompareStrings(str1, str2); -} - -JS_PUBLIC_API(JSString *) -JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length) -{ - CHECK_REQUEST(cx); - return js_NewString(cx, chars, length, GCF_MUTABLE); -} - -JS_PUBLIC_API(JSString *) -JS_NewDependentString(JSContext *cx, JSString *str, size_t start, - size_t length) -{ - CHECK_REQUEST(cx); - return js_NewDependentString(cx, str, start, length, 0); -} - -JS_PUBLIC_API(JSString *) -JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right) -{ - CHECK_REQUEST(cx); - return js_ConcatStrings(cx, left, right); -} - -JS_PUBLIC_API(const jschar *) -JS_UndependString(JSContext *cx, JSString *str) -{ - CHECK_REQUEST(cx); - return js_UndependString(cx, str); -} - -JS_PUBLIC_API(JSBool) -JS_MakeStringImmutable(JSContext *cx, JSString *str) -{ - CHECK_REQUEST(cx); - if (!js_UndependString(cx, str)) - return JS_FALSE; - - *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, - size_t *dstlenp) -{ - return js_DeflateStringToBuffer(cx, src, srclen, dst, dstlenp); -} - -JS_PUBLIC_API(JSBool) -JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, - size_t *dstlenp) -{ - return js_InflateStringToBuffer(cx, src, srclen, dst, dstlenp); -} - -JS_PUBLIC_API(JSBool) -JS_CStringsAreUTF8() -{ -#ifdef JS_C_STRINGS_ARE_UTF8 - return JS_TRUE; -#else - return JS_FALSE; -#endif -} - -/************************************************************************/ - -JS_PUBLIC_API(void) -JS_ReportError(JSContext *cx, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - js_ReportErrorVA(cx, JSREPORT_ERROR, format, ap); - va_end(ap); -} - -JS_PUBLIC_API(void) -JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...) -{ - va_list ap; - - va_start(ap, errorNumber); - js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, - errorNumber, JS_TRUE, ap); - va_end(ap); -} - -JS_PUBLIC_API(void) -JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...) -{ - va_list ap; - - va_start(ap, errorNumber); - js_ReportErrorNumberVA(cx, JSREPORT_ERROR, errorCallback, userRef, - errorNumber, JS_FALSE, ap); - va_end(ap); -} - -JS_PUBLIC_API(JSBool) -JS_ReportWarning(JSContext *cx, const char *format, ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, format); - ok = js_ReportErrorVA(cx, JSREPORT_WARNING, format, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, errorNumber); - ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, - errorNumber, JS_TRUE, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...) -{ - va_list ap; - JSBool ok; - - va_start(ap, errorNumber); - ok = js_ReportErrorNumberVA(cx, flags, errorCallback, userRef, - errorNumber, JS_FALSE, ap); - va_end(ap); - return ok; -} - -JS_PUBLIC_API(void) -JS_ReportOutOfMemory(JSContext *cx) -{ - js_ReportOutOfMemory(cx); -} - -JS_PUBLIC_API(JSErrorReporter) -JS_SetErrorReporter(JSContext *cx, JSErrorReporter er) -{ - JSErrorReporter older; - - older = cx->errorReporter; - cx->errorReporter = er; - return older; -} - -/************************************************************************/ - -/* - * Regular Expressions. - */ -JS_PUBLIC_API(JSObject *) -JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags) -{ - jschar *chars; - JSObject *obj; - - CHECK_REQUEST(cx); - chars = js_InflateString(cx, bytes, &length); - if (!chars) - return NULL; - obj = js_NewRegExpObject(cx, NULL, chars, length, flags); - JS_free(cx, chars); - return obj; -} - -JS_PUBLIC_API(JSObject *) -JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags) -{ - CHECK_REQUEST(cx); - return js_NewRegExpObject(cx, NULL, chars, length, flags); -} - -JS_PUBLIC_API(void) -JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline) -{ - JSRegExpStatics *res; - - CHECK_REQUEST(cx); - /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->input = input; - res->multiline = multiline; - cx->runtime->gcPoke = JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_ClearRegExpStatics(JSContext *cx) -{ - JSRegExpStatics *res; - - /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->input = NULL; - res->multiline = JS_FALSE; - res->parenCount = 0; - res->lastMatch = res->lastParen = js_EmptySubString; - res->leftContext = res->rightContext = js_EmptySubString; - cx->runtime->gcPoke = JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_ClearRegExpRoots(JSContext *cx) -{ - JSRegExpStatics *res; - - /* No locking required, cx is thread-private and input must be live. */ - res = &cx->regExpStatics; - res->input = NULL; - cx->runtime->gcPoke = JS_TRUE; -} - -/* TODO: compile, execute, get/set other statics... */ - -/************************************************************************/ - -JS_PUBLIC_API(void) -JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks) -{ - cx->localeCallbacks = callbacks; -} - -JS_PUBLIC_API(JSLocaleCallbacks *) -JS_GetLocaleCallbacks(JSContext *cx) -{ - return cx->localeCallbacks; -} - -/************************************************************************/ - -JS_PUBLIC_API(JSBool) -JS_IsExceptionPending(JSContext *cx) -{ - return (JSBool) cx->throwing; -} - -JS_PUBLIC_API(JSBool) -JS_GetPendingException(JSContext *cx, jsval *vp) -{ - CHECK_REQUEST(cx); - if (!cx->throwing) - return JS_FALSE; - *vp = cx->exception; - return JS_TRUE; -} - -JS_PUBLIC_API(void) -JS_SetPendingException(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - cx->throwing = JS_TRUE; - cx->exception = v; -} - -JS_PUBLIC_API(void) -JS_ClearPendingException(JSContext *cx) -{ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; -} - -JS_PUBLIC_API(JSBool) -JS_ReportPendingException(JSContext *cx) -{ - JSBool save, ok; - - CHECK_REQUEST(cx); - - /* - * Set cx->creatingException to suppress the standard error-to-exception - * conversion done by all {js,JS}_Report* functions except for OOM. The - * cx->creatingException flag was added to suppress recursive divergence - * under js_ErrorToException, but it serves for our purposes here too. - */ - save = cx->creatingException; - cx->creatingException = JS_TRUE; - ok = js_ReportUncaughtException(cx); - cx->creatingException = save; - return ok; -} - -struct JSExceptionState { - JSBool throwing; - jsval exception; -}; - -JS_PUBLIC_API(JSExceptionState *) -JS_SaveExceptionState(JSContext *cx) -{ - JSExceptionState *state; - - CHECK_REQUEST(cx); - state = (JSExceptionState *) JS_malloc(cx, sizeof(JSExceptionState)); - if (state) { - state->throwing = JS_GetPendingException(cx, &state->exception); - if (state->throwing && JSVAL_IS_GCTHING(state->exception)) - js_AddRoot(cx, &state->exception, "JSExceptionState.exception"); - } - return state; -} - -JS_PUBLIC_API(void) -JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state) -{ - CHECK_REQUEST(cx); - if (state) { - if (state->throwing) - JS_SetPendingException(cx, state->exception); - else - JS_ClearPendingException(cx); - JS_DropExceptionState(cx, state); - } -} - -JS_PUBLIC_API(void) -JS_DropExceptionState(JSContext *cx, JSExceptionState *state) -{ - CHECK_REQUEST(cx); - if (state) { - if (state->throwing && JSVAL_IS_GCTHING(state->exception)) - JS_RemoveRoot(cx, &state->exception); - JS_free(cx, state); - } -} - -JS_PUBLIC_API(JSErrorReport *) -JS_ErrorFromException(JSContext *cx, jsval v) -{ - CHECK_REQUEST(cx); - return js_ErrorFromException(cx, v); -} - -JS_PUBLIC_API(JSBool) -JS_ThrowReportedError(JSContext *cx, const char *message, - JSErrorReport *reportp) -{ - return js_ErrorToException(cx, message, reportp); -} - -#ifdef JS_THREADSAFE -/* - * Get the owning thread id of a context. Returns 0 if the context is not - * owned by any thread. - */ -JS_PUBLIC_API(jsword) -JS_GetContextThread(JSContext *cx) -{ - return JS_THREAD_ID(cx); -} - -/* - * Set the current thread as the owning thread of a context. Returns the - * old owning thread id, or -1 if the operation failed. - */ -JS_PUBLIC_API(jsword) -JS_SetContextThread(JSContext *cx) -{ - jsword old = JS_THREAD_ID(cx); - if (!js_SetContextThread(cx)) - return -1; - return old; -} - -JS_PUBLIC_API(jsword) -JS_ClearContextThread(JSContext *cx) -{ - jsword old = JS_THREAD_ID(cx); - js_ClearContextThread(cx); - return old; -} -#endif - -/************************************************************************/ - -#if defined(XP_WIN) -#include -/* - * Initialization routine for the JS DLL... - */ - -/* - * Global Instance handle... - * In Win32 this is the module handle of the DLL. - * - * In Win16 this is the instance handle of the application - * which loaded the DLL. - */ - -#ifdef _WIN32 -BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) -{ - return TRUE; -} - -#else /* !_WIN32 */ - -int CALLBACK LibMain( HINSTANCE hInst, WORD wDataSeg, - WORD cbHeapSize, LPSTR lpszCmdLine ) -{ - return TRUE; -} - -BOOL CALLBACK __loadds WEP(BOOL fSystemExit) -{ - return TRUE; -} - -#endif /* !_WIN32 */ -#endif /* XP_WIN */ diff --git a/spidermonkey/libjs/jsapi.h b/spidermonkey/libjs/jsapi.h deleted file mode 100644 index 464f19f..0000000 --- a/spidermonkey/libjs/jsapi.h +++ /dev/null @@ -1,2220 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsapi_h___ -#define jsapi_h___ -/* - * JavaScript API. - */ -#include -#include -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * Type tags stored in the low bits of a jsval. - */ -#define JSVAL_OBJECT 0x0 /* untagged reference to object */ -#define JSVAL_INT 0x1 /* tagged 31-bit integer value */ -#define JSVAL_DOUBLE 0x2 /* tagged reference to double */ -#define JSVAL_STRING 0x4 /* tagged reference to string */ -#define JSVAL_BOOLEAN 0x6 /* tagged boolean value */ - -/* Type tag bitfield length and derived macros. */ -#define JSVAL_TAGBITS 3 -#define JSVAL_TAGMASK JS_BITMASK(JSVAL_TAGBITS) -#define JSVAL_TAG(v) ((v) & JSVAL_TAGMASK) -#define JSVAL_SETTAG(v,t) ((v) | (t)) -#define JSVAL_CLRTAG(v) ((v) & ~(jsval)JSVAL_TAGMASK) -#define JSVAL_ALIGN JS_BIT(JSVAL_TAGBITS) - -/* Predicates for type testing. */ -#define JSVAL_IS_OBJECT(v) (JSVAL_TAG(v) == JSVAL_OBJECT) -#define JSVAL_IS_NUMBER(v) (JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v)) -#define JSVAL_IS_INT(v) (((v) & JSVAL_INT) && (v) != JSVAL_VOID) -#define JSVAL_IS_DOUBLE(v) (JSVAL_TAG(v) == JSVAL_DOUBLE) -#define JSVAL_IS_STRING(v) (JSVAL_TAG(v) == JSVAL_STRING) -#define JSVAL_IS_BOOLEAN(v) (JSVAL_TAG(v) == JSVAL_BOOLEAN) -#define JSVAL_IS_NULL(v) ((v) == JSVAL_NULL) -#define JSVAL_IS_VOID(v) ((v) == JSVAL_VOID) -#define JSVAL_IS_PRIMITIVE(v) (!JSVAL_IS_OBJECT(v) || JSVAL_IS_NULL(v)) - -/* Objects, strings, and doubles are GC'ed. */ -#define JSVAL_IS_GCTHING(v) (!((v) & JSVAL_INT) && !JSVAL_IS_BOOLEAN(v)) -#define JSVAL_TO_GCTHING(v) ((void *)JSVAL_CLRTAG(v)) -#define JSVAL_TO_OBJECT(v) ((JSObject *)JSVAL_TO_GCTHING(v)) -#define JSVAL_TO_DOUBLE(v) ((jsdouble *)JSVAL_TO_GCTHING(v)) -#define JSVAL_TO_STRING(v) ((JSString *)JSVAL_TO_GCTHING(v)) -#define OBJECT_TO_JSVAL(obj) ((jsval)(obj)) -#define DOUBLE_TO_JSVAL(dp) JSVAL_SETTAG((jsval)(dp), JSVAL_DOUBLE) -#define STRING_TO_JSVAL(str) JSVAL_SETTAG((jsval)(str), JSVAL_STRING) - -/* Lock and unlock the GC thing held by a jsval. */ -#define JSVAL_LOCK(cx,v) (JSVAL_IS_GCTHING(v) \ - ? JS_LockGCThing(cx, JSVAL_TO_GCTHING(v)) \ - : JS_TRUE) -#define JSVAL_UNLOCK(cx,v) (JSVAL_IS_GCTHING(v) \ - ? JS_UnlockGCThing(cx, JSVAL_TO_GCTHING(v)) \ - : JS_TRUE) - -/* Domain limits for the jsval int type. */ -#define JSVAL_INT_BITS 31 -#define JSVAL_INT_POW2(n) ((jsval)1 << (n)) -#define JSVAL_INT_MIN ((jsval)1 - JSVAL_INT_POW2(30)) -#define JSVAL_INT_MAX (JSVAL_INT_POW2(30) - 1) -#define INT_FITS_IN_JSVAL(i) ((jsuint)((i)+JSVAL_INT_MAX) <= 2*JSVAL_INT_MAX) -#define JSVAL_TO_INT(v) ((jsint)(v) >> 1) -#define INT_TO_JSVAL(i) (((jsval)(i) << 1) | JSVAL_INT) - -/* Convert between boolean and jsval. */ -#define JSVAL_TO_BOOLEAN(v) ((JSBool)((v) >> JSVAL_TAGBITS)) -#define BOOLEAN_TO_JSVAL(b) JSVAL_SETTAG((jsval)(b) << JSVAL_TAGBITS, \ - JSVAL_BOOLEAN) - -/* A private data pointer (2-byte-aligned) can be stored as an int jsval. */ -#define JSVAL_TO_PRIVATE(v) ((void *)((v) & ~JSVAL_INT)) -#define PRIVATE_TO_JSVAL(p) ((jsval)(p) | JSVAL_INT) - -/* Property attributes, set in JSPropertySpec and passed to API functions. */ -#define JSPROP_ENUMERATE 0x01 /* property is visible to for/in loop */ -#define JSPROP_READONLY 0x02 /* not settable: assignment is no-op */ -#define JSPROP_PERMANENT 0x04 /* property cannot be deleted */ -#define JSPROP_EXPORTED 0x08 /* property is exported from object */ -#define JSPROP_GETTER 0x10 /* property holds getter function */ -#define JSPROP_SETTER 0x20 /* property holds setter function */ -#define JSPROP_SHARED 0x40 /* don't allocate a value slot for this - property; don't copy the property on - set of the same-named property in an - object that delegates to a prototype - containing this property */ -#define JSPROP_INDEX 0x80 /* name is actually (jsint) index */ - -/* Function flags, set in JSFunctionSpec and passed to JS_NewFunction etc. */ -#define JSFUN_LAMBDA 0x08 /* expressed, not declared, function */ -#define JSFUN_GETTER JSPROP_GETTER -#define JSFUN_SETTER JSPROP_SETTER -#define JSFUN_BOUND_METHOD 0x40 /* bind this to fun->object's parent */ -#define JSFUN_HEAVYWEIGHT 0x80 /* activation requires a Call object */ - -#define JSFUN_DISJOINT_FLAGS(f) ((f) & 0x0f) -#define JSFUN_GSFLAGS(f) ((f) & (JSFUN_GETTER | JSFUN_SETTER)) - -#ifdef MOZILLA_1_8_BRANCH - -/* - * Squeeze three more bits into existing 8-bit flags by taking advantage of - * the invalid combination (JSFUN_GETTER | JSFUN_SETTER). - */ -#define JSFUN_GETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_GETTER) -#define JSFUN_SETTER_TEST(f) (JSFUN_GSFLAGS(f) == JSFUN_SETTER) -#define JSFUN_FLAGS_TEST(f,t) (JSFUN_GSFLAGS(~(f)) ? (f) & (t) : 0) -#define JSFUN_BOUND_METHOD_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_BOUND_METHOD) -#define JSFUN_HEAVYWEIGHT_TEST(f) JSFUN_FLAGS_TEST(f, JSFUN_HEAVYWEIGHT) - -#define JSFUN_GSFLAG2ATTR(f) (JSFUN_GETTER_TEST(f) ? JSPROP_GETTER : \ - JSFUN_SETTER_TEST(f) ? JSPROP_SETTER : 0) - -#define JSFUN_THISP_FLAGS(f) (JSFUN_GSFLAGS(~(f)) ? 0 : \ - (f) & JSFUN_THISP_PRIMITIVE) -#define JSFUN_THISP_TEST(f,t) ((f) == (t) || (f) == JSFUN_THISP_PRIMITIVE) - -#define JSFUN_THISP_STRING 0x30 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x70 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0xb0 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0xf0 /* |this| may be any primitive value */ - -#define JSFUN_FLAGS_MASK 0xf8 /* overlay JSFUN_* attributes */ - -#else - -#define JSFUN_GETTER_TEST(f) ((f) & JSFUN_GETTER) -#define JSFUN_SETTER_TEST(f) ((f) & JSFUN_SETTER) -#define JSFUN_BOUND_METHOD_TEST(f) ((f) & JSFUN_BOUND_METHOD) -#define JSFUN_HEAVYWEIGHT_TEST(f) ((f) & JSFUN_HEAVYWEIGHT) - -#define JSFUN_GSFLAG2ATTR(f) JSFUN_GSFLAGS(f) - -#define JSFUN_THISP_FLAGS(f) (f) -#define JSFUN_THISP_TEST(f,t) ((f) & t) - -#define JSFUN_THISP_STRING 0x0100 /* |this| may be a primitive string */ -#define JSFUN_THISP_NUMBER 0x0200 /* |this| may be a primitive number */ -#define JSFUN_THISP_BOOLEAN 0x0400 /* |this| may be a primitive boolean */ -#define JSFUN_THISP_PRIMITIVE 0x0700 /* |this| may be any primitive value */ - -#define JSFUN_FLAGS_MASK 0x07f8 /* overlay JSFUN_* attributes -- - note that bit #15 is used internally - to flag interpreted functions */ - -#endif - -/* - * Re-use JSFUN_LAMBDA, which applies only to scripted functions, for use in - * JSFunctionSpec arrays that specify generic native prototype methods, i.e., - * methods of a class prototype that are exposed as static methods taking an - * extra leading argument: the generic |this| parameter. - * - * If you set this flag in a JSFunctionSpec struct's flags initializer, then - * that struct must live at least as long as the native static method object - * created due to this flag by JS_DefineFunctions or JS_InitClass. Typically - * JSFunctionSpec structs are allocated in static arrays. - */ -#define JSFUN_GENERIC_NATIVE JSFUN_LAMBDA - -/* - * Well-known JS values. The extern'd variables are initialized when the - * first JSContext is created by JS_NewContext (see below). - */ -#define JSVAL_VOID INT_TO_JSVAL(0 - JSVAL_INT_POW2(30)) -#define JSVAL_NULL OBJECT_TO_JSVAL(0) -#define JSVAL_ZERO INT_TO_JSVAL(0) -#define JSVAL_ONE INT_TO_JSVAL(1) -#define JSVAL_FALSE BOOLEAN_TO_JSVAL(JS_FALSE) -#define JSVAL_TRUE BOOLEAN_TO_JSVAL(JS_TRUE) - -/* - * Microseconds since the epoch, midnight, January 1, 1970 UTC. See the - * comment in jstypes.h regarding safe int64 usage. - */ -extern JS_PUBLIC_API(int64) -JS_Now(); - -/* Don't want to export data, so provide accessors for non-inline jsvals. */ -extern JS_PUBLIC_API(jsval) -JS_GetNaNValue(JSContext *cx); - -extern JS_PUBLIC_API(jsval) -JS_GetNegativeInfinityValue(JSContext *cx); - -extern JS_PUBLIC_API(jsval) -JS_GetPositiveInfinityValue(JSContext *cx); - -extern JS_PUBLIC_API(jsval) -JS_GetEmptyStringValue(JSContext *cx); - -/* - * Format is a string of the following characters (spaces are insignificant), - * specifying the tabulated type conversions: - * - * b JSBool Boolean - * c uint16/jschar ECMA uint16, Unicode char - * i int32 ECMA int32 - * u uint32 ECMA uint32 - * j int32 Rounded int32 (coordinate) - * d jsdouble IEEE double - * I jsdouble Integral IEEE double - * s char * C string - * S JSString * Unicode string, accessed by a JSString pointer - * W jschar * Unicode character vector, 0-terminated (W for wide) - * o JSObject * Object reference - * f JSFunction * Function private - * v jsval Argument value (no conversion) - * * N/A Skip this argument (no vararg) - * / N/A End of required arguments - * - * The variable argument list after format must consist of &b, &c, &s, e.g., - * where those variables have the types given above. For the pointer types - * char *, JSString *, and JSObject *, the pointed-at memory returned belongs - * to the JS runtime, not to the calling native code. The runtime promises - * to keep this memory valid so long as argv refers to allocated stack space - * (so long as the native function is active). - * - * Fewer arguments than format specifies may be passed only if there is a / - * in format after the last required argument specifier and argc is at least - * the number of required arguments. More arguments than format specifies - * may be passed without error; it is up to the caller to deal with trailing - * unconverted arguments. - */ -extern JS_PUBLIC_API(JSBool) -JS_ConvertArguments(JSContext *cx, uintN argc, jsval *argv, const char *format, - ...); - -#ifdef va_start -extern JS_PUBLIC_API(JSBool) -JS_ConvertArgumentsVA(JSContext *cx, uintN argc, jsval *argv, - const char *format, va_list ap); -#endif - -/* - * Inverse of JS_ConvertArguments: scan format and convert trailing arguments - * into jsvals, GC-rooted if necessary by the JS stack. Return null on error, - * and a pointer to the new argument vector on success. Also return a stack - * mark on success via *markp, in which case the caller must eventually clean - * up by calling JS_PopArguments. - * - * Note that the number of actual arguments supplied is specified exclusively - * by format, so there is no argc parameter. - */ -extern JS_PUBLIC_API(jsval *) -JS_PushArguments(JSContext *cx, void **markp, const char *format, ...); - -#ifdef va_start -extern JS_PUBLIC_API(jsval *) -JS_PushArgumentsVA(JSContext *cx, void **markp, const char *format, va_list ap); -#endif - -extern JS_PUBLIC_API(void) -JS_PopArguments(JSContext *cx, void *mark); - -#ifdef JS_ARGUMENT_FORMATTER_DEFINED - -/* - * Add and remove a format string handler for JS_{Convert,Push}Arguments{,VA}. - * The handler function has this signature (see jspubtd.h): - * - * JSBool MyArgumentFormatter(JSContext *cx, const char *format, - * JSBool fromJS, jsval **vpp, va_list *app); - * - * It should return true on success, and return false after reporting an error - * or detecting an already-reported error. - * - * For a given format string, for example "AA", the formatter is called from - * JS_ConvertArgumentsVA like so: - * - * formatter(cx, "AA...", JS_TRUE, &sp, &ap); - * - * sp points into the arguments array on the JS stack, while ap points into - * the stdarg.h va_list on the C stack. The JS_TRUE passed for fromJS tells - * the formatter to convert zero or more jsvals at sp to zero or more C values - * accessed via pointers-to-values at ap, updating both sp (via *vpp) and ap - * (via *app) to point past the converted arguments and their result pointers - * on the C stack. - * - * When called from JS_PushArgumentsVA, the formatter is invoked thus: - * - * formatter(cx, "AA...", JS_FALSE, &sp, &ap); - * - * where JS_FALSE for fromJS means to wrap the C values at ap according to the - * format specifier and store them at sp, updating ap and sp appropriately. - * - * The "..." after "AA" is the rest of the format string that was passed into - * JS_{Convert,Push}Arguments{,VA}. The actual format trailing substring used - * in each Convert or PushArguments call is passed to the formatter, so that - * one such function may implement several formats, in order to share code. - * - * Remove just forgets about any handler associated with format. Add does not - * copy format, it points at the string storage allocated by the caller, which - * is typically a string constant. If format is in dynamic storage, it is up - * to the caller to keep the string alive until Remove is called. - */ -extern JS_PUBLIC_API(JSBool) -JS_AddArgumentFormatter(JSContext *cx, const char *format, - JSArgumentFormatter formatter); - -extern JS_PUBLIC_API(void) -JS_RemoveArgumentFormatter(JSContext *cx, const char *format); - -#endif /* JS_ARGUMENT_FORMATTER_DEFINED */ - -extern JS_PUBLIC_API(JSBool) -JS_ConvertValue(JSContext *cx, jsval v, JSType type, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToObject(JSContext *cx, jsval v, JSObject **objp); - -extern JS_PUBLIC_API(JSFunction *) -JS_ValueToFunction(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(JSFunction *) -JS_ValueToConstructor(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(JSString *) -JS_ValueToString(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); - -/* - * Convert a value to a number, then to an int32, according to the ECMA rules - * for ToInt32. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); - -/* - * Convert a value to a number, then to a uint32, according to the ECMA rules - * for ToUint32. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); - -/* - * Convert a value to a number, then to an int32 if it fits by rounding to - * nearest; but failing with an error report if the double is out of range - * or unordered. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToInt32(JSContext *cx, jsval v, int32 *ip); - -/* - * ECMA ToUint16, for mapping a jsval to a Unicode point. - */ -extern JS_PUBLIC_API(JSBool) -JS_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); - -extern JS_PUBLIC_API(JSType) -JS_TypeOfValue(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(const char *) -JS_GetTypeName(JSContext *cx, JSType type); - -/************************************************************************/ - -/* - * Initialization, locking, contexts, and memory allocation. - */ -#define JS_NewRuntime JS_Init -#define JS_DestroyRuntime JS_Finish -#define JS_LockRuntime JS_Lock -#define JS_UnlockRuntime JS_Unlock - -extern JS_PUBLIC_API(JSRuntime *) -JS_NewRuntime(uint32 maxbytes); - -extern JS_PUBLIC_API(void) -JS_DestroyRuntime(JSRuntime *rt); - -extern JS_PUBLIC_API(void) -JS_ShutDown(void); - -JS_PUBLIC_API(void *) -JS_GetRuntimePrivate(JSRuntime *rt); - -JS_PUBLIC_API(void) -JS_SetRuntimePrivate(JSRuntime *rt, void *data); - -#ifdef JS_THREADSAFE - -extern JS_PUBLIC_API(void) -JS_BeginRequest(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_EndRequest(JSContext *cx); - -/* Yield to pending GC operations, regardless of request depth */ -extern JS_PUBLIC_API(void) -JS_YieldRequest(JSContext *cx); - -extern JS_PUBLIC_API(jsrefcount) -JS_SuspendRequest(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth); - -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoRequest { - public: - JSAutoRequest(JSContext *cx) : mContext(cx), mSaveDepth(0) { - JS_BeginRequest(mContext); - } - ~JSAutoRequest() { - JS_EndRequest(mContext); - } - - void suspend() { - mSaveDepth = JS_SuspendRequest(mContext); - } - void resume() { - JS_ResumeRequest(mContext, mSaveDepth); - } - - protected: - JSContext *mContext; - jsrefcount mSaveDepth; - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif - -#endif /* JS_THREADSAFE */ - -extern JS_PUBLIC_API(void) -JS_Lock(JSRuntime *rt); - -extern JS_PUBLIC_API(void) -JS_Unlock(JSRuntime *rt); - -extern JS_PUBLIC_API(JSContextCallback) -JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback); - -extern JS_PUBLIC_API(JSContext *) -JS_NewContext(JSRuntime *rt, size_t stackChunkSize); - -extern JS_PUBLIC_API(void) -JS_DestroyContext(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_DestroyContextNoGC(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_DestroyContextMaybeGC(JSContext *cx); - -extern JS_PUBLIC_API(void *) -JS_GetContextPrivate(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_SetContextPrivate(JSContext *cx, void *data); - -extern JS_PUBLIC_API(JSRuntime *) -JS_GetRuntime(JSContext *cx); - -extern JS_PUBLIC_API(JSContext *) -JS_ContextIterator(JSRuntime *rt, JSContext **iterp); - -extern JS_PUBLIC_API(JSVersion) -JS_GetVersion(JSContext *cx); - -extern JS_PUBLIC_API(JSVersion) -JS_SetVersion(JSContext *cx, JSVersion version); - -extern JS_PUBLIC_API(const char *) -JS_VersionToString(JSVersion version); - -extern JS_PUBLIC_API(JSVersion) -JS_StringToVersion(const char *string); - -/* - * JS options are orthogonal to version, and may be freely composed with one - * another as well as with version. - * - * JSOPTION_VAROBJFIX is recommended -- see the comments associated with the - * prototypes for JS_ExecuteScript, JS_EvaluateScript, etc. - */ -#define JSOPTION_STRICT JS_BIT(0) /* warn on dubious practice */ -#define JSOPTION_WERROR JS_BIT(1) /* convert warning to error */ -#define JSOPTION_VAROBJFIX JS_BIT(2) /* make JS_EvaluateScript use - the last object on its 'obj' - param's scope chain as the - ECMA 'variables object' */ -#define JSOPTION_PRIVATE_IS_NSISUPPORTS \ - JS_BIT(3) /* context private data points - to an nsISupports subclass */ -#define JSOPTION_COMPILE_N_GO JS_BIT(4) /* caller of JS_Compile*Script - promises to execute compiled - script once only; enables - compile-time scope chain - resolution of consts. */ -#define JSOPTION_ATLINE JS_BIT(5) /* //@line number ["filename"] - option supported for the - XUL preprocessor and kindred - beasts. */ -#define JSOPTION_XML JS_BIT(6) /* EMCAScript for XML support: - parse as a token, - not backward compatible with - the comment-hiding hack used - in HTML script tags. */ -#define JSOPTION_NATIVE_BRANCH_CALLBACK \ - JS_BIT(7) /* the branch callback set by - JS_SetBranchCallback may be - called with a null script - parameter, by native code - that loops intensively */ -#define JSOPTION_DONT_REPORT_UNCAUGHT \ - JS_BIT(8) /* When returning from the - outermost API call, prevent - uncaught exceptions from - being converted to error - reports */ - -extern JS_PUBLIC_API(uint32) -JS_GetOptions(JSContext *cx); - -extern JS_PUBLIC_API(uint32) -JS_SetOptions(JSContext *cx, uint32 options); - -extern JS_PUBLIC_API(uint32) -JS_ToggleOptions(JSContext *cx, uint32 options); - -extern JS_PUBLIC_API(const char *) -JS_GetImplementationVersion(void); - -extern JS_PUBLIC_API(JSObject *) -JS_GetGlobalObject(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_SetGlobalObject(JSContext *cx, JSObject *obj); - -/* - * Initialize standard JS class constructors, prototypes, and any top-level - * functions and constants associated with the standard classes (e.g. isNaN - * for Number). - * - * NB: This sets cx's global object to obj if it was null. - */ -extern JS_PUBLIC_API(JSBool) -JS_InitStandardClasses(JSContext *cx, JSObject *obj); - -/* - * Resolve id, which must contain either a string or an int, to a standard - * class name in obj if possible, defining the class's constructor and/or - * prototype and storing true in *resolved. If id does not name a standard - * class or a top-level property induced by initializing a standard class, - * store false in *resolved and just return true. Return false on error, - * as usual for JSBool result-typed API entry points. - * - * This API can be called directly from a global object class's resolve op, - * to define standard classes lazily. The class's enumerate op should call - * JS_EnumerateStandardClasses(cx, obj), to define eagerly during for..in - * loops any classes not yet resolved lazily. - */ -extern JS_PUBLIC_API(JSBool) -JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id, - JSBool *resolved); - -extern JS_PUBLIC_API(JSBool) -JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj); - -/* - * Enumerate any already-resolved standard class ids into ida, or into a new - * JSIdArray if ida is null. Return the augmented array on success, null on - * failure with ida (if it was non-null on entry) destroyed. - */ -extern JS_PUBLIC_API(JSIdArray *) -JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, - JSIdArray *ida); - -extern JS_PUBLIC_API(JSBool) -JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetScopeChain(JSContext *cx); - -extern JS_PUBLIC_API(void *) -JS_malloc(JSContext *cx, size_t nbytes); - -extern JS_PUBLIC_API(void *) -JS_realloc(JSContext *cx, void *p, size_t nbytes); - -extern JS_PUBLIC_API(void) -JS_free(JSContext *cx, void *p); - -extern JS_PUBLIC_API(char *) -JS_strdup(JSContext *cx, const char *s); - -extern JS_PUBLIC_API(jsdouble *) -JS_NewDouble(JSContext *cx, jsdouble d); - -extern JS_PUBLIC_API(JSBool) -JS_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); - -/* - * A JS GC root is a pointer to a JSObject *, JSString *, or jsdouble * that - * itself points into the GC heap (more recently, we support this extension: - * a root may be a pointer to a jsval v for which JSVAL_IS_GCTHING(v) is true). - * - * Therefore, you never pass JSObject *obj to JS_AddRoot(cx, obj). You always - * call JS_AddRoot(cx, &obj), passing obj by reference. And later, before obj - * or the structure it is embedded within goes out of scope or is freed, you - * must call JS_RemoveRoot(cx, &obj). - * - * Also, use JS_AddNamedRoot(cx, &structPtr->memberObj, "structPtr->memberObj") - * in preference to JS_AddRoot(cx, &structPtr->memberObj), in order to identify - * roots by their source callsites. This way, you can find the callsite while - * debugging if you should fail to do JS_RemoveRoot(cx, &structPtr->memberObj) - * before freeing structPtr's memory. - */ -extern JS_PUBLIC_API(JSBool) -JS_AddRoot(JSContext *cx, void *rp); - -#ifdef NAME_ALL_GC_ROOTS -#define JS_DEFINE_TO_TOKEN(def) #def -#define JS_DEFINE_TO_STRING(def) JS_DEFINE_TO_TOKEN(def) -#define JS_AddRoot(cx,rp) JS_AddNamedRoot((cx), (rp), (__FILE__ ":" JS_TOKEN_TO_STRING(__LINE__)) -#endif - -extern JS_PUBLIC_API(JSBool) -JS_AddNamedRoot(JSContext *cx, void *rp, const char *name); - -extern JS_PUBLIC_API(JSBool) -JS_AddNamedRootRT(JSRuntime *rt, void *rp, const char *name); - -extern JS_PUBLIC_API(JSBool) -JS_RemoveRoot(JSContext *cx, void *rp); - -extern JS_PUBLIC_API(JSBool) -JS_RemoveRootRT(JSRuntime *rt, void *rp); - -/* - * The last GC thing of each type (object, string, double, external string - * types) created on a given context is kept alive until another thing of the - * same type is created, using a newborn root in the context. These newborn - * roots help native code protect newly-created GC-things from GC invocations - * activated before those things can be rooted using local or global roots. - * - * However, the newborn roots can also entrain great gobs of garbage, so the - * JS_GC entry point clears them for the context on which GC is being forced. - * Embeddings may need to do likewise for all contexts. - * - * See the scoped local root API immediately below for a better way to manage - * newborns in cases where native hooks (functions, getters, setters, etc.) - * create many GC-things, potentially without connecting them to predefined - * local roots such as *rval or argv[i] in an active native function. Using - * JS_EnterLocalRootScope disables updating of the context's per-gc-thing-type - * newborn roots, until control flow unwinds and leaves the outermost nesting - * local root scope. - */ -extern JS_PUBLIC_API(void) -JS_ClearNewbornRoots(JSContext *cx); - -/* - * Scoped local root management allows native functions, getter/setters, etc. - * to avoid worrying about the newborn root pigeon-holes, overloading local - * roots allocated in argv and *rval, or ending up having to call JS_Add*Root - * and JS_RemoveRoot to manage global roots temporarily. - * - * Instead, calling JS_EnterLocalRootScope and JS_LeaveLocalRootScope around - * the body of the native hook causes the engine to allocate a local root for - * each newborn created in between the two API calls, using a local root stack - * associated with cx. For example: - * - * JSBool - * my_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) - * { - * JSBool ok; - * - * if (!JS_EnterLocalRootScope(cx)) - * return JS_FALSE; - * ok = my_GetPropertyBody(cx, obj, id, vp); - * JS_LeaveLocalRootScope(cx); - * return ok; - * } - * - * NB: JS_LeaveLocalRootScope must be called once for every prior successful - * call to JS_EnterLocalRootScope. If JS_EnterLocalRootScope fails, you must - * not make the matching JS_LeaveLocalRootScope call. - * - * JS_LeaveLocalRootScopeWithResult(cx, rval) is an alternative way to leave - * a local root scope that protects a result or return value, by effectively - * pushing it in the caller's local root scope. - * - * In case a native hook allocates many objects or other GC-things, but the - * native protects some of those GC-things by storing them as property values - * in an object that is itself protected, the hook can call JS_ForgetLocalRoot - * to free the local root automatically pushed for the now-protected GC-thing. - * - * JS_ForgetLocalRoot works on any GC-thing allocated in the current local - * root scope, but it's more time-efficient when called on references to more - * recently created GC-things. Calling it successively on other than the most - * recently allocated GC-thing will tend to average the time inefficiency, and - * may risk O(n^2) growth rate, but in any event, you shouldn't allocate too - * many local roots if you can root as you go (build a tree of objects from - * the top down, forgetting each latest-allocated GC-thing immediately upon - * linking it to its parent). - */ -extern JS_PUBLIC_API(JSBool) -JS_EnterLocalRootScope(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScope(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - -extern JS_PUBLIC_API(void) -JS_ForgetLocalRoot(JSContext *cx, void *thing); - -#ifdef __cplusplus -JS_END_EXTERN_C - -class JSAutoLocalRootScope { - public: - JSAutoLocalRootScope(JSContext *cx) : mContext(cx) { - JS_EnterLocalRootScope(mContext); - } - ~JSAutoLocalRootScope() { - JS_LeaveLocalRootScope(mContext); - } - - void forget(void *thing) { - JS_ForgetLocalRoot(mContext, thing); - } - - protected: - JSContext *mContext; - -#if 0 - private: - static void *operator new(size_t) CPP_THROW_NEW { return 0; }; - static void operator delete(void *, size_t) { }; -#endif -}; - -JS_BEGIN_EXTERN_C -#endif - -#ifdef DEBUG -extern JS_PUBLIC_API(void) -JS_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data); -#endif - -/* - * Call JS_MapGCRoots to map the GC's roots table using map(rp, name, data). - * The root is pointed at by rp; if the root is unnamed, name is null; data is - * supplied from the third parameter to JS_MapGCRoots. - * - * The map function should return JS_MAP_GCROOT_REMOVE to cause the currently - * enumerated root to be removed. To stop enumeration, set JS_MAP_GCROOT_STOP - * in the return value. To keep on mapping, return JS_MAP_GCROOT_NEXT. These - * constants are flags; you can OR them together. - * - * This function acquires and releases rt's GC lock around the mapping of the - * roots table, so the map function should run to completion in as few cycles - * as possible. Of course, map cannot call JS_GC, JS_MaybeGC, JS_BeginRequest, - * or any JS API entry point that acquires locks, without double-tripping or - * deadlocking on the GC lock. - * - * JS_MapGCRoots returns the count of roots that were successfully mapped. - */ -#define JS_MAP_GCROOT_NEXT 0 /* continue mapping entries */ -#define JS_MAP_GCROOT_STOP 1 /* stop mapping entries */ -#define JS_MAP_GCROOT_REMOVE 2 /* remove and free the current entry */ - -typedef intN -(* JS_DLL_CALLBACK JSGCRootMapFun)(void *rp, const char *name, void *data); - -extern JS_PUBLIC_API(uint32) -JS_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -extern JS_PUBLIC_API(JSBool) -JS_LockGCThing(JSContext *cx, void *thing); - -extern JS_PUBLIC_API(JSBool) -JS_LockGCThingRT(JSRuntime *rt, void *thing); - -extern JS_PUBLIC_API(JSBool) -JS_UnlockGCThing(JSContext *cx, void *thing); - -extern JS_PUBLIC_API(JSBool) -JS_UnlockGCThingRT(JSRuntime *rt, void *thing); - -/* - * For implementors of JSObjectOps.mark, to mark a GC-thing reachable via a - * property or other strong ref identified for debugging purposes by name. - * The name argument's storage needs to live only as long as the call to - * this routine. - * - * The final arg is used by GC_MARK_DEBUG code to build a ref path through - * the GC's live thing graph. Implementors of JSObjectOps.mark should pass - * its final arg through to this function when marking all GC-things that are - * directly reachable from the object being marked. - * - * See the JSMarkOp typedef in jspubtd.h, and the JSObjectOps struct below. - */ -extern JS_PUBLIC_API(void) -JS_MarkGCThing(JSContext *cx, void *thing, const char *name, void *arg); - -extern JS_PUBLIC_API(void) -JS_GC(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_MaybeGC(JSContext *cx); - -extern JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallback(JSContext *cx, JSGCCallback cb); - -extern JS_PUBLIC_API(JSGCCallback) -JS_SetGCCallbackRT(JSRuntime *rt, JSGCCallback cb); - -extern JS_PUBLIC_API(JSBool) -JS_IsAboutToBeFinalized(JSContext *cx, void *thing); - -typedef enum JSGCParamKey { - JSGC_MAX_BYTES = 0, /* maximum nominal heap before last ditch GC */ - JSGC_MAX_MALLOC_BYTES = 1 /* # of JS_malloc bytes before last ditch GC */ -} JSGCParamKey; - -extern JS_PUBLIC_API(void) -JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value); - -/* - * Add a finalizer for external strings created by JS_NewExternalString (see - * below) using a type-code returned from this function, and that understands - * how to free or release the memory pointed at by JS_GetStringChars(str). - * - * Return a nonnegative type index if there is room for finalizer in the - * global GC finalizers table, else return -1. If the engine is compiled - * JS_THREADSAFE and used in a multi-threaded environment, this function must - * be invoked on the primordial thread only, at startup -- or else the entire - * program must single-thread itself while loading a module that calls this - * function. - */ -extern JS_PUBLIC_API(intN) -JS_AddExternalStringFinalizer(JSStringFinalizeOp finalizer); - -/* - * Remove finalizer from the global GC finalizers table, returning its type - * code if found, -1 if not found. - * - * As with JS_AddExternalStringFinalizer, there is a threading restriction - * if you compile the engine JS_THREADSAFE: this function may be called for a - * given finalizer pointer on only one thread; different threads may call to - * remove distinct finalizers safely. - * - * You must ensure that all strings with finalizer's type have been collected - * before calling this function. Otherwise, string data will be leaked by the - * GC, for want of a finalizer to call. - */ -extern JS_PUBLIC_API(intN) -JS_RemoveExternalStringFinalizer(JSStringFinalizeOp finalizer); - -/* - * Create a new JSString whose chars member refers to external memory, i.e., - * memory requiring special, type-specific finalization. The type code must - * be a nonnegative return value from JS_AddExternalStringFinalizer. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewExternalString(JSContext *cx, jschar *chars, size_t length, intN type); - -/* - * Returns the external-string finalizer index for this string, or -1 if it is - * an "internal" (native to JS engine) string. - */ -extern JS_PUBLIC_API(intN) -JS_GetExternalStringGCType(JSRuntime *rt, JSString *str); - -/* - * Sets maximum (if stack grows upward) or minimum (downward) legal stack byte - * address in limitAddr for the thread or process stack used by cx. To disable - * stack size checking, pass 0 for limitAddr. - */ -extern JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, jsuword limitAddr); - -/************************************************************************/ - -/* - * Classes, objects, and properties. - */ - -/* For detailed comments on the function pointer types, see jspubtd.h. */ -struct JSClass { - const char *name; - uint32 flags; - - /* Mandatory non-null function pointer members. */ - JSPropertyOp addProperty; - JSPropertyOp delProperty; - JSPropertyOp getProperty; - JSPropertyOp setProperty; - JSEnumerateOp enumerate; - JSResolveOp resolve; - JSConvertOp convert; - JSFinalizeOp finalize; - - /* Optionally non-null members start here. */ - JSGetObjectOps getObjectOps; - JSCheckAccessOp checkAccess; - JSNative call; - JSNative construct; - JSXDRObjectOp xdrObject; - JSHasInstanceOp hasInstance; - JSMarkOp mark; - JSReserveSlotsOp reserveSlots; -}; - -struct JSExtendedClass { - JSClass base; - JSEqualityOp equality; - JSObjectOp outerObject; - JSObjectOp innerObject; - void (*reserved0)(); - void (*reserved1)(); - void (*reserved2)(); - void (*reserved3)(); - void (*reserved4)(); -}; - -#define JSCLASS_HAS_PRIVATE (1<<0) /* objects have private slot */ -#define JSCLASS_NEW_ENUMERATE (1<<1) /* has JSNewEnumerateOp hook */ -#define JSCLASS_NEW_RESOLVE (1<<2) /* has JSNewResolveOp hook */ -#define JSCLASS_PRIVATE_IS_NSISUPPORTS (1<<3) /* private is (nsISupports *) */ -#define JSCLASS_SHARE_ALL_PROPERTIES (1<<4) /* all properties are SHARED */ -#define JSCLASS_NEW_RESOLVE_GETS_START (1<<5) /* JSNewResolveOp gets starting - object in prototype chain - passed in via *objp in/out - parameter */ -#define JSCLASS_CONSTRUCT_PROTOTYPE (1<<6) /* call constructor on class - prototype */ -#define JSCLASS_DOCUMENT_OBSERVER (1<<7) /* DOM document observer */ - -/* - * To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or - * JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where - * n is a constant in [1, 255]. Reserved slots are indexed from 0 to n-1. - */ -#define JSCLASS_RESERVED_SLOTS_SHIFT 8 /* room for 8 flags below */ -#define JSCLASS_RESERVED_SLOTS_WIDTH 8 /* and 16 above this field */ -#define JSCLASS_RESERVED_SLOTS_MASK JS_BITMASK(JSCLASS_RESERVED_SLOTS_WIDTH) -#define JSCLASS_HAS_RESERVED_SLOTS(n) (((n) & JSCLASS_RESERVED_SLOTS_MASK) \ - << JSCLASS_RESERVED_SLOTS_SHIFT) -#define JSCLASS_RESERVED_SLOTS(clasp) (((clasp)->flags \ - >> JSCLASS_RESERVED_SLOTS_SHIFT) \ - & JSCLASS_RESERVED_SLOTS_MASK) - -#define JSCLASS_HIGH_FLAGS_SHIFT (JSCLASS_RESERVED_SLOTS_SHIFT + \ - JSCLASS_RESERVED_SLOTS_WIDTH) - -/* True if JSClass is really a JSExtendedClass. */ -#define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0)) -#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1)) -#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2)) - -/* - * ECMA-262 requires that most constructors used internally create objects - * with "the original Foo.prototype value" as their [[Prototype]] (__proto__) - * member initial value. The "original ... value" verbiage is there because - * in ECMA-262, global properties naming class objects are read/write and - * deleteable, for the most part. - * - * Implementing this efficiently requires that global objects have classes - * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break - * anything except the ECMA-262 "original prototype value" behavior, which was - * broken for years in SpiderMonkey. In other words, without these flags you - * get backward compatibility. - */ -#define JSCLASS_GLOBAL_FLAGS \ - (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT)) - -/* Fast access to the original value of each standard class's prototype. */ -#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8) -#define JSCLASS_CACHED_PROTO_WIDTH 8 -#define JSCLASS_CACHED_PROTO_MASK JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH) -#define JSCLASS_HAS_CACHED_PROTO(key) ((key) << JSCLASS_CACHED_PROTO_SHIFT) -#define JSCLASS_CACHED_PROTO_KEY(clasp) (((clasp)->flags \ - >> JSCLASS_CACHED_PROTO_SHIFT) \ - & JSCLASS_CACHED_PROTO_MASK) - -/* Initializer for unused members of statically initialized JSClass structs. */ -#define JSCLASS_NO_OPTIONAL_MEMBERS 0,0,0,0,0,0,0,0 -#define JSCLASS_NO_RESERVED_MEMBERS 0,0,0,0,0 - -/* For detailed comments on these function pointer types, see jspubtd.h. */ -struct JSObjectOps { - /* Mandatory non-null function pointer members. */ - JSNewObjectMapOp newObjectMap; - JSObjectMapOp destroyObjectMap; - JSLookupPropOp lookupProperty; - JSDefinePropOp defineProperty; - JSPropertyIdOp getProperty; - JSPropertyIdOp setProperty; - JSAttributesOp getAttributes; - JSAttributesOp setAttributes; - JSPropertyIdOp deleteProperty; - JSConvertOp defaultValue; - JSNewEnumerateOp enumerate; - JSCheckAccessIdOp checkAccess; - - /* Optionally non-null members start here. */ - JSObjectOp thisObject; - JSPropertyRefOp dropProperty; - JSNative call; - JSNative construct; - JSXDRObjectOp xdrObject; - JSHasInstanceOp hasInstance; - JSSetObjectSlotOp setProto; - JSSetObjectSlotOp setParent; - JSMarkOp mark; - JSFinalizeOp clear; - JSGetRequiredSlotOp getRequiredSlot; - JSSetRequiredSlotOp setRequiredSlot; -}; - -struct JSXMLObjectOps { - JSObjectOps base; - JSGetMethodOp getMethod; - JSSetMethodOp setMethod; - JSEnumerateValuesOp enumerateValues; - JSEqualityOp equality; - JSConcatenateOp concatenate; -}; - -/* - * Classes that expose JSObjectOps via a non-null getObjectOps class hook may - * derive a property structure from this struct, return a pointer to it from - * lookupProperty and defineProperty, and use the pointer to avoid rehashing - * in getAttributes and setAttributes. - * - * The jsid type contains either an int jsval (see JSVAL_IS_INT above), or an - * internal pointer that is opaque to users of this API, but which users may - * convert from and to a jsval using JS_ValueToId and JS_IdToValue. - */ -struct JSProperty { - jsid id; -}; - -struct JSIdArray { - jsint length; - jsid vector[1]; /* actually, length jsid words */ -}; - -extern JS_PUBLIC_API(void) -JS_DestroyIdArray(JSContext *cx, JSIdArray *ida); - -extern JS_PUBLIC_API(JSBool) -JS_ValueToId(JSContext *cx, jsval v, jsid *idp); - -extern JS_PUBLIC_API(JSBool) -JS_IdToValue(JSContext *cx, jsid id, jsval *vp); - -/* - * The magic XML namespace id is int-tagged, but not a valid integer jsval. - * Global object classes in embeddings that enable JS_HAS_XML_SUPPORT (E4X) - * should handle this id specially before converting id via JSVAL_TO_INT. - */ -#define JS_DEFAULT_XML_NAMESPACE_ID ((jsid) JSVAL_VOID) - -/* - * JSNewResolveOp flag bits. - */ -#define JSRESOLVE_QUALIFIED 0x01 /* resolve a qualified property id */ -#define JSRESOLVE_ASSIGNING 0x02 /* resolve on the left of assignment */ -#define JSRESOLVE_DETECTING 0x04 /* 'if (o.p)...' or '(o.p) ?...:...' */ -#define JSRESOLVE_DECLARING 0x08 /* var, const, or function prolog op */ -#define JSRESOLVE_CLASSNAME 0x10 /* class name used when constructing */ - -extern JS_PUBLIC_API(JSBool) -JS_PropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_EnumerateStub(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_ResolveStub(JSContext *cx, JSObject *obj, jsval id); - -extern JS_PUBLIC_API(JSBool) -JS_ConvertStub(JSContext *cx, JSObject *obj, JSType type, jsval *vp); - -extern JS_PUBLIC_API(void) -JS_FinalizeStub(JSContext *cx, JSObject *obj); - -struct JSConstDoubleSpec { - jsdouble dval; - const char *name; - uint8 flags; - uint8 spare[3]; -}; - -/* - * To define an array element rather than a named property member, cast the - * element's index to (const char *) and initialize name with it, and set the - * JSPROP_INDEX bit in flags. - */ -struct JSPropertySpec { - const char *name; - int8 tinyid; - uint8 flags; - JSPropertyOp getter; - JSPropertyOp setter; -}; - -struct JSFunctionSpec { - const char *name; - JSNative call; -#ifdef MOZILLA_1_8_BRANCH - uint8 nargs; - uint8 flags; - uint16 extra; -#else - uint16 nargs; - uint16 flags; - uint32 extra; /* extra & 0xFFFF: - number of arg slots for local GC roots - extra >> 16: - reserved, must be zero */ -#endif -}; - -extern JS_PUBLIC_API(JSObject *) -JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, - JSClass *clasp, JSNative constructor, uintN nargs, - JSPropertySpec *ps, JSFunctionSpec *fs, - JSPropertySpec *static_ps, JSFunctionSpec *static_fs); - -#ifdef JS_THREADSAFE -extern JS_PUBLIC_API(JSClass *) -JS_GetClass(JSContext *cx, JSObject *obj); - -#define JS_GET_CLASS(cx,obj) JS_GetClass(cx, obj) -#else -extern JS_PUBLIC_API(JSClass *) -JS_GetClass(JSObject *obj); - -#define JS_GET_CLASS(cx,obj) JS_GetClass(obj) -#endif - -extern JS_PUBLIC_API(JSBool) -JS_InstanceOf(JSContext *cx, JSObject *obj, JSClass *clasp, jsval *argv); - -extern JS_PUBLIC_API(JSBool) -JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -extern JS_PUBLIC_API(void *) -JS_GetPrivate(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_SetPrivate(JSContext *cx, JSObject *obj, void *data); - -extern JS_PUBLIC_API(void *) -JS_GetInstancePrivate(JSContext *cx, JSObject *obj, JSClass *clasp, - jsval *argv); - -extern JS_PUBLIC_API(JSObject *) -JS_GetPrototype(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_SetPrototype(JSContext *cx, JSObject *obj, JSObject *proto); - -extern JS_PUBLIC_API(JSObject *) -JS_GetParent(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent); - -extern JS_PUBLIC_API(JSObject *) -JS_GetConstructor(JSContext *cx, JSObject *proto); - -/* - * Get a unique identifier for obj, good for the lifetime of obj (even if it - * is moved by a copying GC). Return false on failure (likely out of memory), - * and true with *idp containing the unique id on success. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetObjectId(JSContext *cx, JSObject *obj, jsid *idp); - -extern JS_PUBLIC_API(JSObject *) -JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); - -extern JS_PUBLIC_API(JSBool) -JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep); - -extern JS_PUBLIC_API(JSObject *) -JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent); - -extern JS_PUBLIC_API(JSObject *) -JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv); - -extern JS_PUBLIC_API(JSObject *) -JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, - JSObject *proto, uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_DefineConstDoubles(JSContext *cx, JSObject *obj, JSConstDoubleSpec *cds); - -extern JS_PUBLIC_API(JSBool) -JS_DefineProperties(JSContext *cx, JSObject *obj, JSPropertySpec *ps); - -extern JS_PUBLIC_API(JSBool) -JS_DefineProperty(JSContext *cx, JSObject *obj, const char *name, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs); - -/* - * Determine the attributes (JSPROP_* flags) of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and the value of *attrsp is undefined. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN *attrsp, JSBool *foundp); - -/* - * The same, but if the property is native, return its getter and setter via - * *getterp and *setterp, respectively (and only if the out parameter pointer - * is not null). - */ -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const char *name, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp); - -/* - * Set the attributes of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and nothing will be altered. - */ -extern JS_PUBLIC_API(JSBool) -JS_SetPropertyAttributes(JSContext *cx, JSObject *obj, const char *name, - uintN attrs, JSBool *foundp); - -extern JS_PUBLIC_API(JSBool) -JS_DefinePropertyWithTinyId(JSContext *cx, JSObject *obj, const char *name, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_AliasProperty(JSContext *cx, JSObject *obj, const char *name, - const char *alias); - -extern JS_PUBLIC_API(JSBool) -JS_HasProperty(JSContext *cx, JSObject *obj, const char *name, JSBool *foundp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, const char *name, - uintN flags, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetMethodById(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetMethod(JSContext *cx, JSObject *obj, const char *name, JSObject **objp, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetProperty(JSContext *cx, JSObject *obj, const char *name, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteProperty(JSContext *cx, JSObject *obj, const char *name); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteProperty2(JSContext *cx, JSObject *obj, const char *name, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_DefineUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs); - -/* - * Determine the attributes (JSPROP_* flags) of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and the value of *attrsp is undefined. - */ -extern JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp); - -/* - * The same, but if the property is native, return its getter and setter via - * *getterp and *setterp, respectively (and only if the out parameter pointer - * is not null). - */ -extern JS_PUBLIC_API(JSBool) -JS_GetUCPropertyAttrsGetterAndSetter(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN *attrsp, JSBool *foundp, - JSPropertyOp *getterp, - JSPropertyOp *setterp); - -/* - * Set the attributes of a property on a given object. - * - * If the object does not have a property by that name, *foundp will be - * JS_FALSE and nothing will be altered. - */ -extern JS_PUBLIC_API(JSBool) -JS_SetUCPropertyAttributes(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - uintN attrs, JSBool *foundp); - - -extern JS_PUBLIC_API(JSBool) -JS_DefineUCPropertyWithTinyId(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - int8 tinyid, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_HasUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - JSBool *vp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetUCProperty(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteUCProperty2(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, - jsval *rval); - -extern JS_PUBLIC_API(JSObject *) -JS_NewArrayObject(JSContext *cx, jsint length, jsval *vector); - -extern JS_PUBLIC_API(JSBool) -JS_IsArrayObject(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); - -extern JS_PUBLIC_API(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); - -extern JS_PUBLIC_API(JSBool) -JS_HasArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); - -extern JS_PUBLIC_API(JSBool) -JS_DefineElement(JSContext *cx, JSObject *obj, jsint index, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs); - -extern JS_PUBLIC_API(JSBool) -JS_AliasElement(JSContext *cx, JSObject *obj, const char *name, jsint alias); - -extern JS_PUBLIC_API(JSBool) -JS_HasElement(JSContext *cx, JSObject *obj, jsint index, JSBool *foundp); - -extern JS_PUBLIC_API(JSBool) -JS_LookupElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_GetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetElement(JSContext *cx, JSObject *obj, jsint index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteElement(JSContext *cx, JSObject *obj, jsint index); - -extern JS_PUBLIC_API(JSBool) -JS_DeleteElement2(JSContext *cx, JSObject *obj, jsint index, jsval *rval); - -extern JS_PUBLIC_API(void) -JS_ClearScope(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSIdArray *) -JS_Enumerate(JSContext *cx, JSObject *obj); - -/* - * Create an object to iterate over enumerable properties of obj, in arbitrary - * property definition order. NB: This differs from longstanding for..in loop - * order, which uses order of property definition in obj. - */ -extern JS_PUBLIC_API(JSObject *) -JS_NewPropertyIterator(JSContext *cx, JSObject *obj); - -/* - * Return true on success with *idp containing the id of the next enumerable - * property to visit using iterobj, or JSVAL_VOID if there is no such property - * left to visit. Return false on error. - */ -extern JS_PUBLIC_API(JSBool) -JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp); - -extern JS_PUBLIC_API(JSBool) -JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp); - -extern JS_PUBLIC_API(JSCheckAccessOp) -JS_SetCheckObjectAccessCallback(JSRuntime *rt, JSCheckAccessOp acb); - -extern JS_PUBLIC_API(JSBool) -JS_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v); - -/************************************************************************/ - -/* - * Security protocol. - */ -struct JSPrincipals { - char *codebase; - - /* XXX unspecified and unused by Mozilla code -- can we remove these? */ - void * (* JS_DLL_CALLBACK getPrincipalArray)(JSContext *cx, JSPrincipals *); - JSBool (* JS_DLL_CALLBACK globalPrivilegesEnabled)(JSContext *cx, JSPrincipals *); - - /* Don't call "destroy"; use reference counting macros below. */ - jsrefcount refcount; - - void (* JS_DLL_CALLBACK destroy)(JSContext *cx, JSPrincipals *); - JSBool (* JS_DLL_CALLBACK subsume)(JSPrincipals *, JSPrincipals *); -}; - -#ifdef JS_THREADSAFE -#define JSPRINCIPALS_HOLD(cx, principals) JS_HoldPrincipals(cx,principals) -#define JSPRINCIPALS_DROP(cx, principals) JS_DropPrincipals(cx,principals) - -extern JS_PUBLIC_API(jsrefcount) -JS_HoldPrincipals(JSContext *cx, JSPrincipals *principals); - -extern JS_PUBLIC_API(jsrefcount) -JS_DropPrincipals(JSContext *cx, JSPrincipals *principals); - -#else -#define JSPRINCIPALS_HOLD(cx, principals) (++(principals)->refcount) -#define JSPRINCIPALS_DROP(cx, principals) \ - ((--(principals)->refcount == 0) \ - ? ((*(principals)->destroy)((cx), (principals)), 0) \ - : (principals)->refcount) -#endif - -extern JS_PUBLIC_API(JSPrincipalsTranscoder) -JS_SetPrincipalsTranscoder(JSRuntime *rt, JSPrincipalsTranscoder px); - -extern JS_PUBLIC_API(JSObjectPrincipalsFinder) -JS_SetObjectPrincipalsFinder(JSRuntime *rt, JSObjectPrincipalsFinder fop); - -/************************************************************************/ - -/* - * Functions and scripts. - */ -extern JS_PUBLIC_API(JSFunction *) -JS_NewFunction(JSContext *cx, JSNative call, uintN nargs, uintN flags, - JSObject *parent, const char *name); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFunctionObject(JSFunction *fun); - -/* - * Deprecated, useful only for diagnostics. Use JS_GetFunctionId instead for - * anonymous vs. "anonymous" disambiguation and Unicode fidelity. - */ -extern JS_PUBLIC_API(const char *) -JS_GetFunctionName(JSFunction *fun); - -/* - * Return the function's identifier as a JSString, or null if fun is unnamed. - * The returned string lives as long as fun, so you don't need to root a saved - * reference to it if fun is well-connected or rooted, and provided you bound - * the use of the saved reference by fun's lifetime. - * - * Prefer JS_GetFunctionId over JS_GetFunctionName because it returns null for - * truly anonymous functions, and because it doesn't chop to ISO-Latin-1 chars - * from UTF-16-ish jschars. - */ -extern JS_PUBLIC_API(JSString *) -JS_GetFunctionId(JSFunction *fun); - -/* - * Return JSFUN_* flags for fun. - */ -extern JS_PUBLIC_API(uintN) -JS_GetFunctionFlags(JSFunction *fun); - -/* - * Return the arity (length) of fun. - */ -extern JS_PUBLIC_API(uint16) -JS_GetFunctionArity(JSFunction *fun); - -/* - * Infallible predicate to test whether obj is a function object (faster than - * comparing obj's class name to "Function", but equivalent unless someone has - * overwritten the "Function" identifier with a different constructor and then - * created instances using that constructor that might be passed in as obj). - */ -extern JS_PUBLIC_API(JSBool) -JS_ObjectIsFunction(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_DefineFunctions(JSContext *cx, JSObject *obj, JSFunctionSpec *fs); - -extern JS_PUBLIC_API(JSFunction *) -JS_DefineFunction(JSContext *cx, JSObject *obj, const char *name, JSNative call, - uintN nargs, uintN attrs); - -extern JS_PUBLIC_API(JSFunction *) -JS_DefineUCFunction(JSContext *cx, JSObject *obj, - const jschar *name, size_t namelen, JSNative call, - uintN nargs, uintN attrs); - -extern JS_PUBLIC_API(JSObject *) -JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); - -/* - * Given a buffer, return JS_FALSE if the buffer might become a valid - * javascript statement with the addition of more lines. Otherwise return - * JS_TRUE. The intent is to support interactive compilation - accumulate - * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to - * the compiler. - */ -extern JS_PUBLIC_API(JSBool) -JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, - const char *bytes, size_t length); - -/* - * The JSScript objects returned by the following functions refer to string and - * other kinds of literals, including doubles and RegExp objects. These - * literals are vulnerable to garbage collection; to root script objects and - * prevent literals from being collected, create a rootable object using - * JS_NewScriptObject, and root the resulting object using JS_Add[Named]Root. - */ -extern JS_PUBLIC_API(JSScript *) -JS_CompileScript(JSContext *cx, JSObject *obj, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileFileHandle(JSContext *cx, JSObject *obj, const char *filename, - FILE *fh); - -extern JS_PUBLIC_API(JSScript *) -JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, - const char *filename, FILE *fh, - JSPrincipals *principals); - -/* - * NB: you must use JS_NewScriptObject and root a pointer to its return value - * in order to keep a JSScript and its atoms safe from garbage collection after - * creating the script via JS_Compile* and before a JS_ExecuteScript* call. - * E.g., and without error checks: - * - * JSScript *script = JS_CompileFile(cx, global, filename); - * JSObject *scrobj = JS_NewScriptObject(cx, script); - * JS_AddNamedRoot(cx, &scrobj, "scrobj"); - * do { - * jsval result; - * JS_ExecuteScript(cx, global, script, &result); - * JS_GC(); - * } while (!JSVAL_IS_BOOLEAN(result) || JSVAL_TO_BOOLEAN(result)); - * JS_RemoveRoot(cx, &scrobj); - */ -extern JS_PUBLIC_API(JSObject *) -JS_NewScriptObject(JSContext *cx, JSScript *script); - -/* - * Infallible getter for a script's object. If JS_NewScriptObject has not been - * called on script yet, the return value will be null. - */ -extern JS_PUBLIC_API(JSObject *) -JS_GetScriptObject(JSScript *script); - -extern JS_PUBLIC_API(void) -JS_DestroyScript(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const char *bytes, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunction(JSContext *cx, JSObject *obj, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSFunction *) -JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, const char *name, - uintN nargs, const char **argnames, - const jschar *chars, size_t length, - const char *filename, uintN lineno); - -extern JS_PUBLIC_API(JSString *) -JS_DecompileScript(JSContext *cx, JSScript *script, const char *name, - uintN indent); - -/* - * API extension: OR this into indent to avoid pretty-printing the decompiled - * source resulting from JS_DecompileFunction{,Body}. - */ -#define JS_DONT_PRETTY_PRINT ((uintN)0x8000) - -extern JS_PUBLIC_API(JSString *) -JS_DecompileFunction(JSContext *cx, JSFunction *fun, uintN indent); - -extern JS_PUBLIC_API(JSString *) -JS_DecompileFunctionBody(JSContext *cx, JSFunction *fun, uintN indent); - -/* - * NB: JS_ExecuteScript, JS_ExecuteScriptPart, and the JS_Evaluate*Script* - * quadruplets all use the obj parameter as the initial scope chain header, - * the 'this' keyword value, and the variables object (ECMA parlance for where - * 'var' and 'function' bind names) of the execution context for script. - * - * Using obj as the variables object is problematic if obj's parent (which is - * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in - * this case, variables created by 'var x = 0', e.g., go in obj, but variables - * created by assignment to an unbound id, 'x = 0', go in the last object on - * the scope chain linked by parent. - * - * ECMA calls that last scoping object the "global object", but note that many - * embeddings have several such objects. ECMA requires that "global code" be - * executed with the variables object equal to this global object. But these - * JS API entry points provide freedom to execute code against a "sub-global", - * i.e., a parented or scoped object, in which case the variables object will - * differ from the last object on the scope chain, resulting in confusing and - * non-ECMA explicit vs. implicit variable creation. - * - * Caveat embedders: unless you already depend on this buggy variables object - * binding behavior, you should call JS_SetOptions(cx, JSOPTION_VAROBJFIX) or - * JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_VAROBJFIX) -- the latter if - * someone may have set other options on cx already -- for each context in the - * application, if you pass parented objects as the obj parameter, or may ever - * pass such objects in the future. - * - * Why a runtime option? The alternative is to add six or so new API entry - * points with signatures matching the following six, and that doesn't seem - * worth the code bloat cost. Such new entry points would probably have less - * obvious names, too, so would not tend to be used. The JS_SetOption call, - * OTOH, can be more easily hacked into existing code that does not depend on - * the bug; such code can continue to use the familiar JS_EvaluateScript, - * etc., entry points. - */ -extern JS_PUBLIC_API(JSBool) -JS_ExecuteScript(JSContext *cx, JSObject *obj, JSScript *script, jsval *rval); - -/* - * Execute either the function-defining prolog of a script, or the script's - * main body, but not both. - */ -typedef enum JSExecPart { JSEXEC_PROLOG, JSEXEC_MAIN } JSExecPart; - -extern JS_PUBLIC_API(JSBool) -JS_ExecuteScriptPart(JSContext *cx, JSObject *obj, JSScript *script, - JSExecPart part, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateScript(JSContext *cx, JSObject *obj, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCScript(JSContext *cx, JSObject *obj, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, - JSPrincipals *principals, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_CallFunction(JSContext *cx, JSObject *obj, JSFunction *fun, uintN argc, - jsval *argv, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_CallFunctionName(JSContext *cx, JSObject *obj, const char *name, uintN argc, - jsval *argv, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, - jsval *argv, jsval *rval); - -extern JS_PUBLIC_API(JSBranchCallback) -JS_SetBranchCallback(JSContext *cx, JSBranchCallback cb); - -extern JS_PUBLIC_API(JSBool) -JS_IsRunning(JSContext *cx); - -extern JS_PUBLIC_API(JSBool) -JS_IsConstructing(JSContext *cx); - -/* - * Returns true if a script is executing and its current bytecode is a set - * (assignment) operation, even if there are native (no script) stack frames - * between the script and the caller to JS_IsAssigning. - */ -extern JS_FRIEND_API(JSBool) -JS_IsAssigning(JSContext *cx); - -/* - * Set the second return value, which should be a string or int jsval that - * identifies a property in the returned object, to form an ECMA reference - * type value (obj, id). Only native methods can return reference types, - * and if the returned value is used on the left-hand side of an assignment - * op, the identified property will be set. If the return value is in an - * r-value, the interpreter just gets obj[id]'s value. - */ -extern JS_PUBLIC_API(void) -JS_SetCallReturnValue2(JSContext *cx, jsval v); - -/* - * Saving and restoring frame chains. - * - * These two functions are used to set aside cx->fp while that frame is - * inactive. After a call to JS_SaveFrameChain, it looks as if there is no - * code running on cx. Before calling JS_RestoreFrameChain, cx's call stack - * must be balanced and all nested calls to JS_SaveFrameChain must have had - * matching JS_RestoreFrameChain calls. - * - * JS_SaveFrameChain deals with cx not having any code running on it. A null - * return does not signify an error and JS_RestoreFrameChain handles null - * frames. - */ -extern JS_PUBLIC_API(JSStackFrame *) -JS_SaveFrameChain(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp); - -/************************************************************************/ - -/* - * Strings. - * - * NB: JS_NewString takes ownership of bytes on success, avoiding a copy; but - * on error (signified by null return), it leaves bytes owned by the caller. - * So the caller must free bytes in the error case, if it has no use for them. - * In contrast, all the JS_New*StringCopy* functions do not take ownership of - * the character memory passed to them -- they copy it. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewString(JSContext *cx, char *bytes, size_t length); - -extern JS_PUBLIC_API(JSString *) -JS_NewStringCopyN(JSContext *cx, const char *s, size_t n); - -extern JS_PUBLIC_API(JSString *) -JS_NewStringCopyZ(JSContext *cx, const char *s); - -extern JS_PUBLIC_API(JSString *) -JS_InternString(JSContext *cx, const char *s); - -extern JS_PUBLIC_API(JSString *) -JS_NewUCString(JSContext *cx, jschar *chars, size_t length); - -extern JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyN(JSContext *cx, const jschar *s, size_t n); - -extern JS_PUBLIC_API(JSString *) -JS_NewUCStringCopyZ(JSContext *cx, const jschar *s); - -extern JS_PUBLIC_API(JSString *) -JS_InternUCStringN(JSContext *cx, const jschar *s, size_t length); - -extern JS_PUBLIC_API(JSString *) -JS_InternUCString(JSContext *cx, const jschar *s); - -extern JS_PUBLIC_API(char *) -JS_GetStringBytes(JSString *str); - -extern JS_PUBLIC_API(jschar *) -JS_GetStringChars(JSString *str); - -extern JS_PUBLIC_API(size_t) -JS_GetStringLength(JSString *str); - -extern JS_PUBLIC_API(intN) -JS_CompareStrings(JSString *str1, JSString *str2); - -/* - * Mutable string support. A string's characters are never mutable in this JS - * implementation, but a growable string has a buffer that can be reallocated, - * and a dependent string is a substring of another (growable, dependent, or - * immutable) string. The direct data members of the (opaque to API clients) - * JSString struct may be changed in a single-threaded way for growable and - * dependent strings. - * - * Therefore mutable strings cannot be used by more than one thread at a time. - * You may call JS_MakeStringImmutable to convert the string from a mutable - * (growable or dependent) string to an immutable (and therefore thread-safe) - * string. The engine takes care of converting growable and dependent strings - * to immutable for you if you store strings in multi-threaded objects using - * JS_SetProperty or kindred API entry points. - * - * If you store a JSString pointer in a native data structure that is (safely) - * accessible to multiple threads, you must call JS_MakeStringImmutable before - * retiring the store. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewGrowableString(JSContext *cx, jschar *chars, size_t length); - -/* - * Create a dependent string, i.e., a string that owns no character storage, - * but that refers to a slice of another string's chars. Dependent strings - * are mutable by definition, so the thread safety comments above apply. - */ -extern JS_PUBLIC_API(JSString *) -JS_NewDependentString(JSContext *cx, JSString *str, size_t start, - size_t length); - -/* - * Concatenate two strings, resulting in a new growable string. If you create - * the left string and pass it to JS_ConcatStrings on a single thread, try to - * use JS_NewGrowableString to create the left string -- doing so helps Concat - * avoid allocating a new buffer for the result and copying left's chars into - * the new buffer. See above for thread safety comments. - */ -extern JS_PUBLIC_API(JSString *) -JS_ConcatStrings(JSContext *cx, JSString *left, JSString *right); - -/* - * Convert a dependent string into an independent one. This function does not - * change the string's mutability, so the thread safety comments above apply. - */ -extern JS_PUBLIC_API(const jschar *) -JS_UndependString(JSContext *cx, JSString *str); - -/* - * Convert a mutable string (either growable or dependent) into an immutable, - * thread-safe one. - */ -extern JS_PUBLIC_API(JSBool) -JS_MakeStringImmutable(JSContext *cx, JSString *str); - -/* - * Return JS_TRUE if C (char []) strings passed via the API and internally - * are UTF-8. The source must be compiled with JS_C_STRINGS_ARE_UTF8 defined - * to get UTF-8 support. - */ -JS_PUBLIC_API(JSBool) -JS_CStringsAreUTF8(); - -/* - * Character encoding support. - * - * For both JS_EncodeCharacters and JS_DecodeBytes, set *dstlenp to the size - * of the destination buffer before the call; on return, *dstlenp contains the - * number of bytes (JS_EncodeCharacters) or jschars (JS_DecodeBytes) actually - * stored. To determine the necessary destination buffer size, make a sizing - * call that passes NULL for dst. - * - * On errors, the functions report the error. In that case, *dstlenp contains - * the number of characters or bytes transferred so far. If cx is NULL, no - * error is reported on failure, and the functions simply return JS_FALSE. - * - * NB: Neither function stores an additional zero byte or jschar after the - * transcoded string. - * - * If the source has been compiled with the #define JS_C_STRINGS_ARE_UTF8 to - * enable UTF-8 interpretation of C char[] strings, then JS_EncodeCharacters - * encodes to UTF-8, and JS_DecodeBytes decodes from UTF-8, which may create - * addititional errors if the character sequence is malformed. If UTF-8 - * support is disabled, the functions deflate and inflate, respectively. - */ -JS_PUBLIC_API(JSBool) -JS_EncodeCharacters(JSContext *cx, const jschar *src, size_t srclen, char *dst, - size_t *dstlenp); - -JS_PUBLIC_API(JSBool) -JS_DecodeBytes(JSContext *cx, const char *src, size_t srclen, jschar *dst, - size_t *dstlenp); - -/************************************************************************/ - -/* - * Locale specific string conversion and error message callbacks. - */ -struct JSLocaleCallbacks { - JSLocaleToUpperCase localeToUpperCase; - JSLocaleToLowerCase localeToLowerCase; - JSLocaleCompare localeCompare; - JSLocaleToUnicode localeToUnicode; - JSErrorCallback localeGetErrorMessage; -}; - -/* - * Establish locale callbacks. The pointer must persist as long as the - * JSContext. Passing NULL restores the default behaviour. - */ -extern JS_PUBLIC_API(void) -JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks); - -/* - * Return the address of the current locale callbacks struct, which may - * be NULL. - */ -extern JS_PUBLIC_API(JSLocaleCallbacks *) -JS_GetLocaleCallbacks(JSContext *cx); - -/************************************************************************/ - -/* - * Error reporting. - */ - -/* - * Report an exception represented by the sprintf-like conversion of format - * and its arguments. This exception message string is passed to a pre-set - * JSErrorReporter function (set by JS_SetErrorReporter; see jspubtd.h for - * the JSErrorReporter typedef). - */ -extern JS_PUBLIC_API(void) -JS_ReportError(JSContext *cx, const char *format, ...); - -/* - * Use an errorNumber to retrieve the format string, args are char * - */ -extern JS_PUBLIC_API(void) -JS_ReportErrorNumber(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...); - -/* - * Use an errorNumber to retrieve the format string, args are jschar * - */ -extern JS_PUBLIC_API(void) -JS_ReportErrorNumberUC(JSContext *cx, JSErrorCallback errorCallback, - void *userRef, const uintN errorNumber, ...); - -/* - * As above, but report a warning instead (JSREPORT_IS_WARNING(report.flags)). - * Return true if there was no error trying to issue the warning, and if the - * warning was not converted into an error due to the JSOPTION_WERROR option - * being set, false otherwise. - */ -extern JS_PUBLIC_API(JSBool) -JS_ReportWarning(JSContext *cx, const char *format, ...); - -extern JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumber(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...); - -extern JS_PUBLIC_API(JSBool) -JS_ReportErrorFlagsAndNumberUC(JSContext *cx, uintN flags, - JSErrorCallback errorCallback, void *userRef, - const uintN errorNumber, ...); - -/* - * Complain when out of memory. - */ -extern JS_PUBLIC_API(void) -JS_ReportOutOfMemory(JSContext *cx); - -struct JSErrorReport { - const char *filename; /* source file name, URL, etc., or null */ - uintN lineno; /* source line number */ - const char *linebuf; /* offending source line without final \n */ - const char *tokenptr; /* pointer to error token in linebuf */ - const jschar *uclinebuf; /* unicode (original) line buffer */ - const jschar *uctokenptr; /* unicode (original) token pointer */ - uintN flags; /* error/warning, etc. */ - uintN errorNumber; /* the error number, e.g. see js.msg */ - const jschar *ucmessage; /* the (default) error message */ - const jschar **messageArgs; /* arguments for the error message */ -}; - -/* - * JSErrorReport flag values. These may be freely composed. - */ -#define JSREPORT_ERROR 0x0 /* pseudo-flag for default case */ -#define JSREPORT_WARNING 0x1 /* reported via JS_ReportWarning */ -#define JSREPORT_EXCEPTION 0x2 /* exception was thrown */ -#define JSREPORT_STRICT 0x4 /* error or warning due to strict option */ - -/* - * If JSREPORT_EXCEPTION is set, then a JavaScript-catchable exception - * has been thrown for this runtime error, and the host should ignore it. - * Exception-aware hosts should also check for JS_IsExceptionPending if - * JS_ExecuteScript returns failure, and signal or propagate the exception, as - * appropriate. - */ -#define JSREPORT_IS_WARNING(flags) (((flags) & JSREPORT_WARNING) != 0) -#define JSREPORT_IS_EXCEPTION(flags) (((flags) & JSREPORT_EXCEPTION) != 0) -#define JSREPORT_IS_STRICT(flags) (((flags) & JSREPORT_STRICT) != 0) - -extern JS_PUBLIC_API(JSErrorReporter) -JS_SetErrorReporter(JSContext *cx, JSErrorReporter er); - -/************************************************************************/ - -/* - * Regular Expressions. - */ -#define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ -#define JSREG_GLOB 0x02 /* global exec, creates array of matches */ -#define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ - -extern JS_PUBLIC_API(JSObject *) -JS_NewRegExpObject(JSContext *cx, char *bytes, size_t length, uintN flags); - -extern JS_PUBLIC_API(JSObject *) -JS_NewUCRegExpObject(JSContext *cx, jschar *chars, size_t length, uintN flags); - -extern JS_PUBLIC_API(void) -JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline); - -extern JS_PUBLIC_API(void) -JS_ClearRegExpStatics(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_ClearRegExpRoots(JSContext *cx); - -/* TODO: compile, exec, get/set other statics... */ - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_IsExceptionPending(JSContext *cx); - -extern JS_PUBLIC_API(JSBool) -JS_GetPendingException(JSContext *cx, jsval *vp); - -extern JS_PUBLIC_API(void) -JS_SetPendingException(JSContext *cx, jsval v); - -extern JS_PUBLIC_API(void) -JS_ClearPendingException(JSContext *cx); - -extern JS_PUBLIC_API(JSBool) -JS_ReportPendingException(JSContext *cx); - -/* - * Save the current exception state. This takes a snapshot of cx's current - * exception state without making any change to that state. - * - * The returned state pointer MUST be passed later to JS_RestoreExceptionState - * (to restore that saved state, overriding any more recent state) or else to - * JS_DropExceptionState (to free the state struct in case it is not correct - * or desirable to restore it). Both Restore and Drop free the state struct, - * so callers must stop using the pointer returned from Save after calling the - * Release or Drop API. - */ -extern JS_PUBLIC_API(JSExceptionState *) -JS_SaveExceptionState(JSContext *cx); - -extern JS_PUBLIC_API(void) -JS_RestoreExceptionState(JSContext *cx, JSExceptionState *state); - -extern JS_PUBLIC_API(void) -JS_DropExceptionState(JSContext *cx, JSExceptionState *state); - -/* - * If the given value is an exception object that originated from an error, - * the exception will contain an error report struct, and this API will return - * the address of that struct. Otherwise, it returns NULL. The lifetime of - * the error report struct that might be returned is the same as the lifetime - * of the exception object. - */ -extern JS_PUBLIC_API(JSErrorReport *) -JS_ErrorFromException(JSContext *cx, jsval v); - -/* - * Given a reported error's message and JSErrorReport struct pointer, throw - * the corresponding exception on cx. - */ -extern JS_PUBLIC_API(JSBool) -JS_ThrowReportedError(JSContext *cx, const char *message, - JSErrorReport *reportp); - -#ifdef JS_THREADSAFE - -/* - * Associate the current thread with the given context. This is done - * implicitly by JS_NewContext. - * - * Returns the old thread id for this context, which should be treated as - * an opaque value. This value is provided for comparison to 0, which - * indicates that ClearContextThread has been called on this context - * since the last SetContextThread, or non-0, which indicates the opposite. - */ -extern JS_PUBLIC_API(jsword) -JS_GetContextThread(JSContext *cx); - -extern JS_PUBLIC_API(jsword) -JS_SetContextThread(JSContext *cx); - -extern JS_PUBLIC_API(jsword) -JS_ClearContextThread(JSContext *cx); - -#endif /* JS_THREADSAFE */ - -/************************************************************************/ - -JS_END_EXTERN_C - -#endif /* jsapi_h___ */ diff --git a/spidermonkey/libjs/jsarena.c b/spidermonkey/libjs/jsarena.c deleted file mode 100644 index ef6ccd1..0000000 --- a/spidermonkey/libjs/jsarena.c +++ /dev/null @@ -1,502 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ - -#ifdef JS_ARENAMETER -static JSArenaStats *arena_stats_list; - -#define COUNT(pool,what) (pool)->stats.what++ -#else -#define COUNT(pool,what) /* nothing */ -#endif - -#define JS_ARENA_DEFAULT_ALIGN sizeof(double) - -JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align) -{ - if (align == 0) - align = JS_ARENA_DEFAULT_ALIGN; - pool->mask = JS_BITMASK(JS_CeilingLog2(align)); - pool->first.next = NULL; - pool->first.base = pool->first.avail = pool->first.limit = - JS_ARENA_ALIGN(pool, &pool->first + 1); - pool->current = &pool->first; - pool->arenasize = size; -#ifdef JS_ARENAMETER - memset(&pool->stats, 0, sizeof pool->stats); - pool->stats.name = strdup(name); - pool->stats.next = arena_stats_list; - arena_stats_list = &pool->stats; -#endif -} - -/* - * An allocation that consumes more than pool->arenasize also has a header - * pointing back to its previous arena's next member. This header is not - * included in [a->base, a->limit), so its space can't be wrongly claimed. - * - * As the header is a pointer, it must be well-aligned. If pool->mask is - * greater than or equal to POINTER_MASK, the header just preceding a->base - * for an oversized arena a is well-aligned, because a->base is well-aligned. - * However, we may need to add more space to pad the JSArena ** back-pointer - * so that it lies just behind a->base, because a might not be aligned such - * that (jsuword)(a + 1) is on a pointer boundary. - * - * By how much must we pad? Let M be the alignment modulus for pool and P - * the modulus for a pointer. Given M >= P, the base of an oversized arena - * that satisfies M is well-aligned for P. - * - * On the other hand, if M < P, we must include enough space in the header - * size to align the back-pointer on a P boundary so that it can be found by - * subtracting P from a->base. This means a->base must be on a P boundary, - * even though subsequent allocations from a may be aligned on a lesser (M) - * boundary. Given powers of two M and P as above, the extra space needed - * when M < P is P-M or POINTER_MASK - pool->mask. - * - * The size of a header including padding is given by the HEADER_SIZE macro, - * below, for any pool (for any value of M). - * - * The mask to align a->base for any pool is (pool->mask | POINTER_MASK), or - * HEADER_BASE_MASK(pool). - * - * PTR_TO_HEADER computes the address of the back-pointer, given an oversized - * allocation at p. By definition, p must be a->base for the arena a that - * contains p. GET_HEADER and SET_HEADER operate on an oversized arena a, in - * the case of SET_HEADER with back-pointer ap. - */ -#define POINTER_MASK ((jsuword)(JS_ALIGN_OF_POINTER - 1)) -#define HEADER_SIZE(pool) (sizeof(JSArena **) \ - + (((pool)->mask < POINTER_MASK) \ - ? POINTER_MASK - (pool)->mask \ - : 0)) -#define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK) -#define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \ - & HEADER_BASE_MASK(pool)) \ - == 0), \ - (JSArena ***)(p) - 1) -#define GET_HEADER(pool,a) (*PTR_TO_HEADER(pool, (a)->base)) -#define SET_HEADER(pool,a,ap) (*PTR_TO_HEADER(pool, (a)->base) = (ap)) - -JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb) -{ - JSArena **ap, *a, *b; - jsuword extra, hdrsz, gross; - void *p; - - /* - * Search pool from current forward till we find or make enough space. - * - * NB: subtract nb from a->limit in the loop condition, instead of adding - * nb to a->avail, to avoid overflowing a 32-bit address space (possible - * when running a 32-bit program on a 64-bit system where the kernel maps - * the heap up against the top of the 32-bit address space). - * - * Thanks to Juergen Kreileder , who brought this up in - * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. - */ - JS_ASSERT((nb & pool->mask) == 0); - for (a = pool->current; nb > a->limit || a->avail > a->limit - nb; - pool->current = a) { - ap = &a->next; - if (!*ap) { - /* Not enough space in pool, so we must malloc. */ - extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0; - hdrsz = sizeof *a + extra + pool->mask; - gross = hdrsz + JS_MAX(nb, pool->arenasize); - if (gross < nb) - return NULL; - b = (JSArena *) malloc(gross); - if (!b) - return NULL; - b->next = NULL; - b->limit = (jsuword)b + gross; - JS_COUNT_ARENA(pool,++); - COUNT(pool, nmallocs); - - /* If oversized, store ap in the header, just before a->base. */ - *ap = a = b; - JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a)); - if (extra) { - a->base = a->avail = - ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); - SET_HEADER(pool, a, ap); - } else { - a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1); - } - continue; - } - a = *ap; /* move to next arena */ - } - - p = (void *)a->avail; - a->avail += nb; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - return p; -} - -JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - JSArena **ap, *a, *b; - jsuword boff, aoff, extra, hdrsz, gross; - - /* - * Use the oversized-single-allocation header to avoid searching for ap. - * See JS_ArenaAllocate, the SET_HEADER call. - */ - if (size > pool->arenasize) { - ap = *PTR_TO_HEADER(pool, p); - a = *ap; - } else { - ap = &pool->first.next; - while ((a = *ap) != pool->current) - ap = &a->next; - } - - JS_ASSERT(a->base == (jsuword)p); - boff = JS_UPTRDIFF(a->base, a); - aoff = JS_ARENA_ALIGN(pool, size + incr); - JS_ASSERT(aoff > pool->arenasize); - extra = HEADER_SIZE(pool); /* oversized header holds ap */ - hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */ - gross = hdrsz + aoff; - JS_ASSERT(gross > aoff); - a = (JSArena *) realloc(a, gross); - if (!a) - return NULL; -#ifdef JS_ARENAMETER - pool->stats.nreallocs++; -#endif - - if (a != *ap) { - /* Oops, realloc moved the allocation: update other pointers to a. */ - if (pool->current == *ap) - pool->current = a; - b = a->next; - if (b && b->avail - b->base > pool->arenasize) { - JS_ASSERT(GET_HEADER(pool, b) == &(*ap)->next); - SET_HEADER(pool, b, &a->next); - } - - /* Now update *ap, the next link of the arena before a. */ - *ap = a; - } - - a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool); - a->limit = (jsuword)a + gross; - a->avail = a->base + aoff; - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - /* Check whether realloc aligned differently, and copy if necessary. */ - if (boff != JS_UPTRDIFF(a->base, a)) - memmove((void *)a->base, (char *)a + boff, size); - - /* Store ap in the oversized-load arena header. */ - SET_HEADER(pool, a, ap); - return (void *)a->base; -} - -JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr) -{ - void *newp; - - /* - * If p points to an oversized allocation, it owns an entire arena, so we - * can simply realloc the arena. - */ - if (size > pool->arenasize) - return JS_ArenaRealloc(pool, p, size, incr); - - JS_ARENA_ALLOCATE(newp, pool, size + incr); - if (newp) - memcpy(newp, p, size); - return newp; -} - -/* - * Free tail arenas linked after head, which may not be the true list head. - * Reset pool->current to point to head in case it pointed at a tail arena. - */ -static void -FreeArenaList(JSArenaPool *pool, JSArena *head) -{ - JSArena **ap, *a; - - ap = &head->next; - a = *ap; - if (!a) - return; - -#ifdef DEBUG - do { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - a->avail = a->base; - JS_CLEAR_UNUSED(a); - } while ((a = a->next) != NULL); - a = *ap; -#endif - - do { - *ap = a->next; - JS_CLEAR_ARENA(a); - JS_COUNT_ARENA(pool,--); - free(a); - } while ((a = *ap) != NULL); - - pool->current = head; -} - -JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark) -{ - JSArena *a; - - for (a = &pool->first; a; a = a->next) { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - if (JS_UPTRDIFF(mark, a->base) <= JS_UPTRDIFF(a->avail, a->base)) { - a->avail = JS_ARENA_ALIGN(pool, mark); - JS_ASSERT(a->avail <= a->limit); - FreeArenaList(pool, a); - return; - } - } -} - -JS_PUBLIC_API(void) -JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size) -{ - JSArena **ap, *a, *b; - jsuword q; - - /* - * If the allocation is oversized, it consumes an entire arena, and it has - * a header just before the allocation pointing back to its predecessor's - * next member. Otherwise, we have to search pool for a. - */ - if (size > pool->arenasize) { - ap = *PTR_TO_HEADER(pool, p); - a = *ap; - } else { - q = (jsuword)p + size; - q = JS_ARENA_ALIGN(pool, q); - ap = &pool->first.next; - while ((a = *ap) != NULL) { - JS_ASSERT(a->base <= a->avail && a->avail <= a->limit); - - if (a->avail == q) { - /* - * If a is consumed by the allocation at p, we can free it to - * the malloc heap. - */ - if (a->base == (jsuword)p) - break; - - /* - * We can't free a, but we can "retract" its avail cursor -- - * whether there are others after it in pool. - */ - a->avail = (jsuword)p; - return; - } - ap = &a->next; - } - } - - /* - * At this point, a is doomed, so ensure that pool->current doesn't point - * at it. We must preserve LIFO order of mark/release cursors, so we use - * the oversized-allocation arena's back pointer (or if not oversized, we - * use the result of searching the entire pool) to compute the address of - * the arena that precedes a. - */ - if (pool->current == a) - pool->current = (JSArena *) ((char *)ap - offsetof(JSArena, next)); - - /* - * This is a non-LIFO deallocation, so take care to fix up a->next's back - * pointer in its header, if a->next is oversized. - */ - *ap = b = a->next; - if (b && b->avail - b->base > pool->arenasize) { - JS_ASSERT(GET_HEADER(pool, b) == &a->next); - SET_HEADER(pool, b, ap); - } - JS_CLEAR_ARENA(a); - JS_COUNT_ARENA(pool,--); - free(a); -} - -JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); - COUNT(pool, ndeallocs); -} - -JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool) -{ - FreeArenaList(pool, &pool->first); -#ifdef JS_ARENAMETER - { - JSArenaStats *stats, **statsp; - - if (pool->stats.name) - free(pool->stats.name); - for (statsp = &arena_stats_list; (stats = *statsp) != 0; - statsp = &stats->next) { - if (stats == &pool->stats) { - *statsp = stats->next; - return; - } - } - } -#endif -} - -JS_PUBLIC_API(void) -JS_ArenaFinish() -{ -} - -JS_PUBLIC_API(void) -JS_ArenaShutDown(void) -{ -} - -#ifdef JS_ARENAMETER -JS_PUBLIC_API(void) -JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb) -{ - pool->stats.nallocs++; - pool->stats.nbytes += nb; - if (nb > pool->stats.maxalloc) - pool->stats.maxalloc = nb; - pool->stats.variance += nb * nb; -} - -JS_PUBLIC_API(void) -JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr) -{ - pool->stats.ninplace++; -} - -JS_PUBLIC_API(void) -JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr) -{ - pool->stats.ngrows++; - pool->stats.nbytes += incr; - pool->stats.variance -= size * size; - size += incr; - if (size > pool->stats.maxalloc) - pool->stats.maxalloc = size; - pool->stats.variance += size * size; -} - -JS_PUBLIC_API(void) -JS_ArenaCountRelease(JSArenaPool *pool, char *mark) -{ - pool->stats.nreleases++; -} - -JS_PUBLIC_API(void) -JS_ArenaCountRetract(JSArenaPool *pool, char *mark) -{ - pool->stats.nfastrels++; -} - -#include -#include - -JS_PUBLIC_API(void) -JS_DumpArenaStats(FILE *fp) -{ - JSArenaStats *stats; - uint32 nallocs, nbytes; - double mean, variance, sigma; - - for (stats = arena_stats_list; stats; stats = stats->next) { - nallocs = stats->nallocs; - if (nallocs != 0) { - nbytes = stats->nbytes; - mean = (double)nbytes / nallocs; - variance = stats->variance * nallocs - nbytes * nbytes; - if (variance < 0 || nallocs == 1) - variance = 0; - else - variance /= nallocs * (nallocs - 1); - sigma = sqrt(variance); - } else { - mean = variance = sigma = 0; - } - - fprintf(fp, "\n%s allocation statistics:\n", stats->name); - fprintf(fp, " number of arenas: %u\n", stats->narenas); - fprintf(fp, " number of allocations: %u\n", stats->nallocs); - fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims); - fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs); - fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs); - fprintf(fp, " number of allocation growths: %u\n", stats->ngrows); - fprintf(fp, " number of in-place growths: %u\n", stats->ninplace); - fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs); - fprintf(fp, "number of released allocations: %u\n", stats->nreleases); - fprintf(fp, " number of fast releases: %u\n", stats->nfastrels); - fprintf(fp, " total bytes allocated: %u\n", stats->nbytes); - fprintf(fp, " mean allocation size: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc); - } -} -#endif /* JS_ARENAMETER */ diff --git a/spidermonkey/libjs/jsarena.h b/spidermonkey/libjs/jsarena.h deleted file mode 100644 index 8be15d0..0000000 --- a/spidermonkey/libjs/jsarena.h +++ /dev/null @@ -1,303 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsarena_h___ -#define jsarena_h___ -/* - * Lifetime-based fast allocation, inspired by much prior art, including - * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes" - * David R. Hanson, Software -- Practice and Experience, Vol. 20(1). - * - * Also supports LIFO allocation (JS_ARENA_MARK/JS_ARENA_RELEASE). - */ -#include -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -typedef struct JSArena JSArena; -typedef struct JSArenaPool JSArenaPool; - -struct JSArena { - JSArena *next; /* next arena for this lifetime */ - jsuword base; /* aligned base address, follows this header */ - jsuword limit; /* one beyond last byte in arena */ - jsuword avail; /* points to next available byte */ -}; - -#ifdef JS_ARENAMETER -typedef struct JSArenaStats JSArenaStats; - -struct JSArenaStats { - JSArenaStats *next; /* next in arenaStats list */ - char *name; /* name for debugging */ - uint32 narenas; /* number of arenas in pool */ - uint32 nallocs; /* number of JS_ARENA_ALLOCATE() calls */ - uint32 nmallocs; /* number of malloc() calls */ - uint32 ndeallocs; /* number of lifetime deallocations */ - uint32 ngrows; /* number of JS_ARENA_GROW() calls */ - uint32 ninplace; /* number of in-place growths */ - uint32 nreallocs; /* number of arena grow extending reallocs */ - uint32 nreleases; /* number of JS_ARENA_RELEASE() calls */ - uint32 nfastrels; /* number of "fast path" releases */ - size_t nbytes; /* total bytes allocated */ - size_t maxalloc; /* maximum allocation size in bytes */ - double variance; /* size variance accumulator */ -}; -#endif - -struct JSArenaPool { - JSArena first; /* first arena in pool list */ - JSArena *current; /* arena from which to allocate space */ - size_t arenasize; /* net exact size of a new arena */ - jsuword mask; /* alignment mask (power-of-2 - 1) */ -#ifdef JS_ARENAMETER - JSArenaStats stats; -#endif -}; - -/* - * If the including .c file uses only one power-of-2 alignment, it may define - * JS_ARENA_CONST_ALIGN_MASK to the alignment mask and save a few instructions - * per ALLOCATE and GROW. - */ -#ifdef JS_ARENA_CONST_ALIGN_MASK -#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + JS_ARENA_CONST_ALIGN_MASK) \ - & ~(jsuword)JS_ARENA_CONST_ALIGN_MASK) - -#define JS_INIT_ARENA_POOL(pool, name, size) \ - JS_InitArenaPool(pool, name, size, JS_ARENA_CONST_ALIGN_MASK + 1) -#else -#define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask) -#endif - -#define JS_ARENA_ALLOCATE(p, pool, nb) \ - JS_ARENA_ALLOCATE_CAST(p, void *, pool, nb) - -#define JS_ARENA_ALLOCATE_TYPE(p, type, pool) \ - JS_ARENA_ALLOCATE_COMMON(p, type *, pool, sizeof(type), 0) - -#define JS_ARENA_ALLOCATE_CAST(p, type, pool, nb) \ - JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, _nb > _a->limit) - -/* - * NB: In JS_ARENA_ALLOCATE_CAST and JS_ARENA_GROW_CAST, always subtract _nb - * from a->limit rather than adding _nb to _p, to avoid overflowing a 32-bit - * address space (possible when running a 32-bit program on a 64-bit system - * where the kernel maps the heap up against the top of the 32-bit address - * space). - * - * Thanks to Juergen Kreileder , who brought this up in - * https://bugzilla.mozilla.org/show_bug.cgi?id=279273. - */ -#define JS_ARENA_ALLOCATE_COMMON(p, type, pool, nb, guard) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - size_t _nb = JS_ARENA_ALIGN(pool, nb); \ - jsuword _p = _a->avail; \ - if ((guard) || _p > _a->limit - _nb) \ - _p = (jsuword)JS_ArenaAllocate(pool, _nb); \ - else \ - _a->avail = _p + _nb; \ - p = (type) _p; \ - JS_ArenaCountAllocation(pool, nb); \ - JS_END_MACRO - -#define JS_ARENA_GROW(p, pool, size, incr) \ - JS_ARENA_GROW_CAST(p, void *, pool, size, incr) - -#define JS_ARENA_GROW_CAST(p, type, pool, size, incr) \ - JS_BEGIN_MACRO \ - JSArena *_a = (pool)->current; \ - if (_a->avail == (jsuword)(p) + JS_ARENA_ALIGN(pool, size)) { \ - size_t _nb = (size) + (incr); \ - _nb = JS_ARENA_ALIGN(pool, _nb); \ - if (_a->limit >= _nb && (jsuword)(p) <= _a->limit - _nb) { \ - _a->avail = (jsuword)(p) + _nb; \ - JS_ArenaCountInplaceGrowth(pool, size, incr); \ - } else if ((jsuword)(p) == _a->base) { \ - p = (type) JS_ArenaRealloc(pool, p, size, incr); \ - } else { \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - } else { \ - p = (type) JS_ArenaGrow(pool, p, size, incr); \ - } \ - JS_ArenaCountGrowth(pool, size, incr); \ - JS_END_MACRO - -#define JS_ARENA_MARK(pool) ((void *) (pool)->current->avail) -#define JS_UPTRDIFF(p,q) ((jsuword)(p) - (jsuword)(q)) - -#ifdef DEBUG -#define JS_FREE_PATTERN 0xDA -#define JS_CLEAR_UNUSED(a) (JS_ASSERT((a)->avail <= (a)->limit), \ - memset((void*)(a)->avail, JS_FREE_PATTERN, \ - (a)->limit - (a)->avail)) -#define JS_CLEAR_ARENA(a) memset((void*)(a), JS_FREE_PATTERN, \ - (a)->limit - (jsuword)(a)) -#else -#define JS_CLEAR_UNUSED(a) /* nothing */ -#define JS_CLEAR_ARENA(a) /* nothing */ -#endif - -#define JS_ARENA_RELEASE(pool, mark) \ - JS_BEGIN_MACRO \ - char *_m = (char *)(mark); \ - JSArena *_a = (pool)->current; \ - if (_a != &(pool)->first && \ - JS_UPTRDIFF(_m, _a->base) <= JS_UPTRDIFF(_a->avail, _a->base)) { \ - _a->avail = (jsuword)JS_ARENA_ALIGN(pool, _m); \ - JS_ASSERT(_a->avail <= _a->limit); \ - JS_CLEAR_UNUSED(_a); \ - JS_ArenaCountRetract(pool, _m); \ - } else { \ - JS_ArenaRelease(pool, _m); \ - } \ - JS_ArenaCountRelease(pool, _m); \ - JS_END_MACRO - -#ifdef JS_ARENAMETER -#define JS_COUNT_ARENA(pool,op) ((pool)->stats.narenas op) -#else -#define JS_COUNT_ARENA(pool,op) -#endif - -#define JS_ARENA_DESTROY(pool, a, pnext) \ - JS_BEGIN_MACRO \ - JS_COUNT_ARENA(pool,--); \ - if ((pool)->current == (a)) (pool)->current = &(pool)->first; \ - *(pnext) = (a)->next; \ - JS_CLEAR_ARENA(a); \ - free(a); \ - (a) = NULL; \ - JS_END_MACRO - -/* - * Initialize an arena pool with the given name for debugging and metering, - * with a minimum size per arena of size bytes. - */ -extern JS_PUBLIC_API(void) -JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, - size_t align); - -/* - * Free the arenas in pool. The user may continue to allocate from pool - * after calling this function. There is no need to call JS_InitArenaPool() - * again unless JS_FinishArenaPool(pool) has been called. - */ -extern JS_PUBLIC_API(void) -JS_FreeArenaPool(JSArenaPool *pool); - -/* - * Free the arenas in pool and finish using it altogether. - */ -extern JS_PUBLIC_API(void) -JS_FinishArenaPool(JSArenaPool *pool); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaFinish(void); - -/* - * Deprecated do-nothing function. - */ -extern JS_PUBLIC_API(void) -JS_ArenaShutDown(void); - -/* - * Friend functions used by the JS_ARENA_*() macros. - */ -extern JS_PUBLIC_API(void *) -JS_ArenaAllocate(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void *) -JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void *) -JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaRelease(JSArenaPool *pool, char *mark); - -/* - * Function to be used directly when an allocation has likely grown to consume - * an entire JSArena, in which case the arena is returned to the malloc heap. - */ -extern JS_PUBLIC_API(void) -JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size); - -#ifdef JS_ARENAMETER - -#include - -extern JS_PUBLIC_API(void) -JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb); - -extern JS_PUBLIC_API(void) -JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr); - -extern JS_PUBLIC_API(void) -JS_ArenaCountRelease(JSArenaPool *pool, char *mark); - -extern JS_PUBLIC_API(void) -JS_ArenaCountRetract(JSArenaPool *pool, char *mark); - -extern JS_PUBLIC_API(void) -JS_DumpArenaStats(FILE *fp); - -#else /* !JS_ARENAMETER */ - -#define JS_ArenaCountAllocation(ap, nb) /* nothing */ -#define JS_ArenaCountInplaceGrowth(ap, size, incr) /* nothing */ -#define JS_ArenaCountGrowth(ap, size, incr) /* nothing */ -#define JS_ArenaCountRelease(ap, mark) /* nothing */ -#define JS_ArenaCountRetract(ap, mark) /* nothing */ - -#endif /* !JS_ARENAMETER */ - -JS_END_EXTERN_C - -#endif /* jsarena_h___ */ diff --git a/spidermonkey/libjs/jsarray.c b/spidermonkey/libjs/jsarray.c deleted file mode 100644 index 532a1be..0000000 --- a/spidermonkey/libjs/jsarray.c +++ /dev/null @@ -1,1864 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS array class. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsstr.h" - -/* 2^32 - 1 as a number and a string */ -#define MAXINDEX 4294967295u -#define MAXSTR "4294967295" - -/* - * Determine if the id represents an array index or an XML property index. - * - * An id is an array index according to ECMA by (15.4): - * - * "Array objects give special treatment to a certain class of property names. - * A property name P (in the form of a string value) is an array index if and - * only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal - * to 2^32-1." - * - * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id) - * except that by using signed 32-bit integers we miss the top half of the - * valid range. This function checks the string representation itself; note - * that calling a standard conversion routine might allow strings such as - * "08" or "4.0" as array indices, which they are not. - */ -JSBool -js_IdIsIndex(jsval id, jsuint *indexp) -{ - JSString *str; - jschar *cp; - - if (JSVAL_IS_INT(id)) { - jsint i; - i = JSVAL_TO_INT(id); - if (i < 0) - return JS_FALSE; - *indexp = (jsuint)i; - return JS_TRUE; - } - - /* NB: id should be a string, but jsxml.c may call us with an object id. */ - if (!JSVAL_IS_STRING(id)) - return JS_FALSE; - - str = JSVAL_TO_STRING(id); - cp = JSSTRING_CHARS(str); - if (JS7_ISDEC(*cp) && JSSTRING_LENGTH(str) < sizeof(MAXSTR)) { - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; - if (index != 0) { - while (JS7_ISDEC(*cp)) { - oldIndex = index; - c = JS7_UNDEC(*cp); - index = 10*index + c; - cp++; - } - } - - /* Ensure that all characters were consumed and we didn't overflow. */ - if (*cp == 0 && - (oldIndex < (MAXINDEX / 10) || - (oldIndex == (MAXINDEX / 10) && c < (MAXINDEX % 10)))) - { - *indexp = index; - return JS_TRUE; - } - } - return JS_FALSE; -} - -static JSBool -ValueIsLength(JSContext *cx, jsval v, jsuint *lengthp) -{ - jsint i; - jsdouble d; - - if (JSVAL_IS_INT(v)) { - i = JSVAL_TO_INT(v); - if (i < 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - *lengthp = (jsuint) i; - return JS_TRUE; - } - - if (!js_ValueToNumber(cx, v, &d)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - if (!js_DoubleToECMAUint32(cx, d, (uint32 *)lengthp)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - if (JSDOUBLE_IS_NaN(d) || d != *lengthp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ARRAY_LENGTH); - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - JSTempValueRooter tvr; - jsid id; - JSBool ok; - jsint i; - - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); - if (ok) { - /* - * Short-circuit, because js_ValueToECMAUint32 fails when called - * during init time. - */ - if (JSVAL_IS_INT(tvr.u.value)) { - i = JSVAL_TO_INT(tvr.u.value); - *lengthp = (jsuint)i; /* jsuint cast does ToUint32 */ - } else { - ok = js_ValueToECMAUint32(cx, tvr.u.value, (uint32 *)lengthp); - } - } - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -static JSBool -IndexToValue(JSContext *cx, jsuint index, jsval *vp) -{ - if (index <= JSVAL_INT_MAX) { - *vp = INT_TO_JSVAL(index); - return JS_TRUE; - } - return js_NewDoubleValue(cx, (jsdouble)index, vp); -} - -static JSBool -BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, - jsid *idp) -{ - jschar buf[10], *start; - JSClass *clasp; - JSAtom *atom; - JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); - - JS_ASSERT(index > JSVAL_INT_MAX); - - start = JS_ARRAY_END(buf); - do { - --start; - *start = (jschar)('0' + index % 10); - index /= 10; - } while (index != 0); - - /* - * Skip the atomization if the class is known to store atoms corresponding - * to big indexes together with elements. In such case we know that the - * array does not have an element at the given index if its atom does not - * exist. - */ - if (!createAtom && - ((clasp = OBJ_GET_CLASS(cx, obj)) == &js_ArrayClass || - clasp == &js_ArgumentsClass || - clasp == &js_ObjectClass)) { - atom = js_GetExistingStringAtom(cx, start, JS_ARRAY_END(buf) - start); - if (!atom) { - *idp = JSVAL_VOID; - return JS_TRUE; - } - } else { - atom = js_AtomizeChars(cx, start, JS_ARRAY_END(buf) - start, 0); - if (!atom) - return JS_FALSE; - } - - *idp = ATOM_TO_JSID(atom); - return JS_TRUE; -} - -/* - * If the property at the given index exists, get its value into location - * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp - * to JSVAL_VOID. This function assumes that the location pointed by vp is - * properly rooted and can be used as GC-protected storage for temporaries. - */ -static JSBool -GetArrayElement(JSContext *cx, JSObject *obj, jsuint index, JSBool *hole, - jsval *vp) -{ - jsid id; - JSObject *obj2; - JSProperty *prop; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) - return JS_FALSE; - if (id == JSVAL_VOID) { - *hole = JS_TRUE; - *vp = JSVAL_VOID; - return JS_TRUE; - } - } - - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - *hole = JS_TRUE; - *vp = JSVAL_VOID; - } else { - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!OBJ_GET_PROPERTY(cx, obj, id, vp)) - return JS_FALSE; - *hole = JS_FALSE; - } - return JS_TRUE; -} - -/* - * Set the value of the property at the given index to v assuming v is rooted. - */ -static JSBool -SetArrayElement(JSContext *cx, JSObject *obj, jsuint index, jsval v) -{ - jsid id; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_TRUE, &id)) - return JS_FALSE; - JS_ASSERT(id != JSVAL_VOID); - } - return OBJ_SET_PROPERTY(cx, obj, id, &v); -} - -static JSBool -DeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index) -{ - jsid id; - jsval junk; - - if (index <= JSVAL_INT_MAX) { - id = INT_TO_JSID(index); - } else { - if (!BigIndexToId(cx, obj, index, JS_FALSE, &id)) - return JS_FALSE; - if (id == JSVAL_VOID) - return JS_TRUE; - } - return OBJ_DELETE_PROPERTY(cx, obj, id, &junk); -} - -/* - * When hole is true, delete the property at the given index. Otherwise set - * its value to v assuming v is rooted. - */ -static JSBool -SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsuint index, - JSBool hole, jsval v) -{ - if (hole) { - JS_ASSERT(v == JSVAL_VOID); - return DeleteArrayElement(cx, obj, index); - } else { - return SetArrayElement(cx, obj, index, v); - } -} - - -JSBool -js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length) -{ - jsval v; - jsid id; - - if (!IndexToValue(cx, length, &v)) - return JS_FALSE; - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - return OBJ_SET_PROPERTY(cx, obj, id, &v); -} - -JSBool -js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) -{ - JSErrorReporter older; - JSTempValueRooter tvr; - jsid id; - JSBool ok; - - older = JS_SetErrorReporter(cx, NULL); - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value); - JS_SetErrorReporter(cx, older); - if (ok) - ok = ValueIsLength(cx, tvr.u.value, lengthp); - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -JSBool -js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, obj); - *answerp = (clasp == &js_ArgumentsClass || clasp == &js_ArrayClass); - if (!*answerp) { - *lengthp = 0; - return JS_TRUE; - } - return js_GetLengthProperty(cx, obj, lengthp); -} - -/* - * This get function is specific to Array.prototype.length and other array - * instance length properties. It calls back through the class get function - * in case some magic happens there (see call_getProperty in jsfun.c). - */ -static JSBool -array_length_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, id, vp); -} - -static JSBool -array_length_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsuint newlen, oldlen, gap, index; - jsid id2; - jsval junk; - JSObject *iter; - JSTempValueRooter tvr; - JSBool ok; - - if (!ValueIsLength(cx, *vp, &newlen)) - return JS_FALSE; - if (!js_GetLengthProperty(cx, obj, &oldlen)) - return JS_FALSE; - if (oldlen > newlen) { - if (oldlen - newlen < (1 << 24)) { - do { - --oldlen; - if (!DeleteArrayElement(cx, obj, oldlen)) - return JS_FALSE; - } while (oldlen != newlen); - } else { - /* - * We are going to remove a lot of indexes in a presumably sparse - * array. So instead of looping through indexes between newlen and - * oldlen, we iterate through all properties and remove those that - * correspond to indexes from the [newlen, oldlen) range. - * See bug 322135. - */ - iter = JS_NewPropertyIterator(cx, obj); - if (!iter) - return JS_FALSE; - - /* Protect iter against GC in OBJ_DELETE_PROPERTY. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, iter, &tvr); - gap = oldlen - newlen; - for (;;) { - ok = JS_NextProperty(cx, iter, &id2); - if (!ok) - break; - if (id2 == JSVAL_VOID) - break; - if (js_IdIsIndex(id2, &index) && index - newlen < gap) { - ok = OBJ_DELETE_PROPERTY(cx, obj, id2, &junk); - if (!ok) - break; - } - } - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - } - } - return IndexToValue(cx, newlen, vp); -} - -static JSBool -array_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsuint index, length; - - if (!js_IdIsIndex(id, &index)) - return JS_TRUE; - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (index >= length) { - length = index + 1; - return js_SetLengthProperty(cx, obj, length); - } - return JS_TRUE; -} - -static JSBool -array_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - return js_TryValueOf(cx, obj, type, vp); -} - -JSClass js_ArrayClass = { - "Array", - JSCLASS_HAS_CACHED_PROTO(JSProto_Array), - array_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, array_convert, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -enum ArrayToStringOp { - TO_STRING, - TO_LOCALE_STRING, - TO_SOURCE -}; - -/* - * When op is TO_STRING or TO_LOCALE_STRING sep indicates a separator to use - * or "," when sep is NULL. - * When op is TO_SOURCE sep must be NULL. - */ -static JSBool -array_join_sub(JSContext *cx, JSObject *obj, enum ArrayToStringOp op, - JSString *sep, jsval *rval) -{ - JSBool ok, hole; - jsuint length, index; - jschar *chars, *ochars; - size_t nchars, growth, seplen, tmplen, extratail; - const jschar *sepstr; - JSString *str; - JSHashEntry *he; - JSTempValueRooter tvr; - JSAtom *atom; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - ok = js_GetLengthProperty(cx, obj, &length); - if (!ok) - return JS_FALSE; - - he = js_EnterSharpObject(cx, obj, NULL, &chars); - if (!he) - return JS_FALSE; -#ifdef DEBUG - growth = (size_t) -1; -#endif - - if (op == TO_SOURCE) { - if (IS_SHARP(he)) { -#if JS_HAS_SHARP_VARS - nchars = js_strlen(chars); -#else - chars[0] = '['; - chars[1] = ']'; - chars[2] = 0; - nchars = 2; -#endif - goto make_string; - } - - /* - * Always allocate 2 extra chars for closing ']' and terminating 0 - * and then preallocate 1 + extratail to include starting '['. - */ - extratail = 2; - growth = (1 + extratail) * sizeof(jschar); - if (!chars) { - nchars = 0; - chars = (jschar *) malloc(growth); - if (!chars) - goto done; - } else { - MAKE_SHARP(he); - nchars = js_strlen(chars); - growth += nchars * sizeof(jschar); - chars = (jschar *)realloc((ochars = chars), growth); - if (!chars) { - free(ochars); - goto done; - } - } - chars[nchars++] = '['; - JS_ASSERT(sep == NULL); - sepstr = NULL; /* indicates to use ", " as separator */ - seplen = 2; - } else { - /* - * Free any sharp variable definition in chars. Normally, we would - * MAKE_SHARP(he) so that only the first sharp variable annotation is - * a definition, and all the rest are references, but in the current - * case of (op != TO_SOURCE), we don't need chars at all. - */ - if (chars) - JS_free(cx, chars); - chars = NULL; - nchars = 0; - extratail = 1; /* allocate extra char for terminating 0 */ - - /* Return the empty string on a cycle as well as on empty join. */ - if (IS_BUSY(he) || length == 0) { - js_LeaveSharpObject(cx, NULL); - *rval = JS_GetEmptyStringValue(cx); - return ok; - } - - /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ - MAKE_BUSY(he); - - if (sep) { - sepstr = JSSTRING_CHARS(sep); - seplen = JSSTRING_LENGTH(sep); - } else { - sepstr = NULL; /* indicates to use "," as separator */ - seplen = 1; - } - } - - /* Use rval to locally root each element value as we loop and convert. */ -#define v (*rval) - - for (index = 0; index < length; index++) { - ok = GetArrayElement(cx, obj, index, &hole, &v); - if (!ok) - goto done; - if (hole || - (op != TO_SOURCE && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v)))) { - str = cx->runtime->emptyString; - } else { - if (op == TO_LOCALE_STRING) { - atom = cx->runtime->atomState.toLocaleStringAtom; - JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); - ok = js_ValueToObject(cx, v, &tvr.u.object) && - js_TryMethod(cx, tvr.u.object, atom, 0, NULL, &v); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - goto done; - str = js_ValueToString(cx, v); - } else if (op == TO_STRING) { - str = js_ValueToString(cx, v); - } else { - JS_ASSERT(op == TO_SOURCE); - str = js_ValueToSource(cx, v); - } - if (!str) { - ok = JS_FALSE; - goto done; - } - } - - /* - * Do not append separator after the last element unless it is a hole - * and we are in toSource. In that case we append single ",". - */ - if (index + 1 == length) - seplen = (hole && op == TO_SOURCE) ? 1 : 0; - - /* Allocate 1 at end for closing bracket and zero. */ - tmplen = JSSTRING_LENGTH(str); - growth = nchars + tmplen + seplen + extratail; - if (nchars > growth || tmplen > growth || - growth > (size_t)-1 / sizeof(jschar)) { - if (chars) { - free(chars); - chars = NULL; - } - goto done; - } - growth *= sizeof(jschar); - if (!chars) { - chars = (jschar *) malloc(growth); - if (!chars) - goto done; - } else { - chars = (jschar *) realloc((ochars = chars), growth); - if (!chars) { - free(ochars); - goto done; - } - } - - js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); - nchars += tmplen; - - if (seplen) { - if (sepstr) { - js_strncpy(&chars[nchars], sepstr, seplen); - } else { - JS_ASSERT(seplen == 1 || seplen == 2); - chars[nchars] = ','; - if (seplen == 2) - chars[nchars + 1] = ' '; - } - nchars += seplen; - } - } - - done: - if (op == TO_SOURCE) { - if (chars) - chars[nchars++] = ']'; - } else { - CLEAR_BUSY(he); - } - js_LeaveSharpObject(cx, NULL); - if (!ok) { - if (chars) - free(chars); - return ok; - } - -#undef v - - make_string: - if (!chars) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - chars[nchars] = 0; - JS_ASSERT(growth == (size_t)-1 || (nchars + 1) * sizeof(jschar) == growth); - str = js_NewString(cx, chars, nchars, 0); - if (!str) { - free(chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#if JS_HAS_TOSOURCE -static JSBool -array_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_join_sub(cx, obj, TO_SOURCE, NULL, rval); -} -#endif - -static JSBool -array_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_join_sub(cx, obj, TO_STRING, NULL, rval); -} - -static JSBool -array_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* - * Passing comma here as the separator. Need a way to get a - * locale-specific version. - */ - return array_join_sub(cx, obj, TO_LOCALE_STRING, NULL, rval); -} - -static JSBool -InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint end, - jsval *vector) -{ - while (start != end) { - if (!SetArrayElement(cx, obj, start++, *vector++)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, jsval *vector) -{ - jsval v; - jsid id; - - if (!IndexToValue(cx, length, &v)) - return JS_FALSE; - id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom); - if (!OBJ_DEFINE_PROPERTY(cx, obj, id, v, - array_length_getter, array_length_setter, - JSPROP_PERMANENT, - NULL)) { - return JS_FALSE; - } - if (!vector) - return JS_TRUE; - return InitArrayElements(cx, obj, 0, length, vector); -} - -/* - * Perl-inspired join, reverse, and sort. - */ -static JSBool -array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - if (JSVAL_IS_VOID(argv[0])) { - str = NULL; - } else { - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - } - return array_join_sub(cx, obj, TO_STRING, str, rval); -} - -static JSBool -array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsuint len, half, i; - JSBool hole, hole2; - jsval *tmproot, *tmproot2; - - if (!js_GetLengthProperty(cx, obj, &len)) - return JS_FALSE; - - /* - * Use argv[argc] and argv[argc + 1] as local roots to hold temporarily - * array elements for GC-safe swap. - */ - tmproot = argv + argc; - tmproot2 = argv + argc + 1; - half = len / 2; - for (i = 0; i < half; i++) { - if (!GetArrayElement(cx, obj, i, &hole, tmproot) || - !GetArrayElement(cx, obj, len - i - 1, &hole2, tmproot2) || - !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, *tmproot) || - !SetOrDeleteArrayElement(cx, obj, i, hole2, *tmproot2)) { - return JS_FALSE; - } - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -typedef struct HSortArgs { - void *vec; - size_t elsize; - void *pivot; - JSComparator cmp; - void *arg; - JSBool fastcopy; -} HSortArgs; - -static JSBool -sort_compare(void *arg, const void *a, const void *b, int *result); - -static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result); - -static JSBool -HeapSortHelper(JSBool building, HSortArgs *hsa, size_t lo, size_t hi) -{ - void *pivot, *vec, *vec2, *arg, *a, *b; - size_t elsize; - JSComparator cmp; - JSBool fastcopy; - size_t j, hiDiv2; - int cmp_result; - - pivot = hsa->pivot; - vec = hsa->vec; - elsize = hsa->elsize; - vec2 = (char *)vec - 2 * elsize; - cmp = hsa->cmp; - arg = hsa->arg; - - fastcopy = hsa->fastcopy; -#define MEMCPY(p,q,n) \ - (fastcopy ? (void)(*(jsval*)(p) = *(jsval*)(q)) : (void)memcpy(p, q, n)) -#define CALL_CMP(a, b) \ - if (!cmp(arg, (a), (b), &cmp_result)) return JS_FALSE; - - if (lo == 1) { - j = 2; - b = (char *)vec + elsize; - if (j < hi) { - CALL_CMP(vec, b); - if (cmp_result < 0) - j++; - } - a = (char *)vec + (hi - 1) * elsize; - b = (char *)vec2 + j * elsize; - - /* - * During sorting phase b points to a member of heap that cannot be - * bigger then biggest of vec[0] and vec[1], and cmp(a, b, arg) <= 0 - * always holds. - */ - if (building || hi == 2) { - CALL_CMP(a, b); - if (cmp_result >= 0) - return JS_TRUE; - } - - MEMCPY(pivot, a, elsize); - MEMCPY(a, b, elsize); - lo = j; - } else { - a = (char *)vec2 + lo * elsize; - MEMCPY(pivot, a, elsize); - } - - hiDiv2 = hi/2; - while (lo <= hiDiv2) { - j = lo + lo; - a = (char *)vec2 + j * elsize; - b = (char *)vec + (j - 1) * elsize; - if (j < hi) { - CALL_CMP(a, b); - if (cmp_result < 0) - j++; - } - b = (char *)vec2 + j * elsize; - CALL_CMP(pivot, b); - if (cmp_result >= 0) - break; - - a = (char *)vec2 + lo * elsize; - MEMCPY(a, b, elsize); - lo = j; - } - - a = (char *)vec2 + lo * elsize; - MEMCPY(a, pivot, elsize); - - return JS_TRUE; - -#undef CALL_CMP -#undef MEMCPY - -} - -JSBool -js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, - JSComparator cmp, void *arg) -{ - HSortArgs hsa; - size_t i; - - hsa.vec = vec; - hsa.elsize = elsize; - hsa.pivot = pivot; - hsa.cmp = cmp; - hsa.arg = arg; - hsa.fastcopy = (cmp == sort_compare || cmp == sort_compare_strings); - - for (i = nel/2; i != 0; i--) { - if (!HeapSortHelper(JS_TRUE, &hsa, i, nel)) - return JS_FALSE; - } - while (nel > 2) { - if (!HeapSortHelper(JS_FALSE, &hsa, 1, --nel)) - return JS_FALSE; - } - - return JS_TRUE; -} - -typedef struct CompareArgs { - JSContext *context; - jsval fval; - jsval *localroot; /* need one local root, for sort_compare */ -} CompareArgs; - -static JSBool -sort_compare(void *arg, const void *a, const void *b, int *result) -{ - jsval av = *(const jsval *)a, bv = *(const jsval *)b; - CompareArgs *ca = (CompareArgs *) arg; - JSContext *cx = ca->context; - jsval fval; - JSBool ok; - - /** - * array_sort deals with holes and undefs on its own and they should not - * come here. - */ - JS_ASSERT(av != JSVAL_VOID); - JS_ASSERT(bv != JSVAL_VOID); - - *result = 0; - ok = JS_TRUE; - fval = ca->fval; - if (fval == JSVAL_NULL) { - JSString *astr, *bstr; - - if (av != bv) { - /* - * Set our local root to astr in case the second js_ValueToString - * displaces the newborn root in cx, and the GC nests under that - * call. Don't bother guarding the local root store with an astr - * non-null test. If we tag null as a string, the GC will untag, - * null-test, and avoid dereferencing null. - */ - astr = js_ValueToString(cx, av); - *ca->localroot = STRING_TO_JSVAL(astr); - if (astr && (bstr = js_ValueToString(cx, bv))) - *result = js_CompareStrings(astr, bstr); - else - ok = JS_FALSE; - } - } else { - jsdouble cmp; - jsval argv[2]; - - argv[0] = av; - argv[1] = bv; - ok = js_InternalCall(cx, - OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fval)), - fval, 2, argv, ca->localroot); - if (ok) { - ok = js_ValueToNumber(cx, *ca->localroot, &cmp); - - /* Clamp cmp to -1, 0, 1. */ - if (ok) { - if (JSDOUBLE_IS_NaN(cmp)) { - /* - * XXX report some kind of error here? ECMA talks about - * 'consistent compare functions' that don't return NaN, - * but is silent about what the result should be. So we - * currently ignore it. - */ - } else if (cmp != 0) { - *result = cmp > 0 ? 1 : -1; - } - } - } - } - return ok; -} - -static int -sort_compare_strings(void *arg, const void *a, const void *b, int *result) -{ - jsval av = *(const jsval *)a, bv = *(const jsval *)b; - - *result = (int) js_CompareStrings(JSVAL_TO_STRING(av), JSVAL_TO_STRING(bv)); - return JS_TRUE; -} - -static JSBool -array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval fval, *vec, *pivotroot; - CompareArgs ca; - jsuint len, newlen, i, undefs; - JSTempValueRooter tvr; - JSBool hole, ok; - - /* - * Optimize the default compare function case if all of obj's elements - * have values of type string. - */ - JSBool all_strings; - - if (argc > 0) { - if (JSVAL_IS_PRIMITIVE(argv[0])) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SORT_ARG); - return JS_FALSE; - } - fval = argv[0]; - all_strings = JS_FALSE; /* non-default compare function */ - } else { - fval = JSVAL_NULL; - all_strings = JS_TRUE; /* check for all string values */ - } - - if (!js_GetLengthProperty(cx, obj, &len)) - return JS_FALSE; - if (len == 0) { - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - /* - * We need a temporary array of len jsvals to hold elements of the array. - * Check that its size does not overflow size_t, which would allow for - * indexing beyond the end of the malloc'd vector. - */ - if (len > ((size_t) -1) / sizeof(jsval)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - vec = (jsval *) JS_malloc(cx, ((size_t) len) * sizeof(jsval)); - if (!vec) - return JS_FALSE; - - /* - * Initialize vec as a root. We will clear elements of vec one by - * one while increasing tvr.count when we know that the property at - * the corresponding index exists and its value must be rooted. - * - * In this way when sorting a huge mostly sparse array we will not - * access the tail of vec corresponding to properties that do not - * exist, allowing OS to avoiding committing RAM. See bug 330812. - * - * After this point control must flow through label out: to exit. - */ - JS_PUSH_TEMP_ROOT(cx, 0, vec, &tvr); - - /* - * By ECMA 262, 15.4.4.11, a property that does not exist (which we - * call a "hole") is always greater than an existing property with - * value undefined and that is always greater than any other property. - * Thus to sort holes and undefs we simply count them, sort the rest - * of elements, append undefs after them and then make holes after - * undefs. - */ - undefs = 0; - newlen = 0; - for (i = 0; i < len; i++) { - /* Clear vec[newlen] before including it in the rooted set. */ - vec[newlen] = JSVAL_NULL; - tvr.count = newlen + 1; - ok = GetArrayElement(cx, obj, i, &hole, &vec[newlen]); - if (!ok) - goto out; - - if (hole) - continue; - - if (vec[newlen] == JSVAL_VOID) { - ++undefs; - continue; - } - - /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ - all_strings &= JSVAL_IS_STRING(vec[newlen]); - - ++newlen; - } - - /* Here len == newlen + undefs + number_of_holes. */ - ca.context = cx; - ca.fval = fval; - ca.localroot = argv + argc; /* local GC root for temporary string */ - pivotroot = argv + argc + 1; /* local GC root for pivot val */ - ok = js_HeapSort(vec, (size_t) newlen, pivotroot, sizeof(jsval), - all_strings ? sort_compare_strings : sort_compare, - &ca); - if (!ok) - goto out; - - ok = InitArrayElements(cx, obj, 0, newlen, vec); - if (!ok) - goto out; - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - JS_free(cx, vec); - if (!ok) - return JS_FALSE; - - /* Set undefs that sorted after the rest of elements. */ - while (undefs != 0) { - --undefs; - if (!SetArrayElement(cx, obj, newlen++, JSVAL_VOID)) - return JS_FALSE; - } - - /* Re-create any holes that sorted to the end of the array. */ - while (len > newlen) { - if (!DeleteArrayElement(cx, obj, --len)) - return JS_FALSE; - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Perl-inspired push, pop, shift, unshift, and splice methods. - */ -static JSBool -array_push(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint length, newlength; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - newlength = length + argc; - if (!InitArrayElements(cx, obj, length, newlength, argv)) - return JS_FALSE; - - /* Per ECMA-262, return the new array length. */ - if (!IndexToValue(cx, newlength, rval)) - return JS_FALSE; - return js_SetLengthProperty(cx, obj, newlength); -} - -static JSBool -array_pop(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint index; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &index)) - return JS_FALSE; - if (index > 0) { - index--; - - /* Get the to-be-deleted property's value into rval. */ - if (!GetArrayElement(cx, obj, index, &hole, rval)) - return JS_FALSE; - if (!hole && !DeleteArrayElement(cx, obj, index)) - return JS_FALSE; - } - return js_SetLengthProperty(cx, obj, index); -} - -static JSBool -array_shift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint length, i; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (length == 0) { - *rval = JSVAL_VOID; - } else { - length--; - - /* Get the to-be-deleted property's value into rval ASAP. */ - if (!GetArrayElement(cx, obj, 0, &hole, rval)) - return JS_FALSE; - - /* - * Slide down the array above the first element. - */ - for (i = 0; i != length; i++) { - if (!GetArrayElement(cx, obj, i + 1, &hole, &argv[0])) - return JS_FALSE; - if (!SetOrDeleteArrayElement(cx, obj, i, hole, argv[0])) - return JS_FALSE; - } - - /* Delete the only or last element when it exist. */ - if (!hole && !DeleteArrayElement(cx, obj, length)) - return JS_FALSE; - } - return js_SetLengthProperty(cx, obj, length); -} - -static JSBool -array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsuint length, last; - jsval *vp; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (argc > 0) { - /* Slide up the array to make room for argc at the bottom. */ - if (length > 0) { - last = length; - vp = argv + argc; /* local root */ - do { - --last; - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last + argc, hole, *vp)) { - return JS_FALSE; - } - } while (last != 0); - } - - /* Copy from argv to the bottom of the array. */ - if (!InitArrayElements(cx, obj, 0, argc, argv)) - return JS_FALSE; - - length += argc; - if (!js_SetLengthProperty(cx, obj, length)) - return JS_FALSE; - } - - /* Follow Perl by returning the new array length. */ - return IndexToValue(cx, length, rval); -} - -static JSBool -array_splice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp; - jsuint length, begin, end, count, delta, last; - jsdouble d; - JSBool hole; - JSObject *obj2; - - /* - * Nothing to do if no args. Otherwise point vp at our one explicit local - * root and get length. - */ - if (argc == 0) - return JS_TRUE; - vp = argv + argc; - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - - /* Convert the first argument into a starting index. */ - if (!js_ValueToNumber(cx, *argv, &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (jsuint)d; /* d has been clamped to uint32 */ - argc--; - argv++; - - /* Convert the second argument from a count into a fencepost index. */ - delta = length - begin; - if (argc == 0) { - count = delta; - end = length; - } else { - if (!js_ValueToNumber(cx, *argv, &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) - d = 0; - else if (d > delta) - d = delta; - count = (jsuint)d; - end = begin + count; - argc--; - argv++; - } - - - /* - * Create a new array value to return. Our ECMA v2 proposal specs - * that splice always returns an array value, even when given no - * arguments. We think this is best because it eliminates the need - * for callers to do an extra test to handle the empty splice case. - */ - obj2 = js_NewArrayObject(cx, 0, NULL); - if (!obj2) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj2); - - /* If there are elements to remove, put them into the return value. */ - if (count > 0) { - for (last = begin; last < end; last++) { - if (!GetArrayElement(cx, obj, last, &hole, vp)) - return JS_FALSE; - - /* Copy *vp to new array unless it's a hole. */ - if (!hole && !SetArrayElement(cx, obj2, last - begin, *vp)) - return JS_FALSE; - } - - if (!js_SetLengthProperty(cx, obj2, end - begin)) - return JS_FALSE; - } - - /* Find the direction (up or down) to copy and make way for argv. */ - if (argc > count) { - delta = (jsuint)argc - count; - last = length; - /* (uint) end could be 0, so can't use vanilla >= test */ - while (last-- > end) { - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last + delta, hole, *vp)) { - return JS_FALSE; - } - } - length += delta; - } else if (argc < count) { - delta = count - (jsuint)argc; - for (last = end; last < length; last++) { - if (!GetArrayElement(cx, obj, last, &hole, vp) || - !SetOrDeleteArrayElement(cx, obj, last - delta, hole, *vp)) { - return JS_FALSE; - } - } - length -= delta; - } - - /* Copy from argv into the hole to complete the splice. */ - if (!InitArrayElements(cx, obj, begin, begin + argc, argv)) - return JS_FALSE; - - /* Update length in case we deleted elements from the end. */ - return js_SetLengthProperty(cx, obj, length); -} - -/* - * Python-esque sequence operations. - */ -static JSBool -array_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp, v; - JSObject *nobj, *aobj; - jsuint length, alength, slot; - uintN i; - JSBool hole; - - /* Hoist the explicit local root address computation. */ - vp = argv + argc; - - /* Treat obj as the first argument; see ECMA 15.4.4.4. */ - --argv; - JS_ASSERT(obj == JSVAL_TO_OBJECT(argv[0])); - - /* Create a new Array object and store it in the rval local root. */ - nobj = js_NewArrayObject(cx, 0, NULL); - if (!nobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nobj); - - /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */ - length = 0; - for (i = 0; i <= argc; i++) { - v = argv[i]; - if (JSVAL_IS_OBJECT(v)) { - aobj = JSVAL_TO_OBJECT(v); - if (aobj && OBJ_GET_CLASS(cx, aobj) == &js_ArrayClass) { - if (!OBJ_GET_PROPERTY(cx, aobj, - ATOM_TO_JSID(cx->runtime->atomState - .lengthAtom), - vp)) { - return JS_FALSE; - } - if (!ValueIsLength(cx, *vp, &alength)) - return JS_FALSE; - for (slot = 0; slot < alength; slot++) { - if (!GetArrayElement(cx, aobj, slot, &hole, vp)) - return JS_FALSE; - - /* - * Per ECMA 262, 15.4.4.4, step 9, ignore non-existent - * properties. - */ - if (!hole && !SetArrayElement(cx, nobj, length + slot, *vp)) - return JS_FALSE; - } - length += alength; - continue; - } - } - - if (!SetArrayElement(cx, nobj, length, v)) - return JS_FALSE; - length++; - } - - return js_SetLengthProperty(cx, nobj, length); -} - -static JSBool -array_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp; - JSObject *nobj; - jsuint length, begin, end, slot; - jsdouble d; - JSBool hole; - - /* Hoist the explicit local root address computation. */ - vp = argv + argc; - - /* Create a new Array object and store it in the rval local root. */ - nobj = js_NewArrayObject(cx, 0, NULL); - if (!nobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nobj); - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - begin = 0; - end = length; - - if (argc > 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - begin = (jsuint)d; - - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) { - d += length; - if (d < 0) - d = 0; - } else if (d > length) { - d = length; - } - end = (jsuint)d; - } - } - - if (begin > end) - begin = end; - - for (slot = begin; slot < end; slot++) { - if (!GetArrayElement(cx, obj, slot, &hole, vp)) - return JS_FALSE; - if (!hole && !SetArrayElement(cx, nobj, slot - begin, *vp)) - return JS_FALSE; - } - return js_SetLengthProperty(cx, nobj, end - begin); -} - -#if JS_HAS_ARRAY_EXTRAS - -static JSBool -array_indexOfHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval, JSBool isLast) -{ - jsuint length, i, stop; - jsint direction; - JSBool hole; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - if (length == 0) - goto not_found; - - if (argc <= 1) { - i = isLast ? length - 1 : 0; - } else { - jsdouble start; - - if (!js_ValueToNumber(cx, argv[1], &start)) - return JS_FALSE; - start = js_DoubleToInteger(start); - if (start < 0) { - start += length; - if (start < 0) { - if (isLast) - goto not_found; - i = 0; - } else { - i = (jsuint)start; - } - } else if (start >= length) { - if (!isLast) - goto not_found; - i = length - 1; - } else { - i = (jsuint)start; - } - } - - if (isLast) { - stop = 0; - direction = -1; - } else { - stop = length - 1; - direction = 1; - } - - for (;;) { - if (!GetArrayElement(cx, obj, (jsuint)i, &hole, rval)) - return JS_FALSE; - if (!hole && js_StrictlyEqual(*rval, argv[0])) - return js_NewNumberValue(cx, i, rval); - if (i == stop) - goto not_found; - i += direction; - } - - not_found: - *rval = INT_TO_JSVAL(-1); - return JS_TRUE; -} - -static JSBool -array_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_indexOfHelper(cx, obj, argc, argv, rval, JS_FALSE); -} - -static JSBool -array_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_indexOfHelper(cx, obj, argc, argv, rval, JS_TRUE); -} - -/* Order is important; extras that use a caller's predicate must follow MAP. */ -typedef enum ArrayExtraMode { - FOREACH, - MAP, - FILTER, - SOME, - EVERY -} ArrayExtraMode; - -static JSBool -array_extra(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, - ArrayExtraMode mode) -{ - jsval *vp, *sp, *origsp, *oldsp; - jsuint length, newlen, i; - JSObject *callable, *thisp, *newarr; - void *mark; - JSStackFrame *fp; - JSBool ok, cond, hole; - - /* Hoist the explicit local root address computation. */ - vp = argv + argc; - - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; - - /* - * First, get or compute our callee, so that we error out consistently - * when passed a non-callable object. - */ - callable = js_ValueToCallableObject(cx, &argv[0], JSV2F_SEARCH_STACK); - if (!callable) - return JS_FALSE; - - /* - * Set our initial return condition, used for zero-length array cases - * (and pre-size our map return to match our known length, for all cases). - */ -#ifdef __GNUC__ /* quell GCC overwarning */ - newlen = 0; - newarr = NULL; - ok = JS_TRUE; -#endif - switch (mode) { - case MAP: - case FILTER: - newlen = (mode == MAP) ? length : 0; - newarr = js_NewArrayObject(cx, newlen, NULL); - if (!newarr) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(newarr); - break; - case SOME: - *rval = JSVAL_FALSE; - break; - case EVERY: - *rval = JSVAL_TRUE; - break; - case FOREACH: - break; - } - - if (length == 0) - return JS_TRUE; - - if (argc > 1) { - if (!js_ValueToObject(cx, argv[1], &thisp)) - return JS_FALSE; - argv[1] = OBJECT_TO_JSVAL(thisp); - } else { - thisp = NULL; - } - - /* We call with 3 args (value, index, array), plus room for rval. */ - origsp = js_AllocStack(cx, 2 + 3 + 1, &mark); - if (!origsp) - return JS_FALSE; - - /* Lift current frame to include our args. */ - fp = cx->fp; - oldsp = fp->sp; - - for (i = 0; i < length; i++) { - ok = GetArrayElement(cx, obj, i, &hole, vp); - if (!ok) - break; - if (hole) - continue; - - /* - * Push callable and 'this', then args. We must do this for every - * iteration around the loop since js_Invoke uses origsp[0] for rval - * storage and some native functions use origsp[1] for local rooting. - */ - sp = origsp; - *sp++ = OBJECT_TO_JSVAL(callable); - *sp++ = OBJECT_TO_JSVAL(thisp); - *sp++ = *vp; - *sp++ = INT_TO_JSVAL(i); - *sp++ = OBJECT_TO_JSVAL(obj); - - /* Do the call. */ - fp->sp = sp; - ok = js_Invoke(cx, 3, JSINVOKE_INTERNAL); - vp[1] = fp->sp[-1]; - fp->sp = oldsp; - if (!ok) - break; - - if (mode > MAP) { - if (vp[1] == JSVAL_NULL) { - cond = JS_FALSE; - } else if (JSVAL_IS_BOOLEAN(vp[1])) { - cond = JSVAL_TO_BOOLEAN(vp[1]); - } else { - ok = js_ValueToBoolean(cx, vp[1], &cond); - if (!ok) - goto out; - } - } - - switch (mode) { - case FOREACH: - break; - case MAP: - ok = SetArrayElement(cx, newarr, i, vp[1]); - if (!ok) - goto out; - break; - case FILTER: - if (!cond) - break; - /* Filter passed *vp, push as result. */ - ok = SetArrayElement(cx, newarr, newlen++, *vp); - if (!ok) - goto out; - break; - case SOME: - if (cond) { - *rval = JSVAL_TRUE; - goto out; - } - break; - case EVERY: - if (!cond) { - *rval = JSVAL_FALSE; - goto out; - } - break; - } - } - - out: - js_FreeStack(cx, mark); - if (ok && mode == FILTER) - ok = js_SetLengthProperty(cx, newarr, newlen); - return ok; -} - -static JSBool -array_forEach(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, FOREACH); -} - -static JSBool -array_map(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, MAP); -} - -static JSBool -array_filter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, FILTER); -} - -static JSBool -array_some(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, SOME); -} - -static JSBool -array_every(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return array_extra(cx, obj, argc, argv, rval, EVERY); -} -#endif - -static JSFunctionSpec array_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, array_toSource, 0,0,0}, -#endif - {js_toString_str, array_toString, 0,0,0}, - {js_toLocaleString_str, array_toLocaleString, 0,0,0}, - - /* Perl-ish methods. */ - {"join", array_join, 1,JSFUN_GENERIC_NATIVE,0}, - {"reverse", array_reverse, 0,JSFUN_GENERIC_NATIVE,2}, - {"sort", array_sort, 1,JSFUN_GENERIC_NATIVE,2}, - {"push", array_push, 1,JSFUN_GENERIC_NATIVE,0}, - {"pop", array_pop, 0,JSFUN_GENERIC_NATIVE,0}, - {"shift", array_shift, 0,JSFUN_GENERIC_NATIVE,1}, - {"unshift", array_unshift, 1,JSFUN_GENERIC_NATIVE,1}, - {"splice", array_splice, 2,JSFUN_GENERIC_NATIVE,1}, - - /* Python-esque sequence methods. */ - {"concat", array_concat, 1,JSFUN_GENERIC_NATIVE,1}, - {"slice", array_slice, 2,JSFUN_GENERIC_NATIVE,1}, - -#if JS_HAS_ARRAY_EXTRAS - {"indexOf", array_indexOf, 1,JSFUN_GENERIC_NATIVE,0}, - {"lastIndexOf", array_lastIndexOf, 1,JSFUN_GENERIC_NATIVE,0}, - {"forEach", array_forEach, 1,JSFUN_GENERIC_NATIVE,2}, - {"map", array_map, 1,JSFUN_GENERIC_NATIVE,2}, - {"filter", array_filter, 1,JSFUN_GENERIC_NATIVE,2}, - {"some", array_some, 1,JSFUN_GENERIC_NATIVE,2}, - {"every", array_every, 1,JSFUN_GENERIC_NATIVE,2}, -#endif - - {0,0,0,0,0} -}; - -static JSBool -Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsuint length; - jsval *vector; - - /* If called without new, replace obj with a new Array object. */ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - - if (argc == 0) { - length = 0; - vector = NULL; - } else if (argc > 1) { - length = (jsuint) argc; - vector = argv; - } else if (!JSVAL_IS_NUMBER(argv[0])) { - length = 1; - vector = argv; - } else { - if (!ValueIsLength(cx, argv[0], &length)) - return JS_FALSE; - vector = NULL; - } - return InitArrayObject(cx, obj, length, vector); -} - -JSObject * -js_InitArrayClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_ArrayClass, Array, 1, - NULL, array_methods, NULL, NULL); - - /* Initialize the Array prototype object so it gets a length property. */ - if (!proto || !InitArrayObject(cx, proto, 0, NULL)) - return NULL; - return proto; -} - -JSObject * -js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector) -{ - JSTempValueRooter tvr; - JSObject *obj; - - obj = js_NewObject(cx, &js_ArrayClass, NULL, NULL); - if (!obj) - return NULL; - - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - if (!InitArrayObject(cx, obj, length, vector)) - obj = NULL; - JS_POP_TEMP_ROOT(cx, &tvr); - - /* Set/clear newborn root, in case we lost it. */ - cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; - return obj; -} diff --git a/spidermonkey/libjs/jsarray.h b/spidermonkey/libjs/jsarray.h deleted file mode 100644 index a89561b..0000000 --- a/spidermonkey/libjs/jsarray.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsarray_h___ -#define jsarray_h___ -/* - * JS Array interface. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* Generous sanity-bound on length (in elements) of array initialiser. */ -#define ARRAY_INIT_LIMIT JS_BIT(24) - -extern JSBool -js_IdIsIndex(jsval id, jsuint *indexp); - -extern JSClass js_ArrayClass; - -extern JSObject * -js_InitArrayClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_NewArrayObject(JSContext *cx, jsuint length, jsval *vector); - -extern JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); - -extern JSBool -js_SetLengthProperty(JSContext *cx, JSObject *obj, jsuint length); - -extern JSBool -js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); - -/* - * Test whether an object is "array-like". Currently this means whether obj - * is an Array or an arguments object. We would like an API, and probably a - * way in the language, to bless other objects as array-like: having indexed - * properties, and a 'length' property of uint32 value equal to one more than - * the greatest index. - */ -extern JSBool -js_IsArrayLike(JSContext *cx, JSObject *obj, JSBool *answerp, jsuint *lengthp); - -/* - * JS-specific heap sort function. - */ -typedef JSBool (*JSComparator)(void *arg, const void *a, const void *b, - int *result); - -extern JSBool -js_HeapSort(void *vec, size_t nel, void *pivot, size_t elsize, - JSComparator cmp, void *arg); - -JS_END_EXTERN_C - -#endif /* jsarray_h___ */ diff --git a/spidermonkey/libjs/jsatom.c b/spidermonkey/libjs/jsatom.c deleted file mode 100644 index 02ee250..0000000 --- a/spidermonkey/libjs/jsatom.c +++ /dev/null @@ -1,999 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS atom table. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsscan.h" -#include "jsstr.h" - -JS_FRIEND_API(const char *) -js_AtomToPrintableString(JSContext *cx, JSAtom *atom) -{ - return js_ValueToPrintableString(cx, ATOM_KEY(atom)); -} - -/* - * Keep this in sync with jspubtd.h -- an assertion below will insist that - * its length match the JSType enum's JSTYPE_LIMIT limit value. - */ -const char *js_type_strs[] = { - "undefined", - js_object_str, - "function", - "string", - "number", - "boolean", - "null", - "xml", -}; - -JS_STATIC_ASSERT(JSTYPE_LIMIT == - sizeof js_type_strs / sizeof js_type_strs[0]); - -const char *js_boolean_strs[] = { - js_false_str, - js_true_str -}; - -#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name; -#include "jsproto.tbl" -#undef JS_PROTO - -const char *js_proto_strs[JSProto_LIMIT] = { -#define JS_PROTO(name,code,init) js_##name##_str, -#include "jsproto.tbl" -#undef JS_PROTO -}; - -const char js_anonymous_str[] = "anonymous"; -const char js_arguments_str[] = "arguments"; -const char js_arity_str[] = "arity"; -const char js_callee_str[] = "callee"; -const char js_caller_str[] = "caller"; -const char js_class_prototype_str[] = "prototype"; -const char js_constructor_str[] = "constructor"; -const char js_count_str[] = "__count__"; -const char js_each_str[] = "each"; -const char js_eval_str[] = "eval"; -const char js_fileName_str[] = "fileName"; -const char js_get_str[] = "get"; -const char js_getter_str[] = "getter"; -const char js_index_str[] = "index"; -const char js_input_str[] = "input"; -const char js_iterator_str[] = "__iterator__"; -const char js_length_str[] = "length"; -const char js_lineNumber_str[] = "lineNumber"; -const char js_message_str[] = "message"; -const char js_name_str[] = "name"; -const char js_next_str[] = "next"; -const char js_noSuchMethod_str[] = "__noSuchMethod__"; -const char js_object_str[] = "object"; -const char js_parent_str[] = "__parent__"; -const char js_proto_str[] = "__proto__"; -const char js_setter_str[] = "setter"; -const char js_set_str[] = "set"; -const char js_stack_str[] = "stack"; -const char js_toSource_str[] = "toSource"; -const char js_toString_str[] = "toString"; -const char js_toLocaleString_str[] = "toLocaleString"; -const char js_valueOf_str[] = "valueOf"; - -#if JS_HAS_XML_SUPPORT -const char js_etago_str[] = ""; -const char js_qualifier_str[] = "::"; -const char js_space_str[] = " "; -const char js_stago_str[] = "<"; -const char js_star_str[] = "*"; -const char js_starQualifier_str[] = "*::"; -const char js_tagc_str[] = ">"; -const char js_xml_str[] = "xml"; -#endif - -#if JS_HAS_GENERATORS -const char js_close_str[] = "close"; -const char js_send_str[] = "send"; -#endif - -#ifdef NARCISSUS -const char js_call_str[] = "__call__"; -const char js_construct_str[] = "__construct__"; -const char js_hasInstance_str[] = "__hasInstance__"; -const char js_ExecutionContext_str[] = "ExecutionContext"; -const char js_current_str[] = "current"; -#endif - -#define HASH_OBJECT(o) (JS_PTR_TO_UINT32(o) >> JSVAL_TAGBITS) -#define HASH_INT(i) ((JSHashNumber)(i)) -#define HASH_DOUBLE(dp) ((JSDOUBLE_HI32(*dp) ^ JSDOUBLE_LO32(*dp))) -#define HASH_BOOLEAN(b) ((JSHashNumber)(b)) - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_atom_key(const void *key) -{ - jsval v; - jsdouble *dp; - - /* Order JSVAL_IS_* tests by likelihood of success. */ - v = (jsval)key; - if (JSVAL_IS_STRING(v)) - return js_HashString(JSVAL_TO_STRING(v)); - if (JSVAL_IS_INT(v)) - return HASH_INT(JSVAL_TO_INT(v)); - if (JSVAL_IS_DOUBLE(v)) { - dp = JSVAL_TO_DOUBLE(v); - return HASH_DOUBLE(dp); - } - if (JSVAL_IS_OBJECT(v)) - return HASH_OBJECT(JSVAL_TO_OBJECT(v)); - if (JSVAL_IS_BOOLEAN(v)) - return HASH_BOOLEAN(JSVAL_TO_BOOLEAN(v)); - return (JSHashNumber)v; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_compare_atom_keys(const void *k1, const void *k2) -{ - jsval v1, v2; - - v1 = (jsval)k1, v2 = (jsval)k2; - if (JSVAL_IS_STRING(v1) && JSVAL_IS_STRING(v2)) - return js_EqualStrings(JSVAL_TO_STRING(v1), JSVAL_TO_STRING(v2)); - if (JSVAL_IS_DOUBLE(v1) && JSVAL_IS_DOUBLE(v2)) { - double d1 = *JSVAL_TO_DOUBLE(v1); - double d2 = *JSVAL_TO_DOUBLE(v2); - if (JSDOUBLE_IS_NaN(d1)) - return JSDOUBLE_IS_NaN(d2); -#if defined(XP_WIN) - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - return JS_FALSE; -#endif - return d1 == d2; - } - return v1 == v2; -} - -JS_STATIC_DLL_CALLBACK(int) -js_compare_stub(const void *v1, const void *v2) -{ - return 1; -} - -/* These next two are exported to jsscript.c and used similarly there. */ -void * JS_DLL_CALLBACK -js_alloc_table_space(void *priv, size_t size) -{ - return malloc(size); -} - -void JS_DLL_CALLBACK -js_free_table_space(void *priv, void *item) -{ - free(item); -} - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_atom(void *priv, const void *key) -{ - JSAtomState *state = (JSAtomState *) priv; - JSAtom *atom; - - atom = (JSAtom *) malloc(sizeof(JSAtom)); - if (!atom) - return NULL; -#ifdef JS_THREADSAFE - state->tablegen++; -#endif - atom->entry.key = key; - atom->entry.value = NULL; - atom->flags = 0; - atom->number = state->number++; - return &atom->entry; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_atom(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; -#ifdef JS_THREADSAFE - ((JSAtomState *)priv)->tablegen++; -#endif - free(he); -} - -static JSHashAllocOps atom_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_atom, js_free_atom -}; - -#define JS_ATOM_HASH_SIZE 1024 - -JSBool -js_InitAtomState(JSContext *cx, JSAtomState *state) -{ - state->table = JS_NewHashTable(JS_ATOM_HASH_SIZE, js_hash_atom_key, - js_compare_atom_keys, js_compare_stub, - &atom_alloc_ops, state); - if (!state->table) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - state->runtime = cx->runtime; -#ifdef JS_THREADSAFE - js_InitLock(&state->lock); - state->tablegen = 0; -#endif - - if (!js_InitPinnedAtoms(cx, state)) { - js_FreeAtomState(cx, state); - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_InitPinnedAtoms(JSContext *cx, JSAtomState *state) -{ - uintN i; - -#define FROB(lval,str) \ - JS_BEGIN_MACRO \ - if (!(state->lval = js_Atomize(cx, str, strlen(str), ATOM_PINNED))) \ - return JS_FALSE; \ - JS_END_MACRO - - for (i = 0; i < JSTYPE_LIMIT; i++) - FROB(typeAtoms[i], js_type_strs[i]); - - for (i = 0; i < JSProto_LIMIT; i++) - FROB(classAtoms[i], js_proto_strs[i]); - - FROB(booleanAtoms[0], js_false_str); - FROB(booleanAtoms[1], js_true_str); - FROB(nullAtom, js_null_str); - - FROB(anonymousAtom, js_anonymous_str); - FROB(argumentsAtom, js_arguments_str); - FROB(arityAtom, js_arity_str); - FROB(calleeAtom, js_callee_str); - FROB(callerAtom, js_caller_str); - FROB(classPrototypeAtom, js_class_prototype_str); - FROB(constructorAtom, js_constructor_str); - FROB(countAtom, js_count_str); - FROB(eachAtom, js_each_str); - FROB(evalAtom, js_eval_str); - FROB(fileNameAtom, js_fileName_str); - FROB(getAtom, js_get_str); - FROB(getterAtom, js_getter_str); - FROB(indexAtom, js_index_str); - FROB(inputAtom, js_input_str); - FROB(iteratorAtom, js_iterator_str); - FROB(lengthAtom, js_length_str); - FROB(lineNumberAtom, js_lineNumber_str); - FROB(messageAtom, js_message_str); - FROB(nameAtom, js_name_str); - FROB(nextAtom, js_next_str); - FROB(noSuchMethodAtom, js_noSuchMethod_str); - FROB(parentAtom, js_parent_str); - FROB(protoAtom, js_proto_str); - FROB(setAtom, js_set_str); - FROB(setterAtom, js_setter_str); - FROB(stackAtom, js_stack_str); - FROB(toSourceAtom, js_toSource_str); - FROB(toStringAtom, js_toString_str); - FROB(toLocaleStringAtom, js_toLocaleString_str); - FROB(valueOfAtom, js_valueOf_str); - -#if JS_HAS_XML_SUPPORT - FROB(etagoAtom, js_etago_str); - FROB(namespaceAtom, js_namespace_str); - FROB(ptagcAtom, js_ptagc_str); - FROB(qualifierAtom, js_qualifier_str); - FROB(spaceAtom, js_space_str); - FROB(stagoAtom, js_stago_str); - FROB(starAtom, js_star_str); - FROB(starQualifierAtom, js_starQualifier_str); - FROB(tagcAtom, js_tagc_str); - FROB(xmlAtom, js_xml_str); -#endif - -#if JS_HAS_GENERATORS - FROB(closeAtom, js_close_str); -#endif - -#ifdef NARCISSUS - FROB(callAtom, js_call_str); - FROB(constructAtom, js_construct_str); - FROB(hasInstanceAtom, js_hasInstance_str); - FROB(ExecutionContextAtom, js_ExecutionContext_str); - FROB(currentAtom, js_current_str); -#endif - -#undef FROB - - memset(&state->lazy, 0, sizeof state->lazy); - return JS_TRUE; -} - -/* NB: cx unused; js_FinishAtomState calls us with null cx. */ -void -js_FreeAtomState(JSContext *cx, JSAtomState *state) -{ - if (state->table) - JS_HashTableDestroy(state->table); -#ifdef JS_THREADSAFE - js_FinishLock(&state->lock); -#endif - memset(state, 0, sizeof *state); -} - -typedef struct UninternArgs { - JSRuntime *rt; - jsatomid leaks; -} UninternArgs; - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_uninterner(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - UninternArgs *args; - - atom = (JSAtom *)he; - args = (UninternArgs *)arg; - if (ATOM_IS_STRING(atom)) - js_FinalizeStringRT(args->rt, ATOM_TO_STRING(atom)); - else if (ATOM_IS_OBJECT(atom)) - args->leaks++; - return HT_ENUMERATE_NEXT; -} - -void -js_FinishAtomState(JSAtomState *state) -{ - UninternArgs args; - - if (!state->table) - return; - args.rt = state->runtime; - args.leaks = 0; - JS_HashTableEnumerateEntries(state->table, js_atom_uninterner, &args); -#ifdef DEBUG - if (args.leaks != 0) { - fprintf(stderr, -"JS engine warning: %lu atoms remain after destroying the JSRuntime.\n" -" These atoms may point to freed memory. Things reachable\n" -" through them have not been finalized.\n", - (unsigned long) args.leaks); - } -#endif - js_FreeAtomState(NULL, state); -} - -typedef struct MarkArgs { - JSBool keepAtoms; - JSGCThingMarker mark; - void *data; -} MarkArgs; - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_marker(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - MarkArgs *args; - jsval key; - - atom = (JSAtom *)he; - args = (MarkArgs *)arg; - if ((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) || args->keepAtoms) { - atom->flags |= ATOM_MARK; - key = ATOM_KEY(atom); - if (JSVAL_IS_GCTHING(key)) - args->mark(JSVAL_TO_GCTHING(key), args->data); - } - return HT_ENUMERATE_NEXT; -} - -void -js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, - void *data) -{ - MarkArgs args; - - if (!state->table) - return; - args.keepAtoms = keepAtoms; - args.mark = mark; - args.data = data; - JS_HashTableEnumerateEntries(state->table, js_atom_marker, &args); -} - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_sweeper(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - JSAtomState *state; - - atom = (JSAtom *)he; - if (atom->flags & ATOM_MARK) { - atom->flags &= ~ATOM_MARK; - state = (JSAtomState *)arg; - state->liveAtoms++; - return HT_ENUMERATE_NEXT; - } - JS_ASSERT((atom->flags & (ATOM_PINNED | ATOM_INTERNED)) == 0); - atom->entry.key = atom->entry.value = NULL; - atom->flags = 0; - return HT_ENUMERATE_REMOVE; -} - -void -js_SweepAtomState(JSAtomState *state) -{ - state->liveAtoms = 0; - if (state->table) - JS_HashTableEnumerateEntries(state->table, js_atom_sweeper, state); -} - -JS_STATIC_DLL_CALLBACK(intN) -js_atom_unpinner(JSHashEntry *he, intN i, void *arg) -{ - JSAtom *atom; - - atom = (JSAtom *)he; - atom->flags &= ~ATOM_PINNED; - return HT_ENUMERATE_NEXT; -} - -void -js_UnpinPinnedAtoms(JSAtomState *state) -{ - if (state->table) - JS_HashTableEnumerateEntries(state->table, js_atom_unpinner, NULL); -} - -static JSAtom * -js_AtomizeHashedKey(JSContext *cx, jsval key, JSHashNumber keyHash, uintN flags) -{ - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - atom = NULL; - goto out; - } - } - - atom = (JSAtom *)he; - atom->flags |= flags; - cx->weakRoots.lastAtom = atom; -out: - JS_UNLOCK(&state->lock,cx); - return atom; -} - -JSAtom * -js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags) -{ - jsval key; - JSHashNumber keyHash; - - /* XXX must be set in the following order or MSVC1.52 will crash */ - keyHash = HASH_OBJECT(obj); - key = OBJECT_TO_JSVAL(obj); - return js_AtomizeHashedKey(cx, key, keyHash, flags); -} - -JSAtom * -js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags) -{ - jsval key; - JSHashNumber keyHash; - - key = BOOLEAN_TO_JSVAL(b); - keyHash = HASH_BOOLEAN(b); - return js_AtomizeHashedKey(cx, key, keyHash, flags); -} - -JSAtom * -js_AtomizeInt(JSContext *cx, jsint i, uintN flags) -{ - jsval key; - JSHashNumber keyHash; - - key = INT_TO_JSVAL(i); - keyHash = HASH_INT(i); - return js_AtomizeHashedKey(cx, key, keyHash, flags); -} - -/* Worst-case alignment grain and aligning macro for 2x-sized buffer. */ -#define ALIGNMENT(t) JS_MAX(JSVAL_ALIGN, sizeof(t)) -#define ALIGN(b,t) ((t*) &(b)[ALIGNMENT(t) - (jsuword)(b) % ALIGNMENT(t)]) - -JSAtom * -js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags) -{ - jsdouble *dp; - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - char buf[2 * ALIGNMENT(double)]; - - dp = ALIGN(buf, double); - *dp = d; - keyHash = HASH_DOUBLE(dp); - key = DOUBLE_TO_JSVAL(dp); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { -#ifdef JS_THREADSAFE - uint32 gen = state->tablegen; -#endif - JS_UNLOCK(&state->lock,cx); - if (!js_NewDoubleValue(cx, d, &key)) - return NULL; - JS_LOCK(&state->lock, cx); -#ifdef JS_THREADSAFE - if (state->tablegen != gen) { - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) != NULL) { - atom = (JSAtom *)he; - goto out; - } - } -#endif - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - atom = NULL; - goto out; - } - } - - atom = (JSAtom *)he; - atom->flags |= flags; - cx->weakRoots.lastAtom = atom; -out: - JS_UNLOCK(&state->lock,cx); - return atom; -} - -/* - * To put an atom into the hidden subspace. XOR its keyHash with this value, - * which is (sqrt(2)-1) in 32-bit fixed point. - */ -#define HIDDEN_ATOM_SUBSPACE_KEYHASH 0x6A09E667 - -JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags) -{ - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry *he, **hep; - JSAtom *atom; - - keyHash = js_HashString(str); - if (flags & ATOM_HIDDEN) - keyHash ^= HIDDEN_ATOM_SUBSPACE_KEYHASH; - key = STRING_TO_JSVAL(str); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) == NULL) { -#ifdef JS_THREADSAFE - uint32 gen = state->tablegen; - JS_UNLOCK(&state->lock, cx); -#endif - - if (flags & ATOM_TMPSTR) { - str = (flags & ATOM_NOCOPY) - ? js_NewString(cx, str->chars, str->length, 0) - : js_NewStringCopyN(cx, str->chars, str->length, 0); - if (!str) - return NULL; - key = STRING_TO_JSVAL(str); - } else { - if (!JS_MakeStringImmutable(cx, str)) - return NULL; - } - -#ifdef JS_THREADSAFE - JS_LOCK(&state->lock, cx); - if (state->tablegen != gen) { - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - if ((he = *hep) != NULL) { - atom = (JSAtom *)he; - if (flags & ATOM_NOCOPY) - str->chars = NULL; - goto out; - } - } -#endif - - he = JS_HashTableRawAdd(table, hep, keyHash, (void *)key, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - atom = NULL; - goto out; - } - } - - atom = (JSAtom *)he; - atom->flags |= flags & (ATOM_PINNED | ATOM_INTERNED | ATOM_HIDDEN); - cx->weakRoots.lastAtom = atom; -out: - JS_UNLOCK(&state->lock,cx); - return atom; -} - -JS_FRIEND_API(JSAtom *) -js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) -{ - jschar *chars; - JSString *str; - JSAtom *atom; - char buf[2 * ALIGNMENT(JSString)]; - - /* - * Avoiding the malloc in js_InflateString on shorter strings saves us - * over 20,000 malloc calls on mozilla browser startup. This compares to - * only 131 calls where the string is longer than a 31 char (net) buffer. - * The vast majority of atomized strings are already in the hashtable. So - * js_AtomizeString rarely has to copy the temp string we make. - */ -#define ATOMIZE_BUF_MAX 32 - jschar inflated[ATOMIZE_BUF_MAX]; - size_t inflatedLength = ATOMIZE_BUF_MAX - 1; - - if (length < ATOMIZE_BUF_MAX) { - js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); - inflated[inflatedLength] = 0; - chars = inflated; - } else { - inflatedLength = length; - chars = js_InflateString(cx, bytes, &inflatedLength); - if (!chars) - return NULL; - flags |= ATOM_NOCOPY; - } - - str = ALIGN(buf, JSString); - - str->chars = chars; - str->length = inflatedLength; - atom = js_AtomizeString(cx, str, ATOM_TMPSTR | flags); - if (chars != inflated && (!atom || ATOM_TO_STRING(atom)->chars != chars)) - JS_free(cx, chars); - return atom; -} - -JS_FRIEND_API(JSAtom *) -js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) -{ - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; - - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - return js_AtomizeString(cx, str, ATOM_TMPSTR | flags); -} - -JSAtom * -js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) -{ - JSString *str; - char buf[2 * ALIGNMENT(JSString)]; - JSHashNumber keyHash; - jsval key; - JSAtomState *state; - JSHashTable *table; - JSHashEntry **hep; - - str = ALIGN(buf, JSString); - str->chars = (jschar *)chars; - str->length = length; - keyHash = js_HashString(str); - key = STRING_TO_JSVAL(str); - state = &cx->runtime->atomState; - JS_LOCK(&state->lock, cx); - table = state->table; - hep = JS_HashTableRawLookup(table, keyHash, (void *)key); - JS_UNLOCK(&state->lock, cx); - return (hep) ? (JSAtom *)*hep : NULL; -} - -JSAtom * -js_AtomizeValue(JSContext *cx, jsval value, uintN flags) -{ - if (JSVAL_IS_STRING(value)) - return js_AtomizeString(cx, JSVAL_TO_STRING(value), flags); - if (JSVAL_IS_INT(value)) - return js_AtomizeInt(cx, JSVAL_TO_INT(value), flags); - if (JSVAL_IS_DOUBLE(value)) - return js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(value), flags); - if (JSVAL_IS_OBJECT(value)) - return js_AtomizeObject(cx, JSVAL_TO_OBJECT(value), flags); - if (JSVAL_IS_BOOLEAN(value)) - return js_AtomizeBoolean(cx, JSVAL_TO_BOOLEAN(value), flags); - return js_AtomizeHashedKey(cx, value, (JSHashNumber)value, flags); -} - -JSAtom * -js_ValueToStringAtom(JSContext *cx, jsval v) -{ - JSString *str; - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - return js_AtomizeString(cx, str, 0); -} - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_atom_ptr(const void *key) -{ - const JSAtom *atom = key; - return atom->number; -} - -JS_STATIC_DLL_CALLBACK(void *) -js_alloc_temp_space(void *priv, size_t size) -{ - JSContext *cx = priv; - void *space; - - JS_ARENA_ALLOCATE(space, &cx->tempPool, size); - if (!space) - JS_ReportOutOfMemory(cx); - return space; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_temp_space(void *priv, void *item) -{ -} - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_temp_entry(void *priv, const void *key) -{ - JSContext *cx = priv; - JSAtomListElement *ale; - - JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); - if (!ale) { - JS_ReportOutOfMemory(cx); - return NULL; - } - return &ale->entry; -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) -{ -} - -static JSHashAllocOps temp_alloc_ops = { - js_alloc_temp_space, js_free_temp_space, - js_alloc_temp_entry, js_free_temp_entry -}; - -JSAtomListElement * -js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) -{ - JSAtomListElement *ale, *ale2, *next; - JSHashEntry **hep; - - ATOM_LIST_LOOKUP(ale, hep, al, atom); - if (!ale) { - if (al->count < 10) { - /* Few enough for linear search, no hash table needed. */ - JS_ASSERT(!al->table); - ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom); - if (!ale) - return NULL; - ALE_SET_ATOM(ale, atom); - ALE_SET_NEXT(ale, al->list); - al->list = ale; - } else { - /* We want to hash. Have we already made a hash table? */ - if (!al->table) { - /* No hash table yet, so hep had better be null! */ - JS_ASSERT(!hep); - al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr, - JS_CompareValues, JS_CompareValues, - &temp_alloc_ops, cx); - if (!al->table) - return NULL; - - /* - * Set ht->nentries explicitly, because we are moving entries - * from al to ht, not calling JS_HashTable(Raw|)Add. - */ - al->table->nentries = al->count; - - /* Insert each ale on al->list into the new hash table. */ - for (ale2 = al->list; ale2; ale2 = next) { - next = ALE_NEXT(ale2); - ale2->entry.keyHash = ALE_ATOM(ale2)->number; - hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash, - ale2->entry.key); - ALE_SET_NEXT(ale2, *hep); - *hep = &ale2->entry; - } - al->list = NULL; - - /* Set hep for insertion of atom's ale, immediately below. */ - hep = JS_HashTableRawLookup(al->table, atom->number, atom); - } - - /* Finally, add an entry for atom into the hash bucket at hep. */ - ale = (JSAtomListElement *) - JS_HashTableRawAdd(al->table, hep, atom->number, atom, NULL); - if (!ale) - return NULL; - } - - ALE_SET_INDEX(ale, al->count++); - } - return ale; -} - -JS_FRIEND_API(JSAtom *) -js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i) -{ - JSAtom *atom; - static JSAtom dummy; - - JS_ASSERT(map->vector && i < map->length); - if (!map->vector || i >= map->length) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lu", (unsigned long)i); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ATOMIC_NUMBER, numBuf); - return &dummy; - } - atom = map->vector[i]; - JS_ASSERT(atom); - return atom; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_map_atom(JSHashEntry *he, intN i, void *arg) -{ - JSAtomListElement *ale = (JSAtomListElement *)he; - JSAtom **vector = arg; - - vector[ALE_INDEX(ale)] = ALE_ATOM(ale); - return HT_ENUMERATE_NEXT; -} - -#ifdef DEBUG -static jsrefcount js_atom_map_count; -static jsrefcount js_atom_map_hash_table_count; -#endif - -JS_FRIEND_API(JSBool) -js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) -{ - JSAtom **vector; - JSAtomListElement *ale; - uint32 count; - -#ifdef DEBUG - JS_ATOMIC_INCREMENT(&js_atom_map_count); -#endif - ale = al->list; - if (!ale && !al->table) { - map->vector = NULL; - map->length = 0; - return JS_TRUE; - } - - count = al->count; - if (count >= ATOM_INDEX_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_LITERALS); - return JS_FALSE; - } - vector = (JSAtom **) JS_malloc(cx, (size_t) count * sizeof *vector); - if (!vector) - return JS_FALSE; - - if (al->table) { -#ifdef DEBUG - JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count); -#endif - JS_HashTableEnumerateEntries(al->table, js_map_atom, vector); - } else { - do { - vector[ALE_INDEX(ale)] = ALE_ATOM(ale); - } while ((ale = ALE_NEXT(ale)) != NULL); - } - ATOM_LIST_INIT(al); - - map->vector = vector; - map->length = (jsatomid)count; - return JS_TRUE; -} - -JS_FRIEND_API(void) -js_FreeAtomMap(JSContext *cx, JSAtomMap *map) -{ - if (map->vector) { - JS_free(cx, map->vector); - map->vector = NULL; - } - map->length = 0; -} diff --git a/spidermonkey/libjs/jsatom.h b/spidermonkey/libjs/jsatom.h deleted file mode 100644 index 4fb3d8d..0000000 --- a/spidermonkey/libjs/jsatom.h +++ /dev/null @@ -1,456 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsatom_h___ -#define jsatom_h___ -/* - * JS atom table. - */ -#include -#include "jstypes.h" -#include "jshash.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -#ifdef JS_THREADSAFE -#include "jslock.h" -#endif - -JS_BEGIN_EXTERN_C - -#define ATOM_PINNED 0x01 /* atom is pinned against GC */ -#define ATOM_INTERNED 0x02 /* pinned variant for JS_Intern* API */ -#define ATOM_MARK 0x04 /* atom is reachable via GC */ -#define ATOM_HIDDEN 0x08 /* atom is in special hidden subspace */ -#define ATOM_NOCOPY 0x40 /* don't copy atom string bytes */ -#define ATOM_TMPSTR 0x80 /* internal, to avoid extra string */ - -struct JSAtom { - JSHashEntry entry; /* key is jsval or unhidden atom - if ATOM_HIDDEN */ - uint32 flags; /* pinned, interned, and mark flags */ - jsatomid number; /* atom serial number and hash code */ -}; - -#define ATOM_KEY(atom) ((jsval)(atom)->entry.key) -#define ATOM_IS_OBJECT(atom) JSVAL_IS_OBJECT(ATOM_KEY(atom)) -#define ATOM_TO_OBJECT(atom) JSVAL_TO_OBJECT(ATOM_KEY(atom)) -#define ATOM_IS_INT(atom) JSVAL_IS_INT(ATOM_KEY(atom)) -#define ATOM_TO_INT(atom) JSVAL_TO_INT(ATOM_KEY(atom)) -#define ATOM_IS_DOUBLE(atom) JSVAL_IS_DOUBLE(ATOM_KEY(atom)) -#define ATOM_TO_DOUBLE(atom) JSVAL_TO_DOUBLE(ATOM_KEY(atom)) -#define ATOM_IS_STRING(atom) JSVAL_IS_STRING(ATOM_KEY(atom)) -#define ATOM_TO_STRING(atom) JSVAL_TO_STRING(ATOM_KEY(atom)) -#define ATOM_IS_BOOLEAN(atom) JSVAL_IS_BOOLEAN(ATOM_KEY(atom)) -#define ATOM_TO_BOOLEAN(atom) JSVAL_TO_BOOLEAN(ATOM_KEY(atom)) - -/* - * Return a printable, lossless char[] representation of a string-type atom. - * The lifetime of the result extends at least until the next GC activation, - * longer if cx's string newborn root is not overwritten. - */ -extern JS_FRIEND_API(const char *) -js_AtomToPrintableString(JSContext *cx, JSAtom *atom); - -struct JSAtomListElement { - JSHashEntry entry; -}; - -#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) -#define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value)) -#define ALE_JSOP(ale) ((JSOp) (ale)->entry.value) -#define ALE_VALUE(ale) ((jsval) (ale)->entry.value) -#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) - -#define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) -#define ALE_SET_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index)) -#define ALE_SET_JSOP(ale,op) ((ale)->entry.value = JS_UINT32_TO_PTR(op)) -#define ALE_SET_VALUE(ale,val) ((ale)->entry.value = (JSHashEntry *)(val)) -#define ALE_SET_NEXT(ale,link) ((ale)->entry.next = (JSHashEntry *)(link)) - -struct JSAtomList { - JSAtomListElement *list; /* literals indexed for mapping */ - JSHashTable *table; /* hash table if list gets too long */ - jsuint count; /* count of indexed literals */ -}; - -#define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->table = NULL, \ - (al)->count = 0) - -#define ATOM_LIST_SEARCH(_ale,_al,_atom) \ - JS_BEGIN_MACRO \ - JSHashEntry **_hep; \ - ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom); \ - JS_END_MACRO - -#define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom) \ - JS_BEGIN_MACRO \ - if ((_al)->table) { \ - _hep = JS_HashTableRawLookup((_al)->table, _atom->number, _atom); \ - _ale = *_hep ? (JSAtomListElement *) *_hep : NULL; \ - } else { \ - JSAtomListElement **_alep = &(_al)->list; \ - _hep = NULL; \ - while ((_ale = *_alep) != NULL) { \ - if (ALE_ATOM(_ale) == (_atom)) { \ - /* Hit, move atom's element to the front of the list. */ \ - *_alep = ALE_NEXT(_ale); \ - ALE_SET_NEXT(_ale, (_al)->list); \ - (_al)->list = _ale; \ - break; \ - } \ - _alep = (JSAtomListElement **)&_ale->entry.next; \ - } \ - } \ - JS_END_MACRO - -struct JSAtomMap { - JSAtom **vector; /* array of ptrs to indexed atoms */ - jsatomid length; /* count of (to-be-)indexed atoms */ -}; - -struct JSAtomState { - JSRuntime *runtime; /* runtime that owns us */ - JSHashTable *table; /* hash table containing all atoms */ - jsatomid number; /* one beyond greatest atom number */ - jsatomid liveAtoms; /* number of live atoms after last GC */ - - /* The rt->emptyString atom, see jsstr.c's js_InitRuntimeStringState. */ - JSAtom *emptyAtom; - - /* Type names and value literals. */ - JSAtom *typeAtoms[JSTYPE_LIMIT]; - JSAtom *booleanAtoms[2]; - JSAtom *nullAtom; - - /* Standard class constructor or prototype names. */ - JSAtom *classAtoms[JSProto_LIMIT]; - - /* Various built-in or commonly-used atoms, pinned on first context. */ - JSAtom *anonymousAtom; - JSAtom *argumentsAtom; - JSAtom *arityAtom; - JSAtom *calleeAtom; - JSAtom *callerAtom; - JSAtom *classPrototypeAtom; - JSAtom *closeAtom; - JSAtom *constructorAtom; - JSAtom *countAtom; - JSAtom *eachAtom; - JSAtom *etagoAtom; - JSAtom *evalAtom; - JSAtom *fileNameAtom; - JSAtom *getAtom; - JSAtom *getterAtom; - JSAtom *indexAtom; - JSAtom *inputAtom; - JSAtom *iteratorAtom; - JSAtom *lengthAtom; - JSAtom *lineNumberAtom; - JSAtom *messageAtom; - JSAtom *nameAtom; - JSAtom *namespaceAtom; - JSAtom *nextAtom; - JSAtom *noSuchMethodAtom; - JSAtom *parentAtom; - JSAtom *protoAtom; - JSAtom *ptagcAtom; - JSAtom *qualifierAtom; - JSAtom *setAtom; - JSAtom *setterAtom; - JSAtom *spaceAtom; - JSAtom *stackAtom; - JSAtom *stagoAtom; - JSAtom *starAtom; - JSAtom *starQualifierAtom; - JSAtom *tagcAtom; - JSAtom *toLocaleStringAtom; - JSAtom *toSourceAtom; - JSAtom *toStringAtom; - JSAtom *valueOfAtom; - JSAtom *xmlAtom; - - /* Less frequently used atoms, pinned lazily by JS_ResolveStandardClass. */ - struct { - JSAtom *InfinityAtom; - JSAtom *NaNAtom; - JSAtom *XMLListAtom; - JSAtom *decodeURIAtom; - JSAtom *decodeURIComponentAtom; - JSAtom *defineGetterAtom; - JSAtom *defineSetterAtom; - JSAtom *encodeURIAtom; - JSAtom *encodeURIComponentAtom; - JSAtom *escapeAtom; - JSAtom *functionNamespaceURIAtom; - JSAtom *hasOwnPropertyAtom; - JSAtom *isFiniteAtom; - JSAtom *isNaNAtom; - JSAtom *isPrototypeOfAtom; - JSAtom *isXMLNameAtom; - JSAtom *lookupGetterAtom; - JSAtom *lookupSetterAtom; - JSAtom *parseFloatAtom; - JSAtom *parseIntAtom; - JSAtom *propertyIsEnumerableAtom; - JSAtom *unescapeAtom; - JSAtom *unevalAtom; - JSAtom *unwatchAtom; - JSAtom *watchAtom; - } lazy; - -#ifdef JS_THREADSAFE - JSThinLock lock; - volatile uint32 tablegen; -#endif -#ifdef NARCISSUS - JSAtom *callAtom; - JSAtom *constructAtom; - JSAtom *hasInstanceAtom; - JSAtom *ExecutionContextAtom; - JSAtom *currentAtom; -#endif -}; - -#define CLASS_ATOM(cx,name) \ - ((cx)->runtime->atomState.classAtoms[JSProto_##name]) - -/* Well-known predefined strings and their atoms. */ -extern const char *js_type_strs[]; -extern const char *js_boolean_strs[]; -extern const char *js_proto_strs[]; - -#define JS_PROTO(name,code,init) extern const char js_##name##_str[]; -#include "jsproto.tbl" -#undef JS_PROTO - -extern const char js_anonymous_str[]; -extern const char js_arguments_str[]; -extern const char js_arity_str[]; -extern const char js_callee_str[]; -extern const char js_caller_str[]; -extern const char js_class_prototype_str[]; -extern const char js_close_str[]; -extern const char js_constructor_str[]; -extern const char js_count_str[]; -extern const char js_etago_str[]; -extern const char js_each_str[]; -extern const char js_eval_str[]; -extern const char js_fileName_str[]; -extern const char js_get_str[]; -extern const char js_getter_str[]; -extern const char js_index_str[]; -extern const char js_input_str[]; -extern const char js_iterator_str[]; -extern const char js_length_str[]; -extern const char js_lineNumber_str[]; -extern const char js_message_str[]; -extern const char js_name_str[]; -extern const char js_namespace_str[]; -extern const char js_next_str[]; -extern const char js_noSuchMethod_str[]; -extern const char js_object_str[]; -extern const char js_parent_str[]; -extern const char js_private_str[]; -extern const char js_proto_str[]; -extern const char js_ptagc_str[]; -extern const char js_qualifier_str[]; -extern const char js_send_str[]; -extern const char js_setter_str[]; -extern const char js_set_str[]; -extern const char js_space_str[]; -extern const char js_stack_str[]; -extern const char js_stago_str[]; -extern const char js_star_str[]; -extern const char js_starQualifier_str[]; -extern const char js_tagc_str[]; -extern const char js_toSource_str[]; -extern const char js_toString_str[]; -extern const char js_toLocaleString_str[]; -extern const char js_valueOf_str[]; -extern const char js_xml_str[]; - -#ifdef NARCISSUS -extern const char js_call_str[]; -extern const char js_construct_str[]; -extern const char js_hasInstance_str[]; -extern const char js_ExecutionContext_str[]; -extern const char js_current_str[]; -#endif - -/* - * Initialize atom state. Return true on success, false with an out of - * memory error report on failure. - */ -extern JSBool -js_InitAtomState(JSContext *cx, JSAtomState *state); - -/* - * Free and clear atom state (except for any interned string atoms). - */ -extern void -js_FreeAtomState(JSContext *cx, JSAtomState *state); - -/* - * Interned strings are atoms that live until state's runtime is destroyed. - * This function frees all interned string atoms, and then frees and clears - * state's members (just as js_FreeAtomState does), unless there aren't any - * interned strings in state -- in which case state must be "free" already. - * - * NB: js_FreeAtomState is called for each "last" context being destroyed in - * a runtime, where there may yet be another context created in the runtime; - * whereas js_FinishAtomState is called from JS_DestroyRuntime, when we know - * that no more contexts will be created. Thus we minimize garbage during - * context-free episodes on a runtime, while preserving atoms created by the - * JS_Intern*String APIs for the life of the runtime. - */ -extern void -js_FinishAtomState(JSAtomState *state); - -/* - * Atom garbage collection hooks. - */ -typedef void -(*JSGCThingMarker)(void *thing, void *data); - -extern void -js_MarkAtomState(JSAtomState *state, JSBool keepAtoms, JSGCThingMarker mark, - void *data); - -extern void -js_SweepAtomState(JSAtomState *state); - -extern JSBool -js_InitPinnedAtoms(JSContext *cx, JSAtomState *state); - -extern void -js_UnpinPinnedAtoms(JSAtomState *state); - -/* - * Find or create the atom for an object. If we create a new atom, give it the - * type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeObject(JSContext *cx, JSObject *obj, uintN flags); - -/* - * Find or create the atom for a Boolean value. If we create a new atom, give - * it the type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeBoolean(JSContext *cx, JSBool b, uintN flags); - -/* - * Find or create the atom for an integer value. If we create a new atom, give - * it the type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeInt(JSContext *cx, jsint i, uintN flags); - -/* - * Find or create the atom for a double value. If we create a new atom, give - * it the type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeDouble(JSContext *cx, jsdouble d, uintN flags); - -/* - * Find or create the atom for a string. If we create a new atom, give it the - * type indicated in flags. Return 0 on failure to allocate memory. - */ -extern JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, uintN flags); - -extern JS_FRIEND_API(JSAtom *) -js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags); - -extern JS_FRIEND_API(JSAtom *) -js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags); - -/* - * Return an existing atom for the given char array or null if the char - * sequence is currently not atomized. - */ -extern JSAtom * -js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length); - -/* - * This variant handles all value tag types. - */ -extern JSAtom * -js_AtomizeValue(JSContext *cx, jsval value, uintN flags); - -/* - * Convert v to an atomized string. - */ -extern JSAtom * -js_ValueToStringAtom(JSContext *cx, jsval v); - -/* - * Assign atom an index and insert it on al. - */ -extern JSAtomListElement * -js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al); - -/* - * Get the atom with index i from map. - */ -extern JS_FRIEND_API(JSAtom *) -js_GetAtom(JSContext *cx, JSAtomMap *map, jsatomid i); - -/* - * For all unmapped atoms recorded in al, add a mapping from the atom's index - * to its address. The GC must not run until all indexed atoms in atomLists - * have been mapped by scripts connected to live objects (Function and Script - * class objects have scripts as/in their private data -- the GC knows about - * these two classes). - */ -extern JS_FRIEND_API(JSBool) -js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al); - -/* - * Free map->vector and clear map. - */ -extern JS_FRIEND_API(void) -js_FreeAtomMap(JSContext *cx, JSAtomMap *map); - -JS_END_EXTERN_C - -#endif /* jsatom_h___ */ diff --git a/spidermonkey/libjs/jsbit.h b/spidermonkey/libjs/jsbit.h deleted file mode 100644 index 87bb047..0000000 --- a/spidermonkey/libjs/jsbit.h +++ /dev/null @@ -1,195 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsbit_h___ -#define jsbit_h___ - -#include "jstypes.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* -** A jsbitmap_t is a long integer that can be used for bitmaps -*/ -typedef JSUword jsbitmap_t; /* NSPR name, a la Unix system types */ -typedef jsbitmap_t jsbitmap; /* JS-style scalar typedef name */ - -#define JS_TEST_BIT(_map,_bit) \ - ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] & (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) -#define JS_SET_BIT(_map,_bit) \ - ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_WORD-1)))) -#define JS_CLEAR_BIT(_map,_bit) \ - ((_map)[(_bit)>>JS_BITS_PER_WORD_LOG2] &= ~(1L << ((_bit) & (JS_BITS_PER_WORD-1)))) - -/* -** Compute the log of the least power of 2 greater than or equal to n -*/ -extern JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 i); - -/* -** Compute the log of the greatest power of 2 less than or equal to n -*/ -extern JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 i); - -/* - * Check if __builtin_clz is available which apeared first in GCC 3.4. - * The built-in allows to speedup calculations of ceiling/floor log2, - * see bug 327129. - */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# define JS_HAS_GCC_BUILTIN_CLZ -#endif - -/* -** Macro version of JS_CeilingLog2: Compute the log of the least power of -** 2 greater than or equal to _n. The result is returned in _log2. -*/ -#ifdef JS_HAS_GCC_BUILTIN_CLZ -/* - * Use __builtin_clz or count-leading-zeros to calculate ceil(log2(_n)). - * The macro checks for "n <= 1" and not "n != 0" as __builtin_clz(0) is - * undefined. - */ -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ - unsigned int j_ = (unsigned int)(_n); \ - (_log2) = (j_ <= 1 ? 0 : 32 - __builtin_clz(j_ - 1)); \ - JS_END_MACRO -#else -# define JS_CEILING_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JSUint32 j_ = (JSUint32)(_n); \ - (_log2) = 0; \ - if ((j_) & ((j_)-1)) \ - (_log2) += 1; \ - if ((j_) >> 16) \ - (_log2) += 16, (j_) >>= 16; \ - if ((j_) >> 8) \ - (_log2) += 8, (j_) >>= 8; \ - if ((j_) >> 4) \ - (_log2) += 4, (j_) >>= 4; \ - if ((j_) >> 2) \ - (_log2) += 2, (j_) >>= 2; \ - if ((j_) >> 1) \ - (_log2) += 1; \ - JS_END_MACRO -#endif - -/* -** Macro version of JS_FloorLog2: Compute the log of the greatest power of -** 2 less than or equal to _n. The result is returned in _log2. -** -** This is equivalent to finding the highest set bit in the word. -*/ -#if JS_GCC_HAS_BUILTIN_CLZ -/* - * Use __builtin_clz or count-leading-zeros to calculate floor(log2(_n)). - * Since __builtin_clz(0) is undefined, the macro set the loweset bit to 1 - * to ensure 0 result when _n == 0. - */ -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JS_STATIC_ASSERT(sizeof(unsigned int) == sizeof(JSUint32)); \ - (_log2) = 31 - __builtin_clz(((unsigned int)(_n)) | 1); \ - JS_END_MACRO -#else -# define JS_FLOOR_LOG2(_log2,_n) \ - JS_BEGIN_MACRO \ - JSUint32 j_ = (JSUint32)(_n); \ - (_log2) = 0; \ - if ((j_) >> 16) \ - (_log2) += 16, (j_) >>= 16; \ - if ((j_) >> 8) \ - (_log2) += 8, (j_) >>= 8; \ - if ((j_) >> 4) \ - (_log2) += 4, (j_) >>= 4; \ - if ((j_) >> 2) \ - (_log2) += 2, (j_) >>= 2; \ - if ((j_) >> 1) \ - (_log2) += 1; \ - JS_END_MACRO -#endif - -/* - * Internal function. - * Compute the log of the least power of 2 greater than or equal to n. - * This is a version of JS_CeilingLog2 that operates on jsuword with - * CPU-dependant size. - */ -#define JS_CEILING_LOG2W(n) ((n) <= 1 ? 0 : 1 + JS_FLOOR_LOG2W((n) - 1)) - -/* - * Internal function. - * Compute the log of the greatest power of 2 less than or equal to n. - * This is a version of JS_FloorLog2 that operates on jsuword with - * CPU-dependant size and requires that n != 0. - */ -#define JS_FLOOR_LOG2W(n) (JS_ASSERT((n) != 0), js_FloorLog2wImpl(n)) - -#ifdef JS_HAS_GCC_BUILTIN_CLZ - -# if JS_BYTES_PER_WORD == 4 -JS_STATIC_ASSERT(sizeof(unsigned) == sizeof(JSUword)); -# define js_FloorLog2wImpl(n) \ - ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clz(n))) -# elif JS_BYTES_PER_WORD == 8 -JS_STATIC_ASSERT(sizeof(unsigned long long) == sizeof(JSUword)); -# define js_FloorLog2wImpl(n) \ - ((JSUword)(JS_BITS_PER_WORD - 1 - __builtin_clzll(n))) -# else -# error "NOT SUPPORTED" -# endif - -#else - -# if JS_BYTES_PER_WORD == 4 -# define js_FloorLog2wImpl(n) ((JSUword)JS_FloorLog2(n)) -# elif JS_BYTES_PER_WORD == 8 -extern JSUword -js_FloorLog2wImpl(JSUword n); -# else -# error "NOT SUPPORTED" -# endif - -#endif - - -JS_END_EXTERN_C -#endif /* jsbit_h___ */ diff --git a/spidermonkey/libjs/jsbool.c b/spidermonkey/libjs/jsbool.c deleted file mode 100644 index 543b4f3..0000000 --- a/spidermonkey/libjs/jsbool.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS boolean implementation. - */ -#include "jsstddef.h" -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsstr.h" - -JSClass js_BooleanClass = { - "Boolean", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#if JS_HAS_TOSOURCE -#include "jsprf.h" - -static JSBool -bool_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - char buf[32]; - JSString *str; - - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_BOOLEAN(v)) - return js_obj_toSource(cx, obj, argc, argv, rval); - } - JS_snprintf(buf, sizeof buf, "(new %s(%s))", - js_BooleanClass.name, - js_boolean_strs[JSVAL_TO_BOOLEAN(v) ? 1 : 0]); - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -static JSBool -bool_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - JSAtom *atom; - JSString *str; - - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_BOOLEAN(v)) - return js_obj_toString(cx, obj, argc, argv, rval); - } - atom = cx->runtime->atomState.booleanAtoms[JSVAL_TO_BOOLEAN(v) ? 1 : 0]; - str = ATOM_TO_STRING(atom); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -bool_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (JSVAL_IS_BOOLEAN((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_BooleanClass, argv)) - return JS_FALSE; - *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - return JS_TRUE; -} - -static JSFunctionSpec boolean_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, bool_toSource, 0,JSFUN_THISP_BOOLEAN,0}, -#endif - {js_toString_str, bool_toString, 0,JSFUN_THISP_BOOLEAN,0}, - {js_valueOf_str, bool_valueOf, 0,JSFUN_THISP_BOOLEAN,0}, - {0,0,0,0,0} -}; - -static JSBool -Boolean(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool b; - jsval bval; - - if (argc != 0) { - if (!js_ValueToBoolean(cx, argv[0], &b)) - return JS_FALSE; - bval = BOOLEAN_TO_JSVAL(b); - } else { - bval = JSVAL_FALSE; - } - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = bval; - return JS_TRUE; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, bval); - return JS_TRUE; -} - -JSObject * -js_InitBooleanClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_BooleanClass, Boolean, 1, - NULL, boolean_methods, NULL, NULL); - if (!proto) - return NULL; - OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_FALSE); - return proto; -} - -JSObject * -js_BooleanToObject(JSContext *cx, JSBool b) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_BooleanClass, NULL, NULL); - if (!obj) - return NULL; - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, BOOLEAN_TO_JSVAL(b)); - return obj; -} - -JSString * -js_BooleanToString(JSContext *cx, JSBool b) -{ - return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]); -} - -JSBool -js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp) -{ - JSBool b; - jsdouble d; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - b = JS_FALSE; - } else if (JSVAL_IS_OBJECT(v)) { - if (!JS_VERSION_IS_ECMA(cx)) { - if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), JSTYPE_BOOLEAN, &v)) - return JS_FALSE; - if (!JSVAL_IS_BOOLEAN(v)) - v = JSVAL_TRUE; /* non-null object is true */ - b = JSVAL_TO_BOOLEAN(v); - } else { - b = JS_TRUE; - } - } else if (JSVAL_IS_STRING(v)) { - b = JSSTRING_LENGTH(JSVAL_TO_STRING(v)) ? JS_TRUE : JS_FALSE; - } else if (JSVAL_IS_INT(v)) { - b = JSVAL_TO_INT(v) ? JS_TRUE : JS_FALSE; - } else if (JSVAL_IS_DOUBLE(v)) { - d = *JSVAL_TO_DOUBLE(v); - b = (!JSDOUBLE_IS_NaN(d) && d != 0) ? JS_TRUE : JS_FALSE; - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); - b = JSVAL_TO_BOOLEAN(v); - } - - *bp = b; - return JS_TRUE; -} diff --git a/spidermonkey/libjs/jsbool.h b/spidermonkey/libjs/jsbool.h deleted file mode 100644 index 8dbd218..0000000 --- a/spidermonkey/libjs/jsbool.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsbool_h___ -#define jsbool_h___ -/* - * JS boolean interface. - */ - -JS_BEGIN_EXTERN_C - -/* - * Crypto-booleans, not visible to script but used internally by the engine. - * - * JSVAL_HOLE is a useful value for identifying a hole in an array. It's also - * used in the interpreter to represent "no exception pending". In general it - * can be used to represent "no value". - * - * JSVAL_ARETURN is used to throw asynchronous return for generator.close(). - */ -#define JSVAL_HOLE BOOLEAN_TO_JSVAL(2) -#define JSVAL_ARETURN BOOLEAN_TO_JSVAL(3) - -extern JSClass js_BooleanClass; - -extern JSObject * -js_InitBooleanClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_BooleanToObject(JSContext *cx, JSBool b); - -extern JSString * -js_BooleanToString(JSContext *cx, JSBool b); - -extern JSBool -js_ValueToBoolean(JSContext *cx, jsval v, JSBool *bp); - -JS_END_EXTERN_C - -#endif /* jsbool_h___ */ diff --git a/spidermonkey/libjs/jsclist.h b/spidermonkey/libjs/jsclist.h deleted file mode 100644 index 604ec0e..0000000 --- a/spidermonkey/libjs/jsclist.h +++ /dev/null @@ -1,139 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsclist_h___ -#define jsclist_h___ - -#include "jstypes.h" - -/* -** Circular linked list -*/ -typedef struct JSCListStr { - struct JSCListStr *next; - struct JSCListStr *prev; -} JSCList; - -/* -** Insert element "_e" into the list, before "_l". -*/ -#define JS_INSERT_BEFORE(_e,_l) \ - JS_BEGIN_MACRO \ - (_e)->next = (_l); \ - (_e)->prev = (_l)->prev; \ - (_l)->prev->next = (_e); \ - (_l)->prev = (_e); \ - JS_END_MACRO - -/* -** Insert element "_e" into the list, after "_l". -*/ -#define JS_INSERT_AFTER(_e,_l) \ - JS_BEGIN_MACRO \ - (_e)->next = (_l)->next; \ - (_e)->prev = (_l); \ - (_l)->next->prev = (_e); \ - (_l)->next = (_e); \ - JS_END_MACRO - -/* -** Return the element following element "_e" -*/ -#define JS_NEXT_LINK(_e) \ - ((_e)->next) -/* -** Return the element preceding element "_e" -*/ -#define JS_PREV_LINK(_e) \ - ((_e)->prev) - -/* -** Append an element "_e" to the end of the list "_l" -*/ -#define JS_APPEND_LINK(_e,_l) JS_INSERT_BEFORE(_e,_l) - -/* -** Insert an element "_e" at the head of the list "_l" -*/ -#define JS_INSERT_LINK(_e,_l) JS_INSERT_AFTER(_e,_l) - -/* Return the head/tail of the list */ -#define JS_LIST_HEAD(_l) (_l)->next -#define JS_LIST_TAIL(_l) (_l)->prev - -/* -** Remove the element "_e" from it's circular list. -*/ -#define JS_REMOVE_LINK(_e) \ - JS_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - JS_END_MACRO - -/* -** Remove the element "_e" from it's circular list. Also initializes the -** linkage. -*/ -#define JS_REMOVE_AND_INIT_LINK(_e) \ - JS_BEGIN_MACRO \ - (_e)->prev->next = (_e)->next; \ - (_e)->next->prev = (_e)->prev; \ - (_e)->next = (_e); \ - (_e)->prev = (_e); \ - JS_END_MACRO - -/* -** Return non-zero if the given circular list "_l" is empty, zero if the -** circular list is not empty -*/ -#define JS_CLIST_IS_EMPTY(_l) \ - ((_l)->next == (_l)) - -/* -** Initialize a circular list -*/ -#define JS_INIT_CLIST(_l) \ - JS_BEGIN_MACRO \ - (_l)->next = (_l); \ - (_l)->prev = (_l); \ - JS_END_MACRO - -#define JS_INIT_STATIC_CLIST(_l) \ - {(_l), (_l)} - -#endif /* jsclist_h___ */ diff --git a/spidermonkey/libjs/jscntxt.c b/spidermonkey/libjs/jscntxt.c deleted file mode 100644 index 139ad9b..0000000 --- a/spidermonkey/libjs/jscntxt.c +++ /dev/null @@ -1,1229 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS execution context. - */ -#include "jsstddef.h" -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jsprf.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsexn.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#ifdef JS_THREADSAFE - -/* - * Callback function to delete a JSThread info when the thread that owns it - * is destroyed. - */ -void JS_DLL_CALLBACK -js_ThreadDestructorCB(void *ptr) -{ - JSThread *thread = (JSThread *)ptr; - - if (!thread) - return; - while (!JS_CLIST_IS_EMPTY(&thread->contextList)) { - /* NB: use a temporary, as the macro evaluates its args many times. */ - JSCList *link = thread->contextList.next; - - JS_REMOVE_AND_INIT_LINK(link); - } - GSN_CACHE_CLEAR(&thread->gsnCache); - free(thread); -} - -/* - * Get current thread-local JSThread info, creating one if it doesn't exist. - * Each thread has a unique JSThread pointer. - * - * Since we are dealing with thread-local data, no lock is needed. - * - * Return a pointer to the thread local info, NULL if the system runs out - * of memory, or it failed to set thread private data (neither case is very - * likely; both are probably due to out-of-memory). It is up to the caller - * to report an error, if possible. - */ -JSThread * -js_GetCurrentThread(JSRuntime *rt) -{ - JSThread *thread; - - thread = (JSThread *)PR_GetThreadPrivate(rt->threadTPIndex); - if (!thread) { - thread = (JSThread *) calloc(1, sizeof(JSThread)); - if (!thread) - return NULL; - - if (PR_FAILURE == PR_SetThreadPrivate(rt->threadTPIndex, thread)) { - free(thread); - return NULL; - } - - JS_INIT_CLIST(&thread->contextList); - thread->id = js_CurrentThreadId(); - - /* js_SetContextThread initialize gcFreeLists as necessary. */ -#ifdef DEBUG - memset(thread->gcFreeLists, JS_FREE_PATTERN, - sizeof(thread->gcFreeLists)); -#endif - } - return thread; -} - -/* - * Sets current thread as owning thread of a context by assigning the - * thread-private info to the context. If the current thread doesn't have - * private JSThread info, create one. - */ -JSBool -js_SetContextThread(JSContext *cx) -{ - JSThread *thread = js_GetCurrentThread(cx->runtime); - - if (!thread) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* - * Clear gcFreeLists on each transition from 0 to 1 context active on the - * current thread. See bug 351602. - */ - if (JS_CLIST_IS_EMPTY(&thread->contextList)) - memset(thread->gcFreeLists, 0, sizeof(thread->gcFreeLists)); - - cx->thread = thread; - JS_REMOVE_LINK(&cx->threadLinks); - JS_APPEND_LINK(&cx->threadLinks, &thread->contextList); - return JS_TRUE; -} - -/* Remove the owning thread info of a context. */ -void -js_ClearContextThread(JSContext *cx) -{ - JS_REMOVE_AND_INIT_LINK(&cx->threadLinks); -#ifdef DEBUG - if (JS_CLIST_IS_EMPTY(&cx->thread->contextList)) { - memset(cx->thread->gcFreeLists, JS_FREE_PATTERN, - sizeof(cx->thread->gcFreeLists)); - } -#endif - cx->thread = NULL; -} - -#endif /* JS_THREADSAFE */ - -void -js_OnVersionChange(JSContext *cx) -{ -#ifdef DEBUG - JSVersion version = JSVERSION_NUMBER(cx); - - JS_ASSERT(version == JSVERSION_DEFAULT || version >= JSVERSION_ECMA_3); -#endif -} - -void -js_SetVersion(JSContext *cx, JSVersion version) -{ - cx->version = version; - js_OnVersionChange(cx); -} - -JSContext * -js_NewContext(JSRuntime *rt, size_t stackChunkSize) -{ - JSContext *cx; - JSBool ok, first; - JSContextCallback cxCallback; - - cx = (JSContext *) malloc(sizeof *cx); - if (!cx) - return NULL; - memset(cx, 0, sizeof *cx); - - cx->runtime = rt; -#if JS_STACK_GROWTH_DIRECTION > 0 - cx->stackLimit = (jsuword)-1; -#endif -#ifdef JS_THREADSAFE - JS_INIT_CLIST(&cx->threadLinks); - js_SetContextThread(cx); -#endif - - JS_LOCK_GC(rt); - for (;;) { - first = (rt->contextList.next == &rt->contextList); - if (rt->state == JSRTS_UP) { - JS_ASSERT(!first); - break; - } - if (rt->state == JSRTS_DOWN) { - JS_ASSERT(first); - rt->state = JSRTS_LAUNCHING; - break; - } - JS_WAIT_CONDVAR(rt->stateChange, JS_NO_TIMEOUT); - } - JS_APPEND_LINK(&cx->links, &rt->contextList); - JS_UNLOCK_GC(rt); - - /* - * First we do the infallible, every-time per-context initializations. - * Should a later, fallible initialization (js_InitRegExpStatics, e.g., - * or the stuff under 'if (first)' below) fail, at least the version - * and arena-pools will be valid and safe to use (say, from the last GC - * done by js_DestroyContext). - */ - cx->version = JSVERSION_DEFAULT; - cx->jsop_eq = JSOP_EQ; - cx->jsop_ne = JSOP_NE; - JS_InitArenaPool(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval)); - JS_InitArenaPool(&cx->tempPool, "temp", 1024, sizeof(jsdouble)); - - if (!js_InitRegExpStatics(cx, &cx->regExpStatics)) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } - - /* - * If cx is the first context on this runtime, initialize well-known atoms, - * keywords, numbers, and strings. If one of these steps should fail, the - * runtime will be left in a partially initialized state, with zeroes and - * nulls stored in the default-initialized remainder of the struct. We'll - * clean the runtime up under js_DestroyContext, because cx will be "last" - * as well as "first". - */ - if (first) { -#ifdef JS_THREADSAFE - JS_BeginRequest(cx); -#endif - /* - * Both atomState and the scriptFilenameTable may be left over from a - * previous episode of non-zero contexts alive in rt, so don't re-init - * either table if it's not necessary. Just repopulate atomState with - * well-known internal atoms, and with the reserved identifiers added - * by the scanner. - */ - ok = (rt->atomState.liveAtoms == 0) - ? js_InitAtomState(cx, &rt->atomState) - : js_InitPinnedAtoms(cx, &rt->atomState); - if (ok && !rt->scriptFilenameTable) - ok = js_InitRuntimeScriptState(rt); - if (ok) - ok = js_InitRuntimeNumberState(cx); - if (ok) - ok = js_InitRuntimeStringState(cx); -#ifdef JS_THREADSAFE - JS_EndRequest(cx); -#endif - if (!ok) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } - - JS_LOCK_GC(rt); - rt->state = JSRTS_UP; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); - JS_UNLOCK_GC(rt); - } - - cxCallback = rt->cxCallback; - if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) { - js_DestroyContext(cx, JSDCM_NEW_FAILED); - return NULL; - } - return cx; -} - -void -js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) -{ - JSRuntime *rt; - JSContextCallback cxCallback; - JSBool last; - JSArgumentFormatMap *map; - JSLocalRootStack *lrs; - JSLocalRootChunk *lrc; - - rt = cx->runtime; - - if (mode != JSDCM_NEW_FAILED) { - cxCallback = rt->cxCallback; - if (cxCallback) { - /* - * JSCONTEXT_DESTROY callback is not allowed to fail and must - * return true. - */ -#ifdef DEBUG - JSBool callbackStatus = -#endif - cxCallback(cx, JSCONTEXT_DESTROY); - JS_ASSERT(callbackStatus); - } - } - - /* Remove cx from context list first. */ - JS_LOCK_GC(rt); - JS_ASSERT(rt->state == JSRTS_UP || rt->state == JSRTS_LAUNCHING); - JS_REMOVE_LINK(&cx->links); - last = (rt->contextList.next == &rt->contextList); - if (last) - rt->state = JSRTS_LANDING; - JS_UNLOCK_GC(rt); - - if (last) { -#ifdef JS_THREADSAFE - /* - * If cx is not in a request already, begin one now so that we wait - * for any racing GC started on a not-last context to finish, before - * we plow ahead and unpin atoms. Note that even though we begin a - * request here if necessary, we end all requests on cx below before - * forcing a final GC. This lets any not-last context destruction - * racing in another thread try to force or maybe run the GC, but by - * that point, rt->state will not be JSRTS_UP, and that GC attempt - * will return early. - */ - if (cx->requestDepth == 0) - JS_BeginRequest(cx); -#endif - - /* Unpin all pinned atoms before final GC. */ - js_UnpinPinnedAtoms(&rt->atomState); - - /* Unlock and clear GC things held by runtime pointers. */ - js_FinishRuntimeNumberState(cx); - js_FinishRuntimeStringState(cx); - - /* Clear debugging state to remove GC roots. */ - JS_ClearAllTraps(cx); - JS_ClearAllWatchPoints(cx); - } - - /* - * Remove more GC roots in regExpStatics, then collect garbage. - * XXX anti-modularity alert: we rely on the call to js_RemoveRoot within - * XXX this function call to wait for any racing GC to complete, in the - * XXX case where JS_DestroyContext is called outside of a request on cx - */ - js_FreeRegExpStatics(cx, &cx->regExpStatics); - -#ifdef JS_THREADSAFE - /* - * Destroying a context implicitly calls JS_EndRequest(). Also, we must - * end our request here in case we are "last" -- in that event, another - * js_DestroyContext that was not last might be waiting in the GC for our - * request to end. We'll let it run below, just before we do the truly - * final GC and then free atom state. - * - * At this point, cx must be inaccessible to other threads. It's off the - * rt->contextList, and it should not be reachable via any object private - * data structure. - */ - while (cx->requestDepth != 0) - JS_EndRequest(cx); -#endif - - if (last) { - js_GC(cx, GC_LAST_CONTEXT); - - /* Try to free atom state, now that no unrooted scripts survive. */ - if (rt->atomState.liveAtoms == 0) - js_FreeAtomState(cx, &rt->atomState); - - /* Also free the script filename table if it exists and is empty. */ - if (rt->scriptFilenameTable && rt->scriptFilenameTable->nentries == 0) - js_FinishRuntimeScriptState(rt); - - /* - * Free the deflated string cache, but only after the last GC has - * collected all unleaked strings. - */ - js_FinishDeflatedStringCache(rt); - - /* Take the runtime down, now that it has no contexts or atoms. */ - JS_LOCK_GC(rt); - rt->state = JSRTS_DOWN; - JS_NOTIFY_ALL_CONDVAR(rt->stateChange); - JS_UNLOCK_GC(rt); - } else { - if (mode == JSDCM_FORCE_GC) - js_GC(cx, GC_NORMAL); - else if (mode == JSDCM_MAYBE_GC) - JS_MaybeGC(cx); - } - - /* Free the stuff hanging off of cx. */ - JS_FinishArenaPool(&cx->stackPool); - JS_FinishArenaPool(&cx->tempPool); - - if (cx->lastMessage) - free(cx->lastMessage); - - /* Remove any argument formatters. */ - map = cx->argumentFormatMap; - while (map) { - JSArgumentFormatMap *temp = map; - map = map->next; - JS_free(cx, temp); - } - - /* Destroy the resolve recursion damper. */ - if (cx->resolvingTable) { - JS_DHashTableDestroy(cx->resolvingTable); - cx->resolvingTable = NULL; - } - - lrs = cx->localRootStack; - if (lrs) { - while ((lrc = lrs->topChunk) != &lrs->firstChunk) { - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - } - JS_free(cx, lrs); - } - -#ifdef JS_THREADSAFE - js_ClearContextThread(cx); -#endif - - /* Finally, free cx itself. */ - free(cx); -} - -JSBool -js_ValidContextPointer(JSRuntime *rt, JSContext *cx) -{ - JSCList *cl; - - for (cl = rt->contextList.next; cl != &rt->contextList; cl = cl->next) { - if (cl == &cx->links) - return JS_TRUE; - } - JS_RUNTIME_METER(rt, deadContexts); - return JS_FALSE; -} - -JSContext * -js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp) -{ - JSContext *cx = *iterp; - - if (unlocked) - JS_LOCK_GC(rt); - if (!cx) - cx = (JSContext *)&rt->contextList; - cx = (JSContext *)cx->links.next; - if (&cx->links == &rt->contextList) - cx = NULL; - *iterp = cx; - if (unlocked) - JS_UNLOCK_GC(rt); - return cx; -} - -JS_STATIC_DLL_CALLBACK(const void *) -resolving_GetKey(JSDHashTable *table, JSDHashEntryHdr *hdr) -{ - JSResolvingEntry *entry = (JSResolvingEntry *)hdr; - - return &entry->key; -} - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -resolving_HashKey(JSDHashTable *table, const void *ptr) -{ - const JSResolvingKey *key = (const JSResolvingKey *)ptr; - - return ((JSDHashNumber)JS_PTR_TO_UINT32(key->obj) >> JSVAL_TAGBITS) ^ key->id; -} - -JS_PUBLIC_API(JSBool) -resolving_MatchEntry(JSDHashTable *table, - const JSDHashEntryHdr *hdr, - const void *ptr) -{ - const JSResolvingEntry *entry = (const JSResolvingEntry *)hdr; - const JSResolvingKey *key = (const JSResolvingKey *)ptr; - - return entry->key.obj == key->obj && entry->key.id == key->id; -} - -static const JSDHashTableOps resolving_dhash_ops = { - JS_DHashAllocTable, - JS_DHashFreeTable, - resolving_GetKey, - resolving_HashKey, - resolving_MatchEntry, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -JSBool -js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry **entryp) -{ - JSDHashTable *table; - JSResolvingEntry *entry; - - table = cx->resolvingTable; - if (!table) { - table = JS_NewDHashTable(&resolving_dhash_ops, NULL, - sizeof(JSResolvingEntry), - JS_DHASH_MIN_SIZE); - if (!table) - goto outofmem; - cx->resolvingTable = table; - } - - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, key, JS_DHASH_ADD); - if (!entry) - goto outofmem; - - if (entry->flags & flag) { - /* An entry for (key, flag) exists already -- dampen recursion. */ - entry = NULL; - } else { - /* Fill in key if we were the first to add entry, then set flag. */ - if (!entry->key.obj) - entry->key = *key; - entry->flags |= flag; - } - *entryp = entry; - return JS_TRUE; - -outofmem: - JS_ReportOutOfMemory(cx); - return JS_FALSE; -} - -void -js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry *entry, uint32 generation) -{ - JSDHashTable *table; - - /* - * Clear flag from entry->flags and return early if other flags remain. - * We must take care to re-lookup entry if the table has changed since - * it was found by js_StartResolving. - */ - table = cx->resolvingTable; - if (!entry || table->generation != generation) { - entry = (JSResolvingEntry *) - JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); - } - JS_ASSERT(JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)); - entry->flags &= ~flag; - if (entry->flags) - return; - - /* - * Do a raw remove only if fewer entries were removed than would cause - * alpha to be less than .5 (alpha is at most .75). Otherwise, we just - * call JS_DHashTableOperate to re-lookup the key and remove its entry, - * compressing or shrinking the table as needed. - */ - if (table->removedCount < JS_DHASH_TABLE_SIZE(table) >> 2) - JS_DHashTableRawRemove(table, &entry->hdr); - else - JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); -} - -JSBool -js_EnterLocalRootScope(JSContext *cx) -{ - JSLocalRootStack *lrs; - int mark; - - lrs = cx->localRootStack; - if (!lrs) { - lrs = (JSLocalRootStack *) JS_malloc(cx, sizeof *lrs); - if (!lrs) - return JS_FALSE; - lrs->scopeMark = JSLRS_NULL_MARK; - lrs->rootCount = 0; - lrs->topChunk = &lrs->firstChunk; - lrs->firstChunk.down = NULL; - cx->localRootStack = lrs; - } - - /* Push lrs->scopeMark to save it for restore when leaving. */ - mark = js_PushLocalRoot(cx, lrs, INT_TO_JSVAL(lrs->scopeMark)); - if (mark < 0) - return JS_FALSE; - lrs->scopeMark = (uint32) mark; - return JS_TRUE; -} - -void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval) -{ - JSLocalRootStack *lrs; - uint32 mark, m, n; - JSLocalRootChunk *lrc; - - /* Defend against buggy native callers. */ - lrs = cx->localRootStack; - JS_ASSERT(lrs && lrs->rootCount != 0); - if (!lrs || lrs->rootCount == 0) - return; - - mark = lrs->scopeMark; - JS_ASSERT(mark != JSLRS_NULL_MARK); - if (mark == JSLRS_NULL_MARK) - return; - - /* Free any chunks being popped by this leave operation. */ - m = mark >> JSLRS_CHUNK_SHIFT; - n = (lrs->rootCount - 1) >> JSLRS_CHUNK_SHIFT; - while (n > m) { - lrc = lrs->topChunk; - JS_ASSERT(lrc != &lrs->firstChunk); - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - --n; - } - - /* - * Pop the scope, restoring lrs->scopeMark. If rval is a GC-thing, push - * it on the caller's scope, or store it in lastInternalResult if we are - * leaving the outermost scope. We don't need to allocate a new lrc - * because we can overwrite the old mark's slot with rval. - */ - lrc = lrs->topChunk; - m = mark & JSLRS_CHUNK_MASK; - lrs->scopeMark = (uint32) JSVAL_TO_INT(lrc->roots[m]); - if (JSVAL_IS_GCTHING(rval) && !JSVAL_IS_NULL(rval)) { - if (mark == 0) { - cx->weakRoots.lastInternalResult = rval; - } else { - /* - * Increment m to avoid the "else if (m == 0)" case below. If - * rval is not a GC-thing, that case would take care of freeing - * any chunk that contained only the old mark. Since rval *is* - * a GC-thing here, we want to reuse that old mark's slot. - */ - lrc->roots[m++] = rval; - ++mark; - } - } - lrs->rootCount = (uint32) mark; - - /* - * Free the stack eagerly, risking malloc churn. The alternative would - * require an lrs->entryCount member, maintained by Enter and Leave, and - * tested by the GC in addition to the cx->localRootStack non-null test. - * - * That approach would risk hoarding 264 bytes (net) per context. Right - * now it seems better to give fresh (dirty in CPU write-back cache, and - * the data is no longer needed) memory back to the malloc heap. - */ - if (mark == 0) { - cx->localRootStack = NULL; - JS_free(cx, lrs); - } else if (m == 0) { - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - } -} - -void -js_ForgetLocalRoot(JSContext *cx, jsval v) -{ - JSLocalRootStack *lrs; - uint32 i, j, m, n, mark; - JSLocalRootChunk *lrc, *lrc2; - jsval top; - - lrs = cx->localRootStack; - JS_ASSERT(lrs && lrs->rootCount); - if (!lrs || lrs->rootCount == 0) - return; - - /* Prepare to pop the top-most value from the stack. */ - n = lrs->rootCount - 1; - m = n & JSLRS_CHUNK_MASK; - lrc = lrs->topChunk; - top = lrc->roots[m]; - - /* Be paranoid about calls on an empty scope. */ - mark = lrs->scopeMark; - JS_ASSERT(mark < n); - if (mark >= n) - return; - - /* If v was not the last root pushed in the top scope, find it. */ - if (top != v) { - /* Search downward in case v was recently pushed. */ - i = n; - j = m; - lrc2 = lrc; - while (--i > mark) { - if (j == 0) - lrc2 = lrc2->down; - j = i & JSLRS_CHUNK_MASK; - if (lrc2->roots[j] == v) - break; - } - - /* If we didn't find v in this scope, assert and bail out. */ - JS_ASSERT(i != mark); - if (i == mark) - return; - - /* Swap top and v so common tail code can pop v. */ - lrc2->roots[j] = top; - } - - /* Pop the last value from the stack. */ - lrc->roots[m] = JSVAL_NULL; - lrs->rootCount = n; - if (m == 0) { - JS_ASSERT(n != 0); - JS_ASSERT(lrc != &lrs->firstChunk); - lrs->topChunk = lrc->down; - JS_free(cx, lrc); - } -} - -int -js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v) -{ - uint32 n, m; - JSLocalRootChunk *lrc; - - n = lrs->rootCount; - m = n & JSLRS_CHUNK_MASK; - if (n == 0 || m != 0) { - /* - * At start of first chunk, or not at start of a non-first top chunk. - * Check for lrs->rootCount overflow. - */ - if ((uint32)(n + 1) == 0) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_LOCAL_ROOTS); - return -1; - } - lrc = lrs->topChunk; - JS_ASSERT(n != 0 || lrc == &lrs->firstChunk); - } else { - /* - * After lrs->firstChunk, trying to index at a power-of-two chunk - * boundary: need a new chunk. - */ - lrc = (JSLocalRootChunk *) JS_malloc(cx, sizeof *lrc); - if (!lrc) - return -1; - lrc->down = lrs->topChunk; - lrs->topChunk = lrc; - } - lrs->rootCount = n + 1; - lrc->roots[m] = v; - return (int) n; -} - -void -js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs) -{ - uint32 n, m, mark; - JSLocalRootChunk *lrc; - - n = lrs->rootCount; - if (n == 0) - return; - - mark = lrs->scopeMark; - lrc = lrs->topChunk; - do { - while (--n > mark) { -#ifdef GC_MARK_DEBUG - char name[22]; - JS_snprintf(name, sizeof name, "", n); -#endif - m = n & JSLRS_CHUNK_MASK; - JS_ASSERT(JSVAL_IS_GCTHING(lrc->roots[m])); - GC_MARK(cx, JSVAL_TO_GCTHING(lrc->roots[m]), name); - if (m == 0) - lrc = lrc->down; - } - m = n & JSLRS_CHUNK_MASK; - mark = JSVAL_TO_INT(lrc->roots[m]); - if (m == 0) - lrc = lrc->down; - } while (n != 0); - JS_ASSERT(!lrc); -} - -static void -ReportError(JSContext *cx, const char *message, JSErrorReport *reportp) -{ - /* - * Check the error report, and set a JavaScript-catchable exception - * if the error is defined to have an associated exception. If an - * exception is thrown, then the JSREPORT_EXCEPTION flag will be set - * on the error report, and exception-aware hosts should ignore it. - */ - JS_ASSERT(reportp); - if (reportp->errorNumber == JSMSG_UNCAUGHT_EXCEPTION) - reportp->flags |= JSREPORT_EXCEPTION; - - /* - * Call the error reporter only if an exception wasn't raised. - * - * If an exception was raised, then we call the debugErrorHook - * (if present) to give it a chance to see the error before it - * propagates out of scope. This is needed for compatability - * with the old scheme. - */ - if (!js_ErrorToException(cx, message, reportp)) { - js_ReportErrorAgain(cx, message, reportp); - } else if (cx->runtime->debugErrorHook && cx->errorReporter) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - /* test local in case debugErrorHook changed on another thread */ - if (hook) - hook(cx, message, reportp, cx->runtime->debugErrorHookData); - } -} - -/* - * We don't post an exception in this case, since doing so runs into - * complications of pre-allocating an exception object which required - * running the Exception class initializer early etc. - * Instead we just invoke the errorReporter with an "Out Of Memory" - * type message, and then hope the process ends swiftly. - */ -void -js_ReportOutOfMemory(JSContext *cx) -{ - JSStackFrame *fp; - JSErrorReport report; - JSErrorReporter onError = cx->errorReporter; - - /* Get the message for this error, but we won't expand any arguments. */ - const JSErrorFormatString *efs = - js_GetLocalizedErrorMessage(cx, NULL, NULL, JSMSG_OUT_OF_MEMORY); - const char *msg = efs ? efs->format : "Out of memory"; - - /* Fill out the report, but don't do anything that requires allocation. */ - memset(&report, 0, sizeof (struct JSErrorReport)); - report.flags = JSREPORT_ERROR; - report.errorNumber = JSMSG_OUT_OF_MEMORY; - - /* - * Walk stack until we find a frame that is associated with some script - * rather than a native frame. - */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report.filename = fp->script->filename; - report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - - /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular ErrorReporter. - */ - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - if (hook && - !hook(cx, msg, &report, cx->runtime->debugErrorHookData)) { - onError = NULL; - } - } - - if (onError) - onError(cx, msg, &report); -} - -JSBool -js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap) -{ - char *message; - jschar *ucmessage; - size_t messagelen; - JSStackFrame *fp; - JSErrorReport report; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - message = JS_vsmprintf(format, ap); - if (!message) - return JS_FALSE; - messagelen = strlen(message); - - memset(&report, 0, sizeof (struct JSErrorReport)); - report.flags = flags; - report.errorNumber = JSMSG_USER_DEFINED_ERROR; - report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen); - - /* Find the top-most active script frame, for best line number blame. */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report.filename = fp->script->filename; - report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - - warning = JSREPORT_IS_WARNING(report.flags); - if (warning && JS_HAS_WERROR_OPTION(cx)) { - report.flags &= ~JSREPORT_WARNING; - warning = JS_FALSE; - } - - ReportError(cx, message, &report); - free(message); - JS_free(cx, ucmessage); - return warning; -} - -/* - * The arguments from ap need to be packaged up into an array and stored - * into the report struct. - * - * The format string addressed by the error number may contain operands - * identified by the format {N}, where N is a decimal digit. Each of these - * is to be replaced by the Nth argument from the va_list. The complete - * message is placed into reportp->ucmessage converted to a JSString. - * - * Returns true if the expansion succeeds (can fail if out of memory). - */ -JSBool -js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - char **messagep, JSErrorReport *reportp, - JSBool *warningp, JSBool charArgs, va_list ap) -{ - const JSErrorFormatString *efs; - int i; - int argCount; - - *warningp = JSREPORT_IS_WARNING(reportp->flags); - if (*warningp && JS_HAS_WERROR_OPTION(cx)) { - reportp->flags &= ~JSREPORT_WARNING; - *warningp = JS_FALSE; - } - - *messagep = NULL; - - /* Most calls supply js_GetErrorMessage; if this is so, assume NULL. */ - if (!callback || callback == js_GetErrorMessage) - efs = js_GetLocalizedErrorMessage(cx, userRef, NULL, errorNumber); - else - efs = callback(userRef, NULL, errorNumber); - if (efs) { - size_t totalArgsLength = 0; - size_t argLengths[10]; /* only {0} thru {9} supported */ - argCount = efs->argCount; - JS_ASSERT(argCount <= 10); - if (argCount > 0) { - /* - * Gather the arguments into an array, and accumulate - * their sizes. We allocate 1 more than necessary and - * null it out to act as the caboose when we free the - * pointers later. - */ - reportp->messageArgs = (const jschar **) - JS_malloc(cx, sizeof(jschar *) * (argCount + 1)); - if (!reportp->messageArgs) - return JS_FALSE; - reportp->messageArgs[argCount] = NULL; - for (i = 0; i < argCount; i++) { - if (charArgs) { - char *charArg = va_arg(ap, char *); - size_t charArgLength = strlen(charArg); - reportp->messageArgs[i] - = js_InflateString(cx, charArg, &charArgLength); - if (!reportp->messageArgs[i]) - goto error; - } else { - reportp->messageArgs[i] = va_arg(ap, jschar *); - } - argLengths[i] = js_strlen(reportp->messageArgs[i]); - totalArgsLength += argLengths[i]; - } - /* NULL-terminate for easy copying. */ - reportp->messageArgs[i] = NULL; - } - /* - * Parse the error format, substituting the argument X - * for {X} in the format. - */ - if (argCount > 0) { - if (efs->format) { - jschar *buffer, *fmt, *out; - int expandedArgs = 0; - size_t expandedLength; - size_t len = strlen(efs->format); - - buffer = fmt = js_InflateString (cx, efs->format, &len); - if (!buffer) - goto error; - expandedLength = len - - (3 * argCount) /* exclude the {n} */ - + totalArgsLength; - - /* - * Note - the above calculation assumes that each argument - * is used once and only once in the expansion !!! - */ - reportp->ucmessage = out = (jschar *) - JS_malloc(cx, (expandedLength + 1) * sizeof(jschar)); - if (!out) { - JS_free (cx, buffer); - goto error; - } - while (*fmt) { - if (*fmt == '{') { - if (isdigit(fmt[1])) { - int d = JS7_UNDEC(fmt[1]); - JS_ASSERT(d < argCount); - js_strncpy(out, reportp->messageArgs[d], - argLengths[d]); - out += argLengths[d]; - fmt += 3; - expandedArgs++; - continue; - } - } - *out++ = *fmt++; - } - JS_ASSERT(expandedArgs == argCount); - *out = 0; - JS_free (cx, buffer); - *messagep = - js_DeflateString(cx, reportp->ucmessage, - (size_t)(out - reportp->ucmessage)); - if (!*messagep) - goto error; - } - } else { - /* - * Zero arguments: the format string (if it exists) is the - * entire message. - */ - if (efs->format) { - size_t len; - *messagep = JS_strdup(cx, efs->format); - if (!*messagep) - goto error; - len = strlen(*messagep); - reportp->ucmessage = js_InflateString(cx, *messagep, &len); - if (!reportp->ucmessage) - goto error; - } - } - } - if (*messagep == NULL) { - /* where's the right place for this ??? */ - const char *defaultErrorMessage - = "No error message available for error number %d"; - size_t nbytes = strlen(defaultErrorMessage) + 16; - *messagep = (char *)JS_malloc(cx, nbytes); - if (!*messagep) - goto error; - JS_snprintf(*messagep, nbytes, defaultErrorMessage, errorNumber); - } - return JS_TRUE; - -error: - if (reportp->messageArgs) { - /* free the arguments only if we allocated them */ - if (charArgs) { - i = 0; - while (reportp->messageArgs[i]) - JS_free(cx, (void *)reportp->messageArgs[i++]); - } - JS_free(cx, (void *)reportp->messageArgs); - reportp->messageArgs = NULL; - } - if (reportp->ucmessage) { - JS_free(cx, (void *)reportp->ucmessage); - reportp->ucmessage = NULL; - } - if (*messagep) { - JS_free(cx, (void *)*messagep); - *messagep = NULL; - } - return JS_FALSE; -} - -JSBool -js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - JSBool charArgs, va_list ap) -{ - JSStackFrame *fp; - JSErrorReport report; - char *message; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - memset(&report, 0, sizeof (struct JSErrorReport)); - report.flags = flags; - report.errorNumber = errorNumber; - - /* - * If we can't find out where the error was based on the current frame, - * see if the next frame has a script/pc combo we can use. - */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report.filename = fp->script->filename; - report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - - if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber, - &message, &report, &warning, charArgs, ap)) { - return JS_FALSE; - } - - ReportError(cx, message, &report); - - if (message) - JS_free(cx, message); - if (report.messageArgs) { - /* - * js_ExpandErrorArguments owns its messageArgs only if it had to - * inflate the arguments (from regular |char *|s). - */ - if (charArgs) { - int i = 0; - while (report.messageArgs[i]) - JS_free(cx, (void *)report.messageArgs[i++]); - } - JS_free(cx, (void *)report.messageArgs); - } - if (report.ucmessage) - JS_free(cx, (void *)report.ucmessage); - - return warning; -} - -JS_FRIEND_API(void) -js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *reportp) -{ - JSErrorReporter onError; - - if (!message) - return; - - if (cx->lastMessage) - free(cx->lastMessage); - cx->lastMessage = JS_strdup(cx, message); - if (!cx->lastMessage) - return; - onError = cx->errorReporter; - - /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular ErrorReporter. - */ - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - if (hook && - !hook(cx, cx->lastMessage, reportp, - cx->runtime->debugErrorHookData)) { - onError = NULL; - } - } - if (onError) - onError(cx, cx->lastMessage, reportp); -} - -void -js_ReportIsNotDefined(JSContext *cx, const char *name) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_DEFINED, name); -} - -#if defined DEBUG && defined XP_UNIX -/* For gdb usage. */ -void js_traceon(JSContext *cx) { cx->tracefp = stderr; } -void js_traceoff(JSContext *cx) { cx->tracefp = NULL; } -#endif - -JSErrorFormatString js_ErrorFormatString[JSErr_Limit] = { -#define MSG_DEF(name, number, count, exception, format) \ - { format, count, exception } , -#include "js.msg" -#undef MSG_DEF -}; - -const JSErrorFormatString * -js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber) -{ - if ((errorNumber > 0) && (errorNumber < JSErr_Limit)) - return &js_ErrorFormatString[errorNumber]; - return NULL; -} diff --git a/spidermonkey/libjs/jscntxt.h b/spidermonkey/libjs/jscntxt.h deleted file mode 100644 index 7ca678e..0000000 --- a/spidermonkey/libjs/jscntxt.h +++ /dev/null @@ -1,1013 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jscntxt_h___ -#define jscntxt_h___ -/* - * JS execution context. - */ -#include "jsarena.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jslong.h" -#include "jsatom.h" -#include "jsconfig.h" -#include "jsdhash.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsobj.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsregexp.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* - * js_GetSrcNote cache to avoid O(n^2) growth in finding a source note for a - * given pc in a script. - */ -typedef struct JSGSNCache { - JSScript *script; - JSDHashTable table; -#ifdef JS_GSNMETER - uint32 hits; - uint32 misses; - uint32 fills; - uint32 clears; -# define GSN_CACHE_METER(cache,cnt) (++(cache)->cnt) -#else -# define GSN_CACHE_METER(cache,cnt) /* nothing */ -#endif -} JSGSNCache; - -#define GSN_CACHE_CLEAR(cache) \ - JS_BEGIN_MACRO \ - (cache)->script = NULL; \ - if ((cache)->table.ops) { \ - JS_DHashTableFinish(&(cache)->table); \ - (cache)->table.ops = NULL; \ - } \ - GSN_CACHE_METER(cache, clears); \ - JS_END_MACRO - -/* These helper macros take a cx as parameter and operate on its GSN cache. */ -#define JS_CLEAR_GSN_CACHE(cx) GSN_CACHE_CLEAR(&JS_GSN_CACHE(cx)) -#define JS_METER_GSN_CACHE(cx,cnt) GSN_CACHE_METER(&JS_GSN_CACHE(cx), cnt) - -#ifdef JS_THREADSAFE - -/* - * Structure uniquely representing a thread. It holds thread-private data - * that can be accessed without a global lock. - */ -struct JSThread { - /* Linked list of all contexts active on this thread. */ - JSCList contextList; - - /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */ - jsword id; - - /* Thread-local gc free lists array. */ - JSGCThing *gcFreeLists[GC_NUM_FREELISTS]; - - /* - * Thread-local version of JSRuntime.gcMallocBytes to avoid taking - * locks on each JS_malloc. - */ - uint32 gcMallocBytes; - -#if JS_HAS_GENERATORS - /* Flag indicating that the current thread is executing close hooks. */ - JSBool gcRunningCloseHooks; -#endif - - /* - * Store the GSN cache in struct JSThread, not struct JSContext, both to - * save space and to simplify cleanup in js_GC. Any embedding (Firefox - * or another Gecko application) that uses many contexts per thread is - * unlikely to interleave js_GetSrcNote-intensive loops in the decompiler - * among two or more contexts running script in one thread. - */ - JSGSNCache gsnCache; -}; - -#define JS_GSN_CACHE(cx) ((cx)->thread->gsnCache) - -extern void JS_DLL_CALLBACK -js_ThreadDestructorCB(void *ptr); - -extern JSBool -js_SetContextThread(JSContext *cx); - -extern void -js_ClearContextThread(JSContext *cx); - -extern JSThread * -js_GetCurrentThread(JSRuntime *rt); - -#endif /* JS_THREADSAFE */ - -typedef enum JSDestroyContextMode { - JSDCM_NO_GC, - JSDCM_MAYBE_GC, - JSDCM_FORCE_GC, - JSDCM_NEW_FAILED -} JSDestroyContextMode; - -typedef enum JSRuntimeState { - JSRTS_DOWN, - JSRTS_LAUNCHING, - JSRTS_UP, - JSRTS_LANDING -} JSRuntimeState; - -typedef struct JSPropertyTreeEntry { - JSDHashEntryHdr hdr; - JSScopeProperty *child; -} JSPropertyTreeEntry; - -/* - * Forward declaration for opaque JSRuntime.nativeIteratorStates. - */ -typedef struct JSNativeIteratorState JSNativeIteratorState; - -struct JSRuntime { - /* Runtime state, synchronized by the stateChange/gcLock condvar/lock. */ - JSRuntimeState state; - - /* Context create/destroy callback. */ - JSContextCallback cxCallback; - - /* Garbage collector state, used by jsgc.c. */ - JSGCArenaList gcArenaList[GC_NUM_FREELISTS]; - JSDHashTable gcRootsHash; - JSDHashTable *gcLocksHash; - jsrefcount gcKeepAtoms; - uint32 gcBytes; - uint32 gcLastBytes; - uint32 gcMaxBytes; - uint32 gcMaxMallocBytes; - uint32 gcLevel; - uint32 gcNumber; - - /* - * NB: do not pack another flag here by claiming gcPadding unless the new - * flag is written only by the GC thread. Atomic updates to packed bytes - * are not guaranteed, so stores issued by one thread may be lost due to - * unsynchronized read-modify-write cycles on other threads. - */ - JSPackedBool gcPoke; - JSPackedBool gcRunning; - uint16 gcPadding; - - JSGCCallback gcCallback; - uint32 gcMallocBytes; - JSGCArena *gcUnscannedArenaStackTop; -#ifdef DEBUG - size_t gcUnscannedBagSize; -#endif - - /* - * API compatibility requires keeping GCX_PRIVATE bytes separate from the - * original GC types' byte tally. Otherwise embeddings that configure a - * good limit for pre-GCX_PRIVATE versions of the engine will see memory - * over-pressure too often, possibly leading to failed last-ditch GCs. - * - * The new XML GC-thing types do add to gcBytes, and they're larger than - * the original GC-thing type size (8 bytes on most architectures). So a - * user who enables E4X may want to increase the maxbytes value passed to - * JS_NewRuntime. TODO: Note this in the API docs. - */ - uint32 gcPrivateBytes; - - /* - * Table for tracking iterators to ensure that we close iterator's state - * before finalizing the iterable object. - */ - JSPtrTable gcIteratorTable; - -#if JS_HAS_GENERATORS - /* Runtime state to support close hooks. */ - JSGCCloseState gcCloseState; -#endif - -#ifdef JS_GCMETER - JSGCStats gcStats; -#endif - - /* Literal table maintained by jsatom.c functions. */ - JSAtomState atomState; - - /* Random number generator state, used by jsmath.c. */ - JSBool rngInitialized; - int64 rngMultiplier; - int64 rngAddend; - int64 rngMask; - int64 rngSeed; - jsdouble rngDscale; - - /* Well-known numbers held for use by this runtime's contexts. */ - jsdouble *jsNaN; - jsdouble *jsNegativeInfinity; - jsdouble *jsPositiveInfinity; - -#ifdef JS_THREADSAFE - JSLock *deflatedStringCacheLock; -#endif - JSHashTable *deflatedStringCache; -#ifdef DEBUG - uint32 deflatedStringCacheBytes; -#endif - - /* Empty string held for use by this runtime's contexts. */ - JSString *emptyString; - - /* List of active contexts sharing this runtime; protected by gcLock. */ - JSCList contextList; - - /* These are used for debugging -- see jsprvtd.h and jsdbgapi.h. */ - JSTrapHandler interruptHandler; - void *interruptHandlerData; - JSNewScriptHook newScriptHook; - void *newScriptHookData; - JSDestroyScriptHook destroyScriptHook; - void *destroyScriptHookData; - JSTrapHandler debuggerHandler; - void *debuggerHandlerData; - JSSourceHandler sourceHandler; - void *sourceHandlerData; - JSInterpreterHook executeHook; - void *executeHookData; - JSInterpreterHook callHook; - void *callHookData; - JSObjectHook objectHook; - void *objectHookData; - JSTrapHandler throwHook; - void *throwHookData; - JSDebugErrorHook debugErrorHook; - void *debugErrorHookData; - - /* More debugging state, see jsdbgapi.c. */ - JSCList trapList; - JSCList watchPointList; - - /* Weak links to properties, indexed by quickened get/set opcodes. */ - /* XXX must come after JSCLists or MSVC alignment bug bites empty lists */ - JSPropertyCache propertyCache; - - /* Client opaque pointer */ - void *data; - -#ifdef JS_THREADSAFE - /* These combine to interlock the GC and new requests. */ - PRLock *gcLock; - PRCondVar *gcDone; - PRCondVar *requestDone; - uint32 requestCount; - JSThread *gcThread; - - /* Lock and owning thread pointer for JS_LOCK_RUNTIME. */ - PRLock *rtLock; -#ifdef DEBUG - jsword rtLockOwner; -#endif - - /* Used to synchronize down/up state change; protected by gcLock. */ - PRCondVar *stateChange; - - /* Used to serialize cycle checks when setting __proto__ or __parent__. */ - PRLock *setSlotLock; - PRCondVar *setSlotDone; - JSBool setSlotBusy; - JSScope *setSlotScope; /* deadlock avoidance, see jslock.c */ - - /* - * State for sharing single-threaded scopes, once a second thread tries to - * lock a scope. The scopeSharingDone condvar is protected by rt->gcLock, - * to minimize number of locks taken in JS_EndRequest. - * - * The scopeSharingTodo linked list is likewise "global" per runtime, not - * one-list-per-context, to conserve space over all contexts, optimizing - * for the likely case that scopes become shared rarely, and among a very - * small set of threads (contexts). - */ - PRCondVar *scopeSharingDone; - JSScope *scopeSharingTodo; - -/* - * Magic terminator for the rt->scopeSharingTodo linked list, threaded through - * scope->u.link. This hack allows us to test whether a scope is on the list - * by asking whether scope->u.link is non-null. We use a large, likely bogus - * pointer here to distinguish this value from any valid u.count (small int) - * value. - */ -#define NO_SCOPE_SHARING_TODO ((JSScope *) 0xfeedbeef) - - /* - * The index for JSThread info, returned by PR_NewThreadPrivateIndex. - * The value is visible and shared by all threads, but the data is - * private to each thread. - */ - PRUintn threadTPIndex; -#endif /* JS_THREADSAFE */ - - /* - * Check property accessibility for objects of arbitrary class. Used at - * present to check f.caller accessibility for any function object f. - */ - JSCheckAccessOp checkObjectAccess; - - /* Security principals serialization support. */ - JSPrincipalsTranscoder principalsTranscoder; - - /* Optional hook to find principals for an object in this runtime. */ - JSObjectPrincipalsFinder findObjectPrincipals; - - /* - * Shared scope property tree, and arena-pool for allocating its nodes. - * The propertyRemovals counter is incremented for every js_ClearScope, - * and for each js_RemoveScopeProperty that frees a slot in an object. - * See js_NativeGet and js_NativeSet in jsobj.c. - */ - JSDHashTable propertyTreeHash; - JSScopeProperty *propertyFreeList; - JSArenaPool propertyArenaPool; - int32 propertyRemovals; - - /* Script filename table. */ - struct JSHashTable *scriptFilenameTable; - JSCList scriptFilenamePrefixes; -#ifdef JS_THREADSAFE - PRLock *scriptFilenameTableLock; -#endif - - /* Number localization, used by jsnum.c */ - const char *thousandsSeparator; - const char *decimalSeparator; - const char *numGrouping; - - /* - * Weak references to lazily-created, well-known XML singletons. - * - * NB: Singleton objects must be carefully disconnected from the rest of - * the object graph usually associated with a JSContext's global object, - * including the set of standard class objects. See jsxml.c for details. - */ - JSObject *anynameObject; - JSObject *functionNamespaceObject; - - /* - * A helper list for the GC, so it can mark native iterator states. See - * js_MarkNativeIteratorStates for details. - */ - JSNativeIteratorState *nativeIteratorStates; - -#ifndef JS_THREADSAFE - /* - * For thread-unsafe embeddings, the GSN cache lives in the runtime and - * not each context, since we expect it to be filled once when decompiling - * a longer script, then hit repeatedly as js_GetSrcNote is called during - * the decompiler activation that filled it. - */ - JSGSNCache gsnCache; - -#define JS_GSN_CACHE(cx) ((cx)->runtime->gsnCache) -#endif - -#ifdef DEBUG - /* Function invocation metering. */ - jsrefcount inlineCalls; - jsrefcount nativeCalls; - jsrefcount nonInlineCalls; - jsrefcount constructs; - - /* Scope lock and property metering. */ - jsrefcount claimAttempts; - jsrefcount claimedScopes; - jsrefcount deadContexts; - jsrefcount deadlocksAvoided; - jsrefcount liveScopes; - jsrefcount sharedScopes; - jsrefcount totalScopes; - jsrefcount badUndependStrings; - jsrefcount liveScopeProps; - jsrefcount totalScopeProps; - jsrefcount livePropTreeNodes; - jsrefcount duplicatePropTreeNodes; - jsrefcount totalPropTreeNodes; - jsrefcount propTreeKidsChunks; - jsrefcount middleDeleteFixups; - - /* String instrumentation. */ - jsrefcount liveStrings; - jsrefcount totalStrings; - jsrefcount liveDependentStrings; - jsrefcount totalDependentStrings; - double lengthSum; - double lengthSquaredSum; - double strdepLengthSum; - double strdepLengthSquaredSum; -#endif -}; - -#ifdef DEBUG -# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) -# define JS_RUNTIME_UNMETER(rt, which) JS_ATOMIC_DECREMENT(&(rt)->which) -#else -# define JS_RUNTIME_METER(rt, which) /* nothing */ -# define JS_RUNTIME_UNMETER(rt, which) /* nothing */ -#endif - -#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); -#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); - -#ifdef JS_ARGUMENT_FORMATTER_DEFINED -/* - * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to - * formatter functions. Elements are sorted in non-increasing format string - * length order. - */ -struct JSArgumentFormatMap { - const char *format; - size_t length; - JSArgumentFormatter formatter; - JSArgumentFormatMap *next; -}; -#endif - -struct JSStackHeader { - uintN nslots; - JSStackHeader *down; -}; - -#define JS_STACK_SEGMENT(sh) ((jsval *)(sh) + 2) - -/* - * Key and entry types for the JSContext.resolvingTable hash table, typedef'd - * here because all consumers need to see these declarations (and not just the - * typedef names, as would be the case for an opaque pointer-to-typedef'd-type - * declaration), along with cx->resolvingTable. - */ -typedef struct JSResolvingKey { - JSObject *obj; - jsid id; -} JSResolvingKey; - -typedef struct JSResolvingEntry { - JSDHashEntryHdr hdr; - JSResolvingKey key; - uint32 flags; -} JSResolvingEntry; - -#define JSRESFLAG_LOOKUP 0x1 /* resolving id from lookup */ -#define JSRESFLAG_WATCH 0x2 /* resolving id from watch */ - -typedef struct JSLocalRootChunk JSLocalRootChunk; - -#define JSLRS_CHUNK_SHIFT 8 -#define JSLRS_CHUNK_SIZE JS_BIT(JSLRS_CHUNK_SHIFT) -#define JSLRS_CHUNK_MASK JS_BITMASK(JSLRS_CHUNK_SHIFT) - -struct JSLocalRootChunk { - jsval roots[JSLRS_CHUNK_SIZE]; - JSLocalRootChunk *down; -}; - -typedef struct JSLocalRootStack { - uint32 scopeMark; - uint32 rootCount; - JSLocalRootChunk *topChunk; - JSLocalRootChunk firstChunk; -} JSLocalRootStack; - -#define JSLRS_NULL_MARK ((uint32) -1) - -typedef struct JSTempValueRooter JSTempValueRooter; -typedef void -(* JS_DLL_CALLBACK JSTempValueMarker)(JSContext *cx, JSTempValueRooter *tvr); - -typedef union JSTempValueUnion { - jsval value; - JSObject *object; - JSString *string; - void *gcthing; - JSTempValueMarker marker; - JSScopeProperty *sprop; - JSWeakRoots *weakRoots; - jsval *array; -} JSTempValueUnion; - -/* - * The following allows to reinterpret JSTempValueUnion.object as jsval using - * the tagging property of a generic jsval described below. - */ -JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(jsval)); -JS_STATIC_ASSERT(sizeof(JSTempValueUnion) == sizeof(JSObject *)); - -/* - * Context-linked stack of temporary GC roots. - * - * If count is -1, then u.value contains the single value or GC-thing to root. - * If count is -2, then u.marker holds a mark hook called to mark the values. - * If count is -3, then u.sprop points to the property tree node to mark. - * If count is -4, then u.weakRoots points to saved weak roots. - * If count >= 0, then u.array points to a stack-allocated vector of jsvals. - * - * To root a single GC-thing pointer, which need not be tagged and stored as a - * jsval, use JS_PUSH_TEMP_ROOT_GCTHING. The macro reinterprets an arbitrary - * GC-thing as jsval. It works because a GC-thing is aligned on a 0 mod 8 - * boundary, and object has the 0 jsval tag. So any GC-thing may be tagged as - * if it were an object and untagged, if it's then used only as an opaque - * pointer until discriminated by other means than tag bits (this is how the - * GC mark function uses its |thing| parameter -- it consults GC-thing flags - * stored separately from the thing to decide the type of thing). - * - * JS_PUSH_TEMP_ROOT_OBJECT and JS_PUSH_TEMP_ROOT_STRING are type-safe - * alternatives to JS_PUSH_TEMP_ROOT_GCTHING for JSObject and JSString. They - * also provide a simple way to get a single pointer to rooted JSObject or - * JSString via JS_PUSH_TEMP_ROOT_(OBJECT|STRTING)(cx, NULL, &tvr). Then - * &tvr.u.object or tvr.u.string gives the necessary pointer, which puns - * tvr.u.value safely because JSObject * and JSString * are GC-things and, as - * such, their tag bits are all zeroes. - * - * If you need to protect a result value that flows out of a C function across - * several layers of other functions, use the js_LeaveLocalRootScopeWithResult - * internal API (see further below) instead. - */ -struct JSTempValueRooter { - JSTempValueRooter *down; - ptrdiff_t count; - JSTempValueUnion u; -}; - -#define JSTVU_SINGLE (-1) -#define JSTVU_MARKER (-2) -#define JSTVU_SPROP (-3) -#define JSTVU_WEAK_ROOTS (-4) - -#define JS_PUSH_TEMP_ROOT_COMMON(cx,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT((cx)->tempValueRooters != (tvr)); \ - (tvr)->down = (cx)->tempValueRooters; \ - (cx)->tempValueRooters = (tvr); \ - JS_END_MACRO - -#define JS_PUSH_SINGLE_TEMP_ROOT(cx,val,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.value = val; \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT(cx,cnt,arr,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT((ptrdiff_t)(cnt) >= 0); \ - (tvr)->count = (ptrdiff_t)(cnt); \ - (tvr)->u.array = (arr); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_MARKER(cx,marker_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_MARKER; \ - (tvr)->u.marker = (marker_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_OBJECT(cx,obj,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.object = (obj); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_STRING(cx,str,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.string = (str); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_GCTHING(cx,thing,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT(JSVAL_IS_OBJECT((jsval)thing)); \ - (tvr)->count = JSTVU_SINGLE; \ - (tvr)->u.gcthing = (thing); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_POP_TEMP_ROOT(cx,tvr) \ - JS_BEGIN_MACRO \ - JS_ASSERT((cx)->tempValueRooters == (tvr)); \ - (cx)->tempValueRooters = (tvr)->down; \ - JS_END_MACRO - -#define JS_TEMP_ROOT_EVAL(cx,cnt,val,expr) \ - JS_BEGIN_MACRO \ - JSTempValueRooter tvr; \ - JS_PUSH_TEMP_ROOT(cx, cnt, val, &tvr); \ - (expr); \ - JS_POP_TEMP_ROOT(cx, &tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_SPROP(cx,sprop_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_SPROP; \ - (tvr)->u.sprop = (sprop_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -#define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \ - JS_BEGIN_MACRO \ - (tvr)->count = JSTVU_WEAK_ROOTS; \ - (tvr)->u.weakRoots = (weakRoots_); \ - JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \ - JS_END_MACRO - -struct JSContext { - /* JSRuntime contextList linkage. */ - JSCList links; - - /* Interpreter activation count. */ - uintN interpLevel; - - /* Limit pointer for checking stack consumption during recursion. */ - jsuword stackLimit; - - /* Runtime version control identifier and equality operators. */ - uint16 version; - jsbytecode jsop_eq; - jsbytecode jsop_ne; - - /* Data shared by threads in an address space. */ - JSRuntime *runtime; - - /* Stack arena pool and frame pointer register. */ - JSArenaPool stackPool; - JSStackFrame *fp; - - /* Temporary arena pool used while compiling and decompiling. */ - JSArenaPool tempPool; - - /* Top-level object and pointer to top stack frame's scope chain. */ - JSObject *globalObject; - - /* Storage to root recently allocated GC things and script result. */ - JSWeakRoots weakRoots; - - /* Regular expression class statics (XXX not shared globally). */ - JSRegExpStatics regExpStatics; - - /* State for object and array toSource conversion. */ - JSSharpObjectMap sharpObjectMap; - - /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */ - JSArgumentFormatMap *argumentFormatMap; - - /* Last message string and trace file for debugging. */ - char *lastMessage; -#ifdef DEBUG - void *tracefp; -#endif - - /* Per-context optional user callbacks. */ - JSBranchCallback branchCallback; - JSErrorReporter errorReporter; - - /* Client opaque pointer */ - void *data; - - /* GC and thread-safe state. */ - JSStackFrame *dormantFrameChain; /* dormant stack frame to scan */ -#ifdef JS_THREADSAFE - JSThread *thread; - jsrefcount requestDepth; - JSScope *scopeToShare; /* weak reference, see jslock.c */ - JSScope *lockedSealedScope; /* weak ref, for low-cost sealed - scope locking */ - JSCList threadLinks; /* JSThread contextList linkage */ - -#define CX_FROM_THREAD_LINKS(tl) \ - ((JSContext *)((char *)(tl) - offsetof(JSContext, threadLinks))) -#endif - -#if JS_HAS_LVALUE_RETURN - /* - * Secondary return value from native method called on the left-hand side - * of an assignment operator. The native should store the object in which - * to set a property in *rval, and return the property's id expressed as a - * jsval by calling JS_SetCallReturnValue2(cx, idval). - */ - jsval rval2; - JSPackedBool rval2set; -#endif - -#if JS_HAS_XML_SUPPORT - /* - * Bit-set formed from binary exponentials of the XML_* tiny-ids defined - * for boolean settings in jsxml.c, plus an XSF_CACHE_VALID bit. Together - * these act as a cache of the boolean XML.ignore* and XML.prettyPrinting - * property values associated with this context's global object. - */ - uint8 xmlSettingFlags; -#endif - - /* - * True if creating an exception object, to prevent runaway recursion. - * NB: creatingException packs with rval2set, #if JS_HAS_LVALUE_RETURN; - * with xmlSettingFlags, #if JS_HAS_XML_SUPPORT; and with throwing below. - */ - JSPackedBool creatingException; - - /* - * Exception state -- the exception member is a GC root by definition. - * NB: throwing packs with creatingException and rval2set, above. - */ - JSPackedBool throwing; /* is there a pending exception? */ - jsval exception; /* most-recently-thrown exception */ - /* Flag to indicate that we run inside gcCallback(cx, JSGC_MARK_END). */ - JSPackedBool insideGCMarkCallback; - - /* Per-context options. */ - uint32 options; /* see jsapi.h for JSOPTION_* */ - - /* Locale specific callbacks for string conversion. */ - JSLocaleCallbacks *localeCallbacks; - - /* - * cx->resolvingTable is non-null and non-empty if we are initializing - * standard classes lazily, or if we are otherwise recursing indirectly - * from js_LookupProperty through a JSClass.resolve hook. It is used to - * limit runaway recursion (see jsapi.c and jsobj.c). - */ - JSDHashTable *resolvingTable; - - /* PDL of stack headers describing stack slots not rooted by argv, etc. */ - JSStackHeader *stackHeaders; - - /* Optional stack of heap-allocated scoped local GC roots. */ - JSLocalRootStack *localRootStack; - - /* Stack of thread-stack-allocated temporary GC roots. */ - JSTempValueRooter *tempValueRooters; - -#ifdef GC_MARK_DEBUG - /* Top of the GC mark stack. */ - void *gcCurrentMarkNode; -#endif -}; - -#ifdef JS_THREADSAFE -# define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0) -#endif - -#ifdef __cplusplus -/* FIXME(bug 332648): Move this into a public header. */ -class JSAutoTempValueRooter -{ - public: - JSAutoTempValueRooter(JSContext *cx, size_t len, jsval *vec) - : mContext(cx) { - JS_PUSH_TEMP_ROOT(mContext, len, vec, &mTvr); - } - JSAutoTempValueRooter(JSContext *cx, jsval v) - : mContext(cx) { - JS_PUSH_SINGLE_TEMP_ROOT(mContext, v, &mTvr); - } - - ~JSAutoTempValueRooter() { - JS_POP_TEMP_ROOT(mContext, &mTvr); - } - - private: - static void *operator new(size_t); - static void operator delete(void *, size_t); - - JSContext *mContext; - JSTempValueRooter mTvr; -}; -#endif - -/* - * Slightly more readable macros for testing per-context option settings (also - * to hide bitset implementation detail). - * - * JSOPTION_XML must be handled specially in order to propagate from compile- - * to run-time (from cx->options to script->version/cx->version). To do that, - * we copy JSOPTION_XML from cx->options into cx->version as JSVERSION_HAS_XML - * whenever options are set, and preserve this XML flag across version number - * changes done via the JS_SetVersion API. - * - * But when executing a script or scripted function, the interpreter changes - * cx->version, including the XML flag, to script->version. Thus JSOPTION_XML - * is a compile-time option that causes a run-time version change during each - * activation of the compiled script. That version change has the effect of - * changing JS_HAS_XML_OPTION, so that any compiling done via eval enables XML - * support. If an XML-enabled script or function calls a non-XML function, - * the flag bit will be cleared during the callee's activation. - * - * Note that JS_SetVersion API calls never pass JSVERSION_HAS_XML or'd into - * that API's version parameter. - * - * Note also that script->version must contain this XML option flag in order - * for XDR'ed scripts to serialize and deserialize with that option preserved - * for detection at run-time. We can't copy other compile-time options into - * script->version because that would break backward compatibility (certain - * other options, e.g. JSOPTION_VAROBJFIX, are analogous to JSOPTION_XML). - */ -#define JS_HAS_OPTION(cx,option) (((cx)->options & (option)) != 0) -#define JS_HAS_STRICT_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_STRICT) -#define JS_HAS_WERROR_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_WERROR) -#define JS_HAS_COMPILE_N_GO_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_COMPILE_N_GO) -#define JS_HAS_ATLINE_OPTION(cx) JS_HAS_OPTION(cx, JSOPTION_ATLINE) - -#define JSVERSION_MASK 0x0FFF /* see JSVersion in jspubtd.h */ -#define JSVERSION_HAS_XML 0x1000 /* flag induced by XML option */ - -#define JSVERSION_NUMBER(cx) ((cx)->version & JSVERSION_MASK) -#define JS_HAS_XML_OPTION(cx) ((cx)->version & JSVERSION_HAS_XML || \ - JSVERSION_NUMBER(cx) >= JSVERSION_1_6) - -#define JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) \ - JS_HAS_OPTION(cx, JSOPTION_NATIVE_BRANCH_CALLBACK) - -/* - * Wrappers for the JSVERSION_IS_* macros from jspubtd.h taking JSContext *cx - * and masking off the XML flag and any other high order bits. - */ -#define JS_VERSION_IS_ECMA(cx) JSVERSION_IS_ECMA(JSVERSION_NUMBER(cx)) - -/* - * Common subroutine of JS_SetVersion and js_SetVersion, to update per-context - * data that depends on version. - */ -extern void -js_OnVersionChange(JSContext *cx); - -/* - * Unlike the JS_SetVersion API, this function stores JSVERSION_HAS_XML and - * any future non-version-number flags induced by compiler options. - */ -extern void -js_SetVersion(JSContext *cx, JSVersion version); - -/* - * Create and destroy functions for JSContext, which is manually allocated - * and exclusively owned. - */ -extern JSContext * -js_NewContext(JSRuntime *rt, size_t stackChunkSize); - -extern void -js_DestroyContext(JSContext *cx, JSDestroyContextMode mode); - -/* - * Return true if cx points to a context in rt->contextList, else return false. - * NB: the caller (see jslock.c:ClaimScope) must hold rt->gcLock. - */ -extern JSBool -js_ValidContextPointer(JSRuntime *rt, JSContext *cx); - -/* - * If unlocked, acquire and release rt->gcLock around *iterp update; otherwise - * the caller must be holding rt->gcLock. - */ -extern JSContext * -js_ContextIterator(JSRuntime *rt, JSBool unlocked, JSContext **iterp); - -/* - * JSClass.resolve and watchpoint recursion damping machinery. - */ -extern JSBool -js_StartResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry **entryp); - -extern void -js_StopResolving(JSContext *cx, JSResolvingKey *key, uint32 flag, - JSResolvingEntry *entry, uint32 generation); - -/* - * Local root set management. - * - * NB: the jsval parameters below may be properly tagged jsvals, or GC-thing - * pointers cast to (jsval). This relies on JSObject's tag being zero, but - * on the up side it lets us push int-jsval-encoded scopeMark values on the - * local root stack. - */ -extern JSBool -js_EnterLocalRootScope(JSContext *cx); - -#define js_LeaveLocalRootScope(cx) \ - js_LeaveLocalRootScopeWithResult(cx, JSVAL_NULL) - -extern void -js_LeaveLocalRootScopeWithResult(JSContext *cx, jsval rval); - -extern void -js_ForgetLocalRoot(JSContext *cx, jsval v); - -extern int -js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v); - -extern void -js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs); - -/* - * Report an exception, which is currently realized as a printf-style format - * string and its arguments. - */ -typedef enum JSErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "js.msg" -#undef MSG_DEF - JSErr_Limit -} JSErrNum; - -extern const JSErrorFormatString * -js_GetErrorMessage(void *userRef, const char *locale, const uintN errorNumber); - -#ifdef va_start -extern JSBool -js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap); - -extern JSBool -js_ReportErrorNumberVA(JSContext *cx, uintN flags, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - JSBool charArgs, va_list ap); - -extern JSBool -js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, - void *userRef, const uintN errorNumber, - char **message, JSErrorReport *reportp, - JSBool *warningp, JSBool charArgs, va_list ap); -#endif - -extern void -js_ReportOutOfMemory(JSContext *cx); - -/* - * Report an exception using a previously composed JSErrorReport. - * XXXbe remove from "friend" API - */ -extern JS_FRIEND_API(void) -js_ReportErrorAgain(JSContext *cx, const char *message, JSErrorReport *report); - -extern void -js_ReportIsNotDefined(JSContext *cx, const char *name); - -extern JSErrorFormatString js_ErrorFormatString[JSErr_Limit]; - -/* - * See JS_SetThreadStackLimit in jsapi.c, where we check that the stack grows - * in the expected direction. On Unix-y systems, JS_STACK_GROWTH_DIRECTION is - * computed on the build host by jscpucfg.c and written into jsautocfg.h. The - * macro is hardcoded in jscpucfg.h on Windows and Mac systems (for historical - * reasons pre-dating autoconf usage). - */ -#if JS_STACK_GROWTH_DIRECTION > 0 -# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) < (cx)->stackLimit) -#else -# define JS_CHECK_STACK_SIZE(cx, lval) ((jsuword)&(lval) > (cx)->stackLimit) -#endif - -JS_END_EXTERN_C - -#endif /* jscntxt_h___ */ diff --git a/spidermonkey/libjs/jscompat.h b/spidermonkey/libjs/jscompat.h deleted file mode 100644 index 80d8605..0000000 --- a/spidermonkey/libjs/jscompat.h +++ /dev/null @@ -1,57 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-1999 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -*- Mode: C; tab-width: 8 -*- - * Copyright (C) 1996-1999 Netscape Communications Corporation, All Rights Reserved. - */ -#ifndef jscompat_h___ -#define jscompat_h___ -/* - * Compatibility glue for various NSPR versions. We must always define int8, - * int16, jsword, and so on to minimize differences with js/ref, no matter what - * the NSPR typedef names may be. - */ -#include "jstypes.h" -#include "jslong.h" - -typedef JSIntn intN; -typedef JSUintn uintN; -typedef JSUword jsuword; -typedef JSWord jsword; -typedef float float32; -#define allocPriv allocPool -#endif /* jscompat_h___ */ diff --git a/spidermonkey/libjs/jsconfig.h b/spidermonkey/libjs/jsconfig.h deleted file mode 100644 index d61e802..0000000 --- a/spidermonkey/libjs/jsconfig.h +++ /dev/null @@ -1,208 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS configuration macros. - */ -#ifndef JS_VERSION -#define JS_VERSION 170 -#endif - -/* - * Compile-time JS version configuration. The JS version numbers lie on the - * number line like so: - * - * 1.0 1.1 1.2 1.3 1.4 ECMAv3 1.5 1.6 - * ^ ^ - * | | - * basis for ECMAv1 close to ECMAv2 - * - * where ECMAv3 stands for ECMA-262 Edition 3. See the runtime version enum - * JSVersion in jspubtd.h. Code in the engine can therefore count on version - * <= JSVERSION_1_4 to mean "before the Third Edition of ECMA-262" and version - * > JSVERSION_1_4 to mean "at or after the Third Edition". - * - * In the (likely?) event that SpiderMonkey grows to implement JavaScript 2.0, - * or ECMA-262 Edition 4 (JS2 without certain extensions), the version number - * to use would be near 200, or greater. - * - * The JS_VERSION_ECMA_3 version is the minimal configuration conforming to - * the ECMA-262 Edition 3 specification. Use it for minimal embeddings, where - * you're sure you don't need any of the extensions disabled in this version. - * In order to facilitate testing, JS_HAS_OBJ_PROTO_PROP is defined as part of - * the JS_VERSION_ECMA_3_TEST version. - * - * To keep things sane in the modern age, where we need exceptions in order to - * implement, e.g., iterators and generators, we are dropping support for all - * versions <= 1.4. - */ -#define JS_VERSION_ECMA_3 148 -#define JS_VERSION_ECMA_3_TEST 149 - -#if JS_VERSION == JS_VERSION_ECMA_3 || \ - JS_VERSION == JS_VERSION_ECMA_3_TEST - -#define JS_HAS_STR_HTML_HELPERS 0 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 0 /* has str.substr */ -#if JS_VERSION == JS_VERSION_ECMA_3_TEST -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#else -#define JS_HAS_OBJ_PROTO_PROP 0 /* has o.__proto__ etc. */ -#endif -#define JS_HAS_OBJ_WATCHPOINT 0 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 0 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 0 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 0 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 0 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 0 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 0 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 0 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 0 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */ -#define JS_HAS_CONST 0 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION < 150 - -#error "unsupported JS_VERSION" - -#elif JS_VERSION == 150 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION == 160 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 0 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 0 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#elif JS_VERSION == 170 - -#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */ -#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */ -#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */ -#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */ -#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */ -#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */ -#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */ -#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */ -#define JS_HAS_XDR 1 /* has XDR API and internal support */ -#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */ -#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */ -#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */ -#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */ -#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */ -#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */ -#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */ -#define JS_HAS_CONST 1 /* has JS2 const as alternative var */ -#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */ -#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */ -#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */ -#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */ -#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */ -#define JS_HAS_GENERATORS 1 /* has yield in generator function */ -#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */ -#define JS_HAS_DESTRUCTURING 1 /* has [a,b] = ... or {p:a,q:b} = ... */ - -#else - -#error "unknown JS_VERSION" - -#endif - -/* Features that are present in all versions. */ -#define JS_HAS_RESERVED_JAVA_KEYWORDS 1 -#define JS_HAS_RESERVED_ECMA_KEYWORDS 1 - diff --git a/spidermonkey/libjs/jsdate.c b/spidermonkey/libjs/jsdate.c deleted file mode 100644 index 9e6697f..0000000 --- a/spidermonkey/libjs/jsdate.c +++ /dev/null @@ -1,2371 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS date methods. - */ - -/* - * "For example, OS/360 devotes 26 bytes of the permanently - * resident date-turnover routine to the proper handling of - * December 31 on leap years (when it is Day 366). That - * might have been left to the operator." - * - * Frederick Brooks, 'The Second-System Effect'. - */ - -#include "jsstddef.h" -#include -#include -#include -#include -#include -#include "jstypes.h" -#include "jsprf.h" -#include "prmjtime.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsconfig.h" -#include "jscntxt.h" -#include "jsdate.h" -#include "jsinterp.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsstr.h" - -/* - * The JS 'Date' object is patterned after the Java 'Date' object. - * Here is an script: - * - * today = new Date(); - * - * print(today.toLocaleString()); - * - * weekDay = today.getDay(); - * - * - * These Java (and ECMA-262) methods are supported: - * - * UTC - * getDate (getUTCDate) - * getDay (getUTCDay) - * getHours (getUTCHours) - * getMinutes (getUTCMinutes) - * getMonth (getUTCMonth) - * getSeconds (getUTCSeconds) - * getMilliseconds (getUTCMilliseconds) - * getTime - * getTimezoneOffset - * getYear - * getFullYear (getUTCFullYear) - * parse - * setDate (setUTCDate) - * setHours (setUTCHours) - * setMinutes (setUTCMinutes) - * setMonth (setUTCMonth) - * setSeconds (setUTCSeconds) - * setMilliseconds (setUTCMilliseconds) - * setTime - * setYear (setFullYear, setUTCFullYear) - * toGMTString (toUTCString) - * toLocaleString - * toString - * - * - * These Java methods are not supported - * - * setDay - * before - * after - * equals - * hashCode - */ - -/* - * 11/97 - jsdate.c has been rewritten to conform to the ECMA-262 language - * definition and reduce dependence on NSPR. NSPR is used to get the current - * time in milliseconds, the time zone offset, and the daylight savings time - * offset for a given time. NSPR is also used for Date.toLocaleString(), for - * locale-specific formatting, and to get a string representing the timezone. - * (Which turns out to be platform-dependent.) - * - * To do: - * (I did some performance tests by timing how long it took to run what - * I had of the js ECMA conformance tests.) - * - * - look at saving results across multiple calls to supporting - * functions; the toString functions compute some of the same values - * multiple times. Although - I took a quick stab at this, and I lost - * rather than gained. (Fractionally.) Hard to tell what compilers/processors - * are doing these days. - * - * - look at tweaking function return types to return double instead - * of int; this seems to make things run slightly faster sometimes. - * (though it could be architecture-dependent.) It'd be good to see - * how this does on win32. (Tried it on irix.) Types could use a - * general going-over. - */ - -/* - * Supporting functions - ECMA 15.9.1.* - */ - -#define HalfTimeDomain 8.64e15 -#define HoursPerDay 24.0 -#define MinutesPerDay (HoursPerDay * MinutesPerHour) -#define MinutesPerHour 60.0 -#define SecondsPerDay (MinutesPerDay * SecondsPerMinute) -#define SecondsPerHour (MinutesPerHour * SecondsPerMinute) -#define SecondsPerMinute 60.0 - -#if defined(XP_WIN) || defined(XP_OS2) -/* Work around msvc double optimization bug by making these runtime values; if - * they're available at compile time, msvc optimizes division by them by - * computing the reciprocal and multiplying instead of dividing - this loses - * when the reciprocal isn't representable in a double. - */ -static jsdouble msPerSecond = 1000.0; -static jsdouble msPerDay = SecondsPerDay * 1000.0; -static jsdouble msPerHour = SecondsPerHour * 1000.0; -static jsdouble msPerMinute = SecondsPerMinute * 1000.0; -#else -#define msPerDay (SecondsPerDay * msPerSecond) -#define msPerHour (SecondsPerHour * msPerSecond) -#define msPerMinute (SecondsPerMinute * msPerSecond) -#define msPerSecond 1000.0 -#endif - -#define Day(t) floor((t) / msPerDay) - -static jsdouble -TimeWithinDay(jsdouble t) -{ - jsdouble result; - result = fmod(t, msPerDay); - if (result < 0) - result += msPerDay; - return result; -} - -#define DaysInYear(y) ((y) % 4 == 0 && ((y) % 100 || ((y) % 400 == 0)) \ - ? 366 : 365) - -/* math here has to be f.p, because we need - * floor((1968 - 1969) / 4) == -1 - */ -#define DayFromYear(y) (365 * ((y)-1970) + floor(((y)-1969)/4.0) \ - - floor(((y)-1901)/100.0) + floor(((y)-1601)/400.0)) -#define TimeFromYear(y) (DayFromYear(y) * msPerDay) - -static jsint -YearFromTime(jsdouble t) -{ - jsint y = (jsint) floor(t /(msPerDay*365.2425)) + 1970; - jsdouble t2 = (jsdouble) TimeFromYear(y); - - if (t2 > t) { - y--; - } else { - if (t2 + msPerDay * DaysInYear(y) <= t) - y++; - } - return y; -} - -#define InLeapYear(t) (JSBool) (DaysInYear(YearFromTime(t)) == 366) - -#define DayWithinYear(t, year) ((intN) (Day(t) - DayFromYear(year))) - -/* - * The following array contains the day of year for the first day of - * each month, where index 0 is January, and day 0 is January 1. - */ -static jsdouble firstDayOfMonth[2][12] = { - {0.0, 31.0, 59.0, 90.0, 120.0, 151.0, 181.0, 212.0, 243.0, 273.0, 304.0, 334.0}, - {0.0, 31.0, 60.0, 91.0, 121.0, 152.0, 182.0, 213.0, 244.0, 274.0, 305.0, 335.0} -}; - -#define DayFromMonth(m, leap) firstDayOfMonth[leap][(intN)m]; - -static intN -MonthFromTime(jsdouble t) -{ - intN d, step; - jsint year = YearFromTime(t); - d = DayWithinYear(t, year); - - if (d < (step = 31)) - return 0; - step += (InLeapYear(t) ? 29 : 28); - if (d < step) - return 1; - if (d < (step += 31)) - return 2; - if (d < (step += 30)) - return 3; - if (d < (step += 31)) - return 4; - if (d < (step += 30)) - return 5; - if (d < (step += 31)) - return 6; - if (d < (step += 31)) - return 7; - if (d < (step += 30)) - return 8; - if (d < (step += 31)) - return 9; - if (d < (step += 30)) - return 10; - return 11; -} - -static intN -DateFromTime(jsdouble t) -{ - intN d, step, next; - jsint year = YearFromTime(t); - d = DayWithinYear(t, year); - - if (d <= (next = 30)) - return d + 1; - step = next; - next += (InLeapYear(t) ? 29 : 28); - if (d <= next) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - if (d <= (next += 31)) - return d - step; - step = next; - if (d <= (next += 30)) - return d - step; - step = next; - return d - step; -} - -static intN -WeekDay(jsdouble t) -{ - jsint result; - result = (jsint) Day(t) + 4; - result = result % 7; - if (result < 0) - result += 7; - return (intN) result; -} - -#define MakeTime(hour, min, sec, ms) \ -((((hour) * MinutesPerHour + (min)) * SecondsPerMinute + (sec)) * msPerSecond + (ms)) - -static jsdouble -MakeDay(jsdouble year, jsdouble month, jsdouble date) -{ - JSBool leap; - jsdouble yearday; - jsdouble monthday; - - year += floor(month / 12); - - month = fmod(month, 12.0); - if (month < 0) - month += 12; - - leap = (DaysInYear((jsint) year) == 366); - - yearday = floor(TimeFromYear(year) / msPerDay); - monthday = DayFromMonth(month, leap); - - return yearday + monthday + date - 1; -} - -#define MakeDate(day, time) ((day) * msPerDay + (time)) - -/* - * Years and leap years on which Jan 1 is a Sunday, Monday, etc. - * - * yearStartingWith[0][i] is an example non-leap year where - * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. - * - * yearStartingWith[1][i] is an example leap year where - * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc. - */ -static jsint yearStartingWith[2][7] = { - {1978, 1973, 1974, 1975, 1981, 1971, 1977}, - {1984, 1996, 1980, 1992, 1976, 1988, 1972} -}; - -/* - * Find a year for which any given date will fall on the same weekday. - * - * This function should be used with caution when used other than - * for determining DST; it hasn't been proven not to produce an - * incorrect year for times near year boundaries. - */ -static jsint -EquivalentYearForDST(jsint year) -{ - jsint day; - JSBool isLeapYear; - - day = (jsint) DayFromYear(year) + 4; - day = day % 7; - if (day < 0) - day += 7; - - isLeapYear = (DaysInYear(year) == 366); - - return yearStartingWith[isLeapYear][day]; -} - -/* LocalTZA gets set by js_InitDateClass() */ -static jsdouble LocalTZA; - -static jsdouble -DaylightSavingTA(jsdouble t) -{ - volatile int64 PR_t; - int64 ms2us; - int64 offset; - jsdouble result; - - /* abort if NaN */ - if (JSDOUBLE_IS_NaN(t)) - return t; - - /* - * If earlier than 1970 or after 2038, potentially beyond the ken of - * many OSes, map it to an equivalent year before asking. - */ - if (t < 0.0 || t > 2145916800000.0) { - jsint year; - jsdouble day; - - year = EquivalentYearForDST(YearFromTime(t)); - day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - t = MakeDate(day, TimeWithinDay(t)); - } - - /* put our t in an LL, and map it to usec for prtime */ - JSLL_D2L(PR_t, t); - JSLL_I2L(ms2us, PRMJ_USEC_PER_MSEC); - JSLL_MUL(PR_t, PR_t, ms2us); - - offset = PRMJ_DSTOffset(PR_t); - - JSLL_DIV(offset, offset, ms2us); - JSLL_L2D(result, offset); - return result; -} - - -#define AdjustTime(t) fmod(LocalTZA + DaylightSavingTA(t), msPerDay) - -#define LocalTime(t) ((t) + AdjustTime(t)) - -static jsdouble -UTC(jsdouble t) -{ - return t - AdjustTime(t - LocalTZA); -} - -static intN -HourFromTime(jsdouble t) -{ - intN result = (intN) fmod(floor(t/msPerHour), HoursPerDay); - if (result < 0) - result += (intN)HoursPerDay; - return result; -} - -static intN -MinFromTime(jsdouble t) -{ - intN result = (intN) fmod(floor(t / msPerMinute), MinutesPerHour); - if (result < 0) - result += (intN)MinutesPerHour; - return result; -} - -static intN -SecFromTime(jsdouble t) -{ - intN result = (intN) fmod(floor(t / msPerSecond), SecondsPerMinute); - if (result < 0) - result += (intN)SecondsPerMinute; - return result; -} - -static intN -msFromTime(jsdouble t) -{ - intN result = (intN) fmod(t, msPerSecond); - if (result < 0) - result += (intN)msPerSecond; - return result; -} - -#define TIMECLIP(d) ((JSDOUBLE_IS_FINITE(d) \ - && !((d < 0 ? -d : d) > HalfTimeDomain)) \ - ? js_DoubleToInteger(d + (+0.)) : *cx->runtime->jsNaN) - -/** - * end of ECMA 'support' functions - */ - -/* - * Other Support routines and definitions - */ - -JSClass js_DateClass = { - js_Date_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Date), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -/* for use by date_parse */ - -static const char* wtb[] = { - "am", "pm", - "monday", "tuesday", "wednesday", "thursday", "friday", - "saturday", "sunday", - "january", "february", "march", "april", "may", "june", - "july", "august", "september", "october", "november", "december", - "gmt", "ut", "utc", - "est", "edt", - "cst", "cdt", - "mst", "mdt", - "pst", "pdt" - /* time zone table needs to be expanded */ -}; - -static int ttb[] = { - -1, -2, 0, 0, 0, 0, 0, 0, 0, /* AM/PM */ - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 10000 + 0, 10000 + 0, 10000 + 0, /* GMT/UT/UTC */ - 10000 + 5 * 60, 10000 + 4 * 60, /* EST/EDT */ - 10000 + 6 * 60, 10000 + 5 * 60, /* CST/CDT */ - 10000 + 7 * 60, 10000 + 6 * 60, /* MST/MDT */ - 10000 + 8 * 60, 10000 + 7 * 60 /* PST/PDT */ -}; - -/* helper for date_parse */ -static JSBool -date_regionMatches(const char* s1, int s1off, const jschar* s2, int s2off, - int count, int ignoreCase) -{ - JSBool result = JS_FALSE; - /* return true if matches, otherwise, false */ - - while (count > 0 && s1[s1off] && s2[s2off]) { - if (ignoreCase) { - if (JS_TOLOWER((jschar)s1[s1off]) != JS_TOLOWER(s2[s2off])) { - break; - } - } else { - if ((jschar)s1[s1off] != s2[s2off]) { - break; - } - } - s1off++; - s2off++; - count--; - } - - if (count == 0) { - result = JS_TRUE; - } - - return result; -} - -/* find UTC time from given date... no 1900 correction! */ -static jsdouble -date_msecFromDate(jsdouble year, jsdouble mon, jsdouble mday, jsdouble hour, - jsdouble min, jsdouble sec, jsdouble msec) -{ - jsdouble day; - jsdouble msec_time; - jsdouble result; - - day = MakeDay(year, mon, mday); - msec_time = MakeTime(hour, min, sec, msec); - result = MakeDate(day, msec_time); - return result; -} - -/* - * See ECMA 15.9.4.[3-10]; - */ -/* XXX this function must be above date_parseString to avoid a - horrid bug in the Win16 1.52 compiler */ -#define MAXARGS 7 -static JSBool -date_UTC(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble array[MAXARGS]; - uintN loop; - jsdouble d; - - for (loop = 0; loop < MAXARGS; loop++) { - if (loop < argc) { - if (!js_ValueToNumber(cx, argv[loop], &d)) - return JS_FALSE; - /* return NaN if any arg is NaN */ - if (!JSDOUBLE_IS_FINITE(d)) { - return js_NewNumberValue(cx, d, rval); - } - array[loop] = floor(d); - } else { - array[loop] = 0; - } - } - - /* adjust 2-digit years into the 20th century */ - if (array[0] >= 0 && array[0] <= 99) - array[0] += 1900; - - /* if we got a 0 for 'date' (which is out of range) - * pretend it's a 1. (So Date.UTC(1972, 5) works) */ - if (array[2] < 1) - array[2] = 1; - - d = date_msecFromDate(array[0], array[1], array[2], - array[3], array[4], array[5], array[6]); - d = TIMECLIP(d); - - return js_NewNumberValue(cx, d, rval); -} - -static JSBool -date_parseString(JSString *str, jsdouble *result) -{ - jsdouble msec; - - const jschar *s = JSSTRING_CHARS(str); - size_t limit = JSSTRING_LENGTH(str); - size_t i = 0; - int year = -1; - int mon = -1; - int mday = -1; - int hour = -1; - int min = -1; - int sec = -1; - int c = -1; - int n = -1; - jsdouble tzoffset = -1; /* was an int, overflowed on win16!!! */ - int prevc = 0; - JSBool seenplusminus = JS_FALSE; - int temp; - JSBool seenmonthname = JS_FALSE; - - if (limit == 0) - goto syntax; - while (i < limit) { - c = s[i]; - i++; - if (c <= ' ' || c == ',' || c == '-') { - if (c == '-' && '0' <= s[i] && s[i] <= '9') { - prevc = c; - } - continue; - } - if (c == '(') { /* comments) */ - int depth = 1; - while (i < limit) { - c = s[i]; - i++; - if (c == '(') depth++; - else if (c == ')') - if (--depth <= 0) - break; - } - continue; - } - if ('0' <= c && c <= '9') { - n = c - '0'; - while (i < limit && '0' <= (c = s[i]) && c <= '9') { - n = n * 10 + c - '0'; - i++; - } - - /* allow TZA before the year, so - * 'Wed Nov 05 21:49:11 GMT-0800 1997' - * works */ - - /* uses of seenplusminus allow : in TZA, so Java - * no-timezone style of GMT+4:30 works - */ - - if ((prevc == '+' || prevc == '-')/* && year>=0 */) { - /* make ':' case below change tzoffset */ - seenplusminus = JS_TRUE; - - /* offset */ - if (n < 24) - n = n * 60; /* EG. "GMT-3" */ - else - n = n % 100 + n / 100 * 60; /* eg "GMT-0430" */ - if (prevc == '+') /* plus means east of GMT */ - n = -n; - if (tzoffset != 0 && tzoffset != -1) - goto syntax; - tzoffset = n; - } else if (prevc == '/' && mon >= 0 && mday >= 0 && year < 0) { - if (c <= ' ' || c == ',' || c == '/' || i >= limit) - year = n; - else - goto syntax; - } else if (c == ':') { - if (hour < 0) - hour = /*byte*/ n; - else if (min < 0) - min = /*byte*/ n; - else - goto syntax; - } else if (c == '/') { - /* until it is determined that mon is the actual - month, keep it as 1-based rather than 0-based */ - if (mon < 0) - mon = /*byte*/ n; - else if (mday < 0) - mday = /*byte*/ n; - else - goto syntax; - } else if (i < limit && c != ',' && c > ' ' && c != '-' && c != '(') { - goto syntax; - } else if (seenplusminus && n < 60) { /* handle GMT-3:30 */ - if (tzoffset < 0) - tzoffset -= n; - else - tzoffset += n; - } else if (hour >= 0 && min < 0) { - min = /*byte*/ n; - } else if (prevc == ':' && min >= 0 && sec < 0) { - sec = /*byte*/ n; - } else if (mon < 0) { - mon = /*byte*/n; - } else if (mon >= 0 && mday < 0) { - mday = /*byte*/ n; - } else if (mon >= 0 && mday >= 0 && year < 0) { - year = n; - } else { - goto syntax; - } - prevc = 0; - } else if (c == '/' || c == ':' || c == '+' || c == '-') { - prevc = c; - } else { - size_t st = i - 1; - int k; - while (i < limit) { - c = s[i]; - if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))) - break; - i++; - } - if (i <= st + 1) - goto syntax; - for (k = (sizeof(wtb)/sizeof(char*)); --k >= 0;) - if (date_regionMatches(wtb[k], 0, s, st, i-st, 1)) { - int action = ttb[k]; - if (action != 0) { - if (action < 0) { - /* - * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as - * 12:30, instead of blindly adding 12 if PM. - */ - JS_ASSERT(action == -1 || action == -2); - if (hour > 12 || hour < 0) { - goto syntax; - } else { - if (action == -1 && hour == 12) { /* am */ - hour = 0; - } else if (action == -2 && hour != 12) { /* pm */ - hour += 12; - } - } - } else if (action <= 13) { /* month! */ - /* Adjust mon to be 1-based until the final values - for mon, mday and year are adjusted below */ - if (seenmonthname) { - goto syntax; - } - seenmonthname = JS_TRUE; - temp = /*byte*/ (action - 2) + 1; - - if (mon < 0) { - mon = temp; - } else if (mday < 0) { - mday = mon; - mon = temp; - } else if (year < 0) { - year = mon; - mon = temp; - } else { - goto syntax; - } - } else { - tzoffset = action - 10000; - } - } - break; - } - if (k < 0) - goto syntax; - prevc = 0; - } - } - if (year < 0 || mon < 0 || mday < 0) - goto syntax; - /* - Case 1. The input string contains an English month name. - The form of the string can be month f l, or f month l, or - f l month which each evaluate to the same date. - If f and l are both greater than or equal to 70, or - both less than 70, the date is invalid. - The year is taken to be the greater of the values f, l. - If the year is greater than or equal to 70 and less than 100, - it is considered to be the number of years after 1900. - Case 2. The input string is of the form "f/m/l" where f, m and l are - integers, e.g. 7/16/45. - Adjust the mon, mday and year values to achieve 100% MSIE - compatibility. - a. If 0 <= f < 70, f/m/l is interpreted as month/day/year. - i. If year < 100, it is the number of years after 1900 - ii. If year >= 100, it is the number of years after 0. - b. If 70 <= f < 100 - i. If m < 70, f/m/l is interpreted as - year/month/day where year is the number of years after - 1900. - ii. If m >= 70, the date is invalid. - c. If f >= 100 - i. If m < 70, f/m/l is interpreted as - year/month/day where year is the number of years after 0. - ii. If m >= 70, the date is invalid. - */ - if (seenmonthname) { - if ((mday >= 70 && year >= 70) || (mday < 70 && year < 70)) { - goto syntax; - } - if (mday > year) { - temp = year; - year = mday; - mday = temp; - } - if (year >= 70 && year < 100) { - year += 1900; - } - } else if (mon < 70) { /* (a) month/day/year */ - if (year < 100) { - year += 1900; - } - } else if (mon < 100) { /* (b) year/month/day */ - if (mday < 70) { - temp = year; - year = mon + 1900; - mon = mday; - mday = temp; - } else { - goto syntax; - } - } else { /* (c) year/month/day */ - if (mday < 70) { - temp = year; - year = mon; - mon = mday; - mday = temp; - } else { - goto syntax; - } - } - mon -= 1; /* convert month to 0-based */ - if (sec < 0) - sec = 0; - if (min < 0) - min = 0; - if (hour < 0) - hour = 0; - if (tzoffset == -1) { /* no time zone specified, have to use local */ - jsdouble msec_time; - msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - - *result = UTC(msec_time); - return JS_TRUE; - } - - msec = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - msec += tzoffset * msPerMinute; - *result = msec; - return JS_TRUE; - -syntax: - /* syntax error */ - *result = 0; - return JS_FALSE; -} - -static JSBool -date_parse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble result; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - if (!date_parseString(str, &result)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - - result = TIMECLIP(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_now(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - int64 us, ms, us2ms; - jsdouble msec_time; - - us = PRMJ_Now(); - JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); - JSLL_DIV(ms, us, us2ms); - JSLL_L2D(msec_time, ms); - - return js_NewDoubleValue(cx, msec_time, rval); -} - -/* - * Check that obj is an object of class Date, and get the date value. - * Return NULL on failure. - */ -static jsdouble * -date_getProlog(JSContext *cx, JSObject *obj, jsval *argv) -{ - if (!JS_InstanceOf(cx, obj, &js_DateClass, argv)) - return NULL; - return JSVAL_TO_DOUBLE(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)); -} - -/* - * See ECMA 15.9.5.4 thru 15.9.5.23 - */ -static JSBool -date_getTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - return js_NewNumberValue(cx, *date, rval); -} - -static JSBool -date_getYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble *date; - jsdouble result; - - date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = YearFromTime(LocalTime(result)); - - /* Follow ECMA-262 to the letter, contrary to IE JScript. */ - result -= 1900; - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = YearFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = YearFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MonthFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCMonth(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MonthFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = LocalTime(result); - result = DateFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCDate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = DateFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = LocalTime(result); - result = WeekDay(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCDay(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = WeekDay(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = HourFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCHours(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = HourFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MinFromTime(LocalTime(result)); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = MinFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -/* Date.getSeconds is mapped to getUTCSeconds */ - -static JSBool -date_getUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = SecFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -/* Date.getMilliseconds is mapped to getUTCMilliseconds */ - -static JSBool -date_getUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - result = msFromTime(result); - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_getTimezoneOffset(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - result = *date; - - /* - * Return the time zone offset in minutes for the current locale - * that is appropriate for this time. This value would be a - * constant except for daylight savings time. - */ - result = (result - LocalTime(result)) / msPerMinute; - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_setTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble result; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - if (!js_ValueToNumber(cx, argv[0], &result)) - return JS_FALSE; - - result = TIMECLIP(result); - - *date = result; - return js_NewNumberValue(cx, result, rval); -} - -static JSBool -date_makeTime(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - uintN maxargs, JSBool local, jsval *rval) -{ - uintN i; - jsdouble args[4], *argp, *stop; - jsdouble hour, min, sec, msec; - jsdouble lorutime; /* Local or UTC version of *date */ - - jsdouble msec_time; - jsdouble result; - - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - - /* just return NaN if the date is already NaN */ - if (!JSDOUBLE_IS_FINITE(result)) - return js_NewNumberValue(cx, result, rval); - - /* Satisfy the ECMA rule that if a function is called with - * fewer arguments than the specified formal arguments, the - * remaining arguments are set to undefined. Seems like all - * the Date.setWhatever functions in ECMA are only varargs - * beyond the first argument; this should be set to undefined - * if it's not given. This means that "d = new Date(); - * d.setMilliseconds()" returns NaN. Blech. - */ - if (argc == 0) - argc = 1; /* should be safe, because length of all setters is 1 */ - else if (argc > maxargs) - argc = maxargs; /* clamp argc */ - - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &args[i])) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(args[i])) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); - } - args[i] = js_DoubleToInteger(args[i]); - } - - if (local) - lorutime = LocalTime(result); - else - lorutime = result; - - argp = args; - stop = argp + argc; - if (maxargs >= 4 && argp < stop) - hour = *argp++; - else - hour = HourFromTime(lorutime); - - if (maxargs >= 3 && argp < stop) - min = *argp++; - else - min = MinFromTime(lorutime); - - if (maxargs >= 2 && argp < stop) - sec = *argp++; - else - sec = SecFromTime(lorutime); - - if (maxargs >= 1 && argp < stop) - msec = *argp; - else - msec = msFromTime(lorutime); - - msec_time = MakeTime(hour, min, sec, msec); - result = MakeDate(Day(lorutime), msec_time); - -/* fprintf(stderr, "%f\n", result); */ - - if (local) - result = UTC(result); - -/* fprintf(stderr, "%f\n", result); */ - - *date = TIMECLIP(result); - return js_NewNumberValue(cx, *date, rval); -} - -static JSBool -date_setMilliseconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 1, JS_TRUE, rval); -} - -static JSBool -date_setUTCMilliseconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 1, JS_FALSE, rval); -} - -static JSBool -date_setSeconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 2, JS_TRUE, rval); -} - -static JSBool -date_setUTCSeconds(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 2, JS_FALSE, rval); -} - -static JSBool -date_setMinutes(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 3, JS_TRUE, rval); -} - -static JSBool -date_setUTCMinutes(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 3, JS_FALSE, rval); -} - -static JSBool -date_setHours(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 4, JS_TRUE, rval); -} - -static JSBool -date_setUTCHours(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeTime(cx, obj, argc, argv, 4, JS_FALSE, rval); -} - -static JSBool -date_makeDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, uintN maxargs, JSBool local, jsval *rval) -{ - uintN i; - jsdouble lorutime; /* local or UTC version of *date */ - jsdouble args[3], *argp, *stop; - jsdouble year, month, day; - jsdouble result; - - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - - /* see complaint about ECMA in date_MakeTime */ - if (argc == 0) - argc = 1; /* should be safe, because length of all setters is 1 */ - else if (argc > maxargs) - argc = maxargs; /* clamp argc */ - - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &args[i])) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(args[i])) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); - } - args[i] = js_DoubleToInteger(args[i]); - } - - /* return NaN if date is NaN and we're not setting the year, - * If we are, use 0 as the time. */ - if (!(JSDOUBLE_IS_FINITE(result))) { - if (maxargs < 3) - return js_NewNumberValue(cx, result, rval); - else - lorutime = +0.; - } else { - if (local) - lorutime = LocalTime(result); - else - lorutime = result; - } - - argp = args; - stop = argp + argc; - if (maxargs >= 3 && argp < stop) - year = *argp++; - else - year = YearFromTime(lorutime); - - if (maxargs >= 2 && argp < stop) - month = *argp++; - else - month = MonthFromTime(lorutime); - - if (maxargs >= 1 && argp < stop) - day = *argp++; - else - day = DateFromTime(lorutime); - - day = MakeDay(year, month, day); /* day within year */ - result = MakeDate(day, TimeWithinDay(lorutime)); - - if (local) - result = UTC(result); - - *date = TIMECLIP(result); - return js_NewNumberValue(cx, *date, rval); -} - -static JSBool -date_setDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 1, JS_TRUE, rval); -} - -static JSBool -date_setUTCDate(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 1, JS_FALSE, rval); -} - -static JSBool -date_setMonth(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 2, JS_TRUE, rval); -} - -static JSBool -date_setUTCMonth(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 2, JS_FALSE, rval); -} - -static JSBool -date_setFullYear(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 3, JS_TRUE, rval); -} - -static JSBool -date_setUTCFullYear(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_makeDate(cx, obj, argc, argv, 3, JS_FALSE, rval); -} - -static JSBool -date_setYear(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - jsdouble t; - jsdouble year; - jsdouble day; - jsdouble result; - - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - result = *date; - - if (!js_ValueToNumber(cx, argv[0], &year)) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(year)) { - *date = *cx->runtime->jsNaN; - return js_NewNumberValue(cx, *date, rval); - } - - year = js_DoubleToInteger(year); - - if (!JSDOUBLE_IS_FINITE(result)) { - t = +0.0; - } else { - t = LocalTime(result); - } - - if (year >= 0 && year <= 99) - year += 1900; - - day = MakeDay(year, MonthFromTime(t), DateFromTime(t)); - result = MakeDate(day, TimeWithinDay(t)); - result = UTC(result); - - *date = TIMECLIP(result); - return js_NewNumberValue(cx, *date, rval); -} - -/* constants for toString, toUTCString */ -static char js_NaN_date_str[] = "Invalid Date"; -static const char* days[] = -{ - "Sun","Mon","Tue","Wed","Thu","Fri","Sat" -}; -static const char* months[] = -{ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -static JSBool -date_toGMTString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - char buf[100]; - JSString *str; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - if (!JSDOUBLE_IS_FINITE(*date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); - } else { - jsdouble temp = *date; - - /* Avoid dependence on PRMJ_FormatTimeUSEnglish, because it - * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. - */ - JS_snprintf(buf, sizeof buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", - days[WeekDay(temp)], - DateFromTime(temp), - months[MonthFromTime(temp)], - YearFromTime(temp), - HourFromTime(temp), - MinFromTime(temp), - SecFromTime(temp)); - } - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* for Date.toLocaleString; interface to PRMJTime date struct. - * If findEquivalent is true, then try to map the year to an equivalent year - * that's in range. - */ -static void -new_explode(jsdouble timeval, PRMJTime *split, JSBool findEquivalent) -{ - jsint year = YearFromTime(timeval); - int16 adjustedYear; - - /* If the year doesn't fit in a PRMJTime, find something to do about it. */ - if (year > 32767 || year < -32768) { - if (findEquivalent) { - /* We're really just trying to get a timezone string; map the year - * to some equivalent year in the range 0 to 2800. Borrowed from - * A. D. Olsen. - */ - jsint cycles; -#define CYCLE_YEARS 2800L - cycles = (year >= 0) ? year / CYCLE_YEARS - : -1 - (-1 - year) / CYCLE_YEARS; - adjustedYear = (int16)(year - cycles * CYCLE_YEARS); - } else { - /* Clamp it to the nearest representable year. */ - adjustedYear = (int16)((year > 0) ? 32767 : - 32768); - } - } else { - adjustedYear = (int16)year; - } - - split->tm_usec = (int32) msFromTime(timeval) * 1000; - split->tm_sec = (int8) SecFromTime(timeval); - split->tm_min = (int8) MinFromTime(timeval); - split->tm_hour = (int8) HourFromTime(timeval); - split->tm_mday = (int8) DateFromTime(timeval); - split->tm_mon = (int8) MonthFromTime(timeval); - split->tm_wday = (int8) WeekDay(timeval); - split->tm_year = (int16) adjustedYear; - split->tm_yday = (int16) DayWithinYear(timeval, year); - - /* not sure how this affects things, but it doesn't seem - to matter. */ - split->tm_isdst = (DaylightSavingTA(timeval) != 0); -} - -typedef enum formatspec { - FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME -} formatspec; - -/* helper function */ -static JSBool -date_format(JSContext *cx, jsdouble date, formatspec format, jsval *rval) -{ - char buf[100]; - JSString *str; - char tzbuf[100]; - JSBool usetz; - size_t i, tzlen; - PRMJTime split; - - if (!JSDOUBLE_IS_FINITE(date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); - } else { - jsdouble local = LocalTime(date); - - /* offset from GMT in minutes. The offset includes daylight savings, - if it applies. */ - jsint minutes = (jsint) floor(AdjustTime(date) / msPerMinute); - - /* map 510 minutes to 0830 hours */ - intN offset = (minutes / 60) * 100 + minutes % 60; - - /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is - * printed as 'GMT-0800' rather than as 'PST' to avoid - * operating-system dependence on strftime (which - * PRMJ_FormatTimeUSEnglish calls, for %Z only.) win32 prints - * PST as 'Pacific Standard Time.' This way we always know - * what we're getting, and can parse it if we produce it. - * The OS TZA string is included as a comment. - */ - - /* get a timezone string from the OS to include as a - comment. */ - new_explode(date, &split, JS_TRUE); - if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) { - - /* Decide whether to use the resulting timezone string. - * - * Reject it if it contains any non-ASCII, non-alphanumeric - * characters. It's then likely in some other character - * encoding, and we probably won't display it correctly. - */ - usetz = JS_TRUE; - tzlen = strlen(tzbuf); - if (tzlen > 100) { - usetz = JS_FALSE; - } else { - for (i = 0; i < tzlen; i++) { - jschar c = tzbuf[i]; - if (c > 127 || - !(isalpha(c) || isdigit(c) || - c == ' ' || c == '(' || c == ')')) { - usetz = JS_FALSE; - } - } - } - - /* Also reject it if it's not parenthesized or if it's '()'. */ - if (tzbuf[0] != '(' || tzbuf[1] == ')') - usetz = JS_FALSE; - } else - usetz = JS_FALSE; - - switch (format) { - case FORMATSPEC_FULL: - /* - * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it - * requires a PRMJTime... which only has 16-bit years. Sub-ECMA. - */ - /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */ - JS_snprintf(buf, sizeof buf, - "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s", - days[WeekDay(local)], - months[MonthFromTime(local)], - DateFromTime(local), - YearFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - offset, - usetz ? " " : "", - usetz ? tzbuf : ""); - break; - case FORMATSPEC_DATE: - /* Tue Oct 31 2000 */ - JS_snprintf(buf, sizeof buf, - "%s %s %.2d %.4d", - days[WeekDay(local)], - months[MonthFromTime(local)], - DateFromTime(local), - YearFromTime(local)); - break; - case FORMATSPEC_TIME: - /* 09:41:40 GMT-0800 (PST) */ - JS_snprintf(buf, sizeof buf, - "%.2d:%.2d:%.2d GMT%+.4d%s%s", - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - offset, - usetz ? " " : "", - usetz ? tzbuf : ""); - break; - } - } - - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -date_toLocaleHelper(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval, char *format) -{ - char buf[100]; - JSString *str; - PRMJTime split; - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - if (!JSDOUBLE_IS_FINITE(*date)) { - JS_snprintf(buf, sizeof buf, js_NaN_date_str); - } else { - intN result_len; - jsdouble local = LocalTime(*date); - new_explode(local, &split, JS_FALSE); - - /* let PRMJTime format it. */ - result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split); - - /* If it failed, default to toString. */ - if (result_len == 0) - return date_format(cx, *date, FORMATSPEC_FULL, rval); - - /* Hacked check against undesired 2-digit year 00/00/00 form. */ - if (strcmp(format, "%x") == 0 && result_len >= 6 && - /* Format %x means use OS settings, which may have 2-digit yr, so - hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/ - !isdigit(buf[result_len - 3]) && - isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) && - /* ...but not if starts with 4-digit year, like 2022/3/11. */ - !(isdigit(buf[0]) && isdigit(buf[1]) && - isdigit(buf[2]) && isdigit(buf[3]))) { - JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), - "%d", js_DateGetYear(cx, obj)); - } - - } - - if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -date_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - /* Use '%#c' for windows, because '%c' is - * backward-compatible and non-y2k with msvc; '%#c' requests that a - * full year be used in the result string. - */ - return date_toLocaleHelper(cx, obj, argc, argv, rval, -#if defined(_WIN32) && !defined(__MWERKS__) - "%#c" -#else - "%c" -#endif - ); -} - -static JSBool -date_toLocaleDateString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - /* Use '%#x' for windows, because '%x' is - * backward-compatible and non-y2k with msvc; '%#x' requests that a - * full year be used in the result string. - */ - return date_toLocaleHelper(cx, obj, argc, argv, rval, -#if defined(_WIN32) && !defined(__MWERKS__) - "%#x" -#else - "%x" -#endif - ); -} - -static JSBool -date_toLocaleTimeString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - return date_toLocaleHelper(cx, obj, argc, argv, rval, "%X"); -} - -static JSBool -date_toLocaleFormat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *fmt; - - if (argc == 0) - return date_toLocaleString(cx, obj, argc, argv, rval); - - fmt = JS_ValueToString(cx, argv[0]); - if (!fmt) - return JS_FALSE; - - return date_toLocaleHelper(cx, obj, argc, argv, rval, - JS_GetStringBytes(fmt)); -} - -static JSBool -date_toTimeString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - return date_format(cx, *date, FORMATSPEC_TIME, rval); -} - -static JSBool -date_toDateString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - return date_format(cx, *date, FORMATSPEC_DATE, rval); -} - -#if JS_HAS_TOSOURCE -#include -#include "jsdtoa.h" - -static JSBool -date_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble *date; - char buf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr, *bytes; - JSString *str; - - date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - - numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, *date); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - bytes = JS_smprintf("(new %s(%s))", js_Date_str, numStr); - if (!bytes) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - str = JS_NewString(cx, bytes, strlen(bytes)); - if (!str) { - free(bytes); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -static JSBool -date_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsdouble *date = date_getProlog(cx, obj, argv); - if (!date) - return JS_FALSE; - return date_format(cx, *date, FORMATSPEC_FULL, rval); -} - -static JSBool -date_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* It is an error to call date_valueOf on a non-date object, but we don't - * need to check for that explicitly here because every path calls - * date_getProlog, which does the check. - */ - - /* If called directly with no arguments, convert to a time number. */ - if (argc == 0) - return date_getTime(cx, obj, argc, argv, rval); - - /* Convert to number only if the hint was given, otherwise favor string. */ - if (argc == 1) { - JSString *str, *str2; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - str2 = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]); - if (js_EqualStrings(str, str2)) - return date_getTime(cx, obj, argc, argv, rval); - } - return date_toString(cx, obj, argc, argv, rval); -} - - -/* - * creation and destruction - */ - -static JSFunctionSpec date_static_methods[] = { - {"UTC", date_UTC, MAXARGS,0,0 }, - {"parse", date_parse, 1,0,0 }, - {"now", date_now, 0,0,0 }, - {0,0,0,0,0} -}; - -static JSFunctionSpec date_methods[] = { - {"getTime", date_getTime, 0,0,0 }, - {"getTimezoneOffset", date_getTimezoneOffset, 0,0,0 }, - {"getYear", date_getYear, 0,0,0 }, - {"getFullYear", date_getFullYear, 0,0,0 }, - {"getUTCFullYear", date_getUTCFullYear, 0,0,0 }, - {"getMonth", date_getMonth, 0,0,0 }, - {"getUTCMonth", date_getUTCMonth, 0,0,0 }, - {"getDate", date_getDate, 0,0,0 }, - {"getUTCDate", date_getUTCDate, 0,0,0 }, - {"getDay", date_getDay, 0,0,0 }, - {"getUTCDay", date_getUTCDay, 0,0,0 }, - {"getHours", date_getHours, 0,0,0 }, - {"getUTCHours", date_getUTCHours, 0,0,0 }, - {"getMinutes", date_getMinutes, 0,0,0 }, - {"getUTCMinutes", date_getUTCMinutes, 0,0,0 }, - {"getSeconds", date_getUTCSeconds, 0,0,0 }, - {"getUTCSeconds", date_getUTCSeconds, 0,0,0 }, - {"getMilliseconds", date_getUTCMilliseconds,0,0,0 }, - {"getUTCMilliseconds", date_getUTCMilliseconds,0,0,0 }, - {"setTime", date_setTime, 1,0,0 }, - {"setYear", date_setYear, 1,0,0 }, - {"setFullYear", date_setFullYear, 3,0,0 }, - {"setUTCFullYear", date_setUTCFullYear, 3,0,0 }, - {"setMonth", date_setMonth, 2,0,0 }, - {"setUTCMonth", date_setUTCMonth, 2,0,0 }, - {"setDate", date_setDate, 1,0,0 }, - {"setUTCDate", date_setUTCDate, 1,0,0 }, - {"setHours", date_setHours, 4,0,0 }, - {"setUTCHours", date_setUTCHours, 4,0,0 }, - {"setMinutes", date_setMinutes, 3,0,0 }, - {"setUTCMinutes", date_setUTCMinutes, 3,0,0 }, - {"setSeconds", date_setSeconds, 2,0,0 }, - {"setUTCSeconds", date_setUTCSeconds, 2,0,0 }, - {"setMilliseconds", date_setMilliseconds, 1,0,0 }, - {"setUTCMilliseconds", date_setUTCMilliseconds,1,0,0 }, - {"toUTCString", date_toGMTString, 0,0,0 }, - {js_toLocaleString_str, date_toLocaleString, 0,0,0 }, - {"toLocaleDateString", date_toLocaleDateString,0,0,0 }, - {"toLocaleTimeString", date_toLocaleTimeString,0,0,0 }, - {"toLocaleFormat", date_toLocaleFormat, 1,0,0 }, - {"toDateString", date_toDateString, 0,0,0 }, - {"toTimeString", date_toTimeString, 0,0,0 }, -#if JS_HAS_TOSOURCE - {js_toSource_str, date_toSource, 0,0,0 }, -#endif - {js_toString_str, date_toString, 0,0,0 }, - {js_valueOf_str, date_valueOf, 0,0,0 }, - {0,0,0,0,0} -}; - -static jsdouble * -date_constructor(JSContext *cx, JSObject* obj) -{ - jsdouble *date; - - date = js_NewDouble(cx, 0.0, 0); - if (!date) - return NULL; - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, DOUBLE_TO_JSVAL(date)); - return date; -} - -static JSBool -Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble *date; - JSString *str; - jsdouble d; - - /* Date called as function. */ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - int64 us, ms, us2ms; - jsdouble msec_time; - - /* NSPR 2.0 docs say 'We do not support PRMJ_NowMS and PRMJ_NowS', - * so compute ms from PRMJ_Now. - */ - us = PRMJ_Now(); - JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); - JSLL_DIV(ms, us, us2ms); - JSLL_L2D(msec_time, ms); - - return date_format(cx, msec_time, FORMATSPEC_FULL, rval); - } - - /* Date called as constructor. */ - if (argc == 0) { - int64 us, ms, us2ms; - jsdouble msec_time; - - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - - us = PRMJ_Now(); - JSLL_UI2L(us2ms, PRMJ_USEC_PER_MSEC); - JSLL_DIV(ms, us, us2ms); - JSLL_L2D(msec_time, ms); - - *date = msec_time; - } else if (argc == 1) { - if (!JSVAL_IS_STRING(argv[0])) { - /* the argument is a millisecond number */ - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - *date = TIMECLIP(d); - } else { - /* the argument is a string; parse it. */ - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - - if (!date_parseString(str, date)) - *date = *cx->runtime->jsNaN; - *date = TIMECLIP(*date); - } - } else { - jsdouble array[MAXARGS]; - uintN loop; - jsdouble double_arg; - jsdouble day; - jsdouble msec_time; - - for (loop = 0; loop < MAXARGS; loop++) { - if (loop < argc) { - if (!js_ValueToNumber(cx, argv[loop], &double_arg)) - return JS_FALSE; - /* if any arg is NaN, make a NaN date object - and return */ - if (!JSDOUBLE_IS_FINITE(double_arg)) { - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - *date = *cx->runtime->jsNaN; - return JS_TRUE; - } - array[loop] = js_DoubleToInteger(double_arg); - } else { - if (loop == 2) { - array[loop] = 1; /* Default the date argument to 1. */ - } else { - array[loop] = 0; - } - } - } - - date = date_constructor(cx, obj); - if (!date) - return JS_FALSE; - - /* adjust 2-digit years into the 20th century */ - if (array[0] >= 0 && array[0] <= 99) - array[0] += 1900; - - day = MakeDay(array[0], array[1], array[2]); - msec_time = MakeTime(array[3], array[4], array[5], array[6]); - msec_time = MakeDate(day, msec_time); - msec_time = UTC(msec_time); - *date = TIMECLIP(msec_time); - } - return JS_TRUE; -} - -JSObject * -js_InitDateClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - jsdouble *proto_date; - - /* set static LocalTZA */ - LocalTZA = -(PRMJ_LocalGMTDifference() * msPerSecond); - proto = JS_InitClass(cx, obj, NULL, &js_DateClass, Date, MAXARGS, - NULL, date_methods, NULL, date_static_methods); - if (!proto) - return NULL; - - /* Alias toUTCString with toGMTString. (ECMA B.2.6) */ - if (!JS_AliasProperty(cx, proto, "toUTCString", "toGMTString")) - return NULL; - - /* Set the value of the Date.prototype date to NaN */ - proto_date = date_constructor(cx, proto); - if (!proto_date) - return NULL; - *proto_date = *cx->runtime->jsNaN; - - return proto; -} - -JS_FRIEND_API(JSObject *) -js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time) -{ - JSObject *obj; - jsdouble *date; - - obj = js_NewObject(cx, &js_DateClass, NULL, NULL); - if (!obj) - return NULL; - - date = date_constructor(cx, obj); - if (!date) - return NULL; - - *date = msec_time; - return obj; -} - -JS_FRIEND_API(JSObject *) -js_NewDateObject(JSContext* cx, int year, int mon, int mday, - int hour, int min, int sec) -{ - JSObject *obj; - jsdouble msec_time; - - msec_time = date_msecFromDate(year, mon, mday, hour, min, sec, 0); - obj = js_NewDateObjectMsec(cx, UTC(msec_time)); - return obj; -} - -JS_FRIEND_API(JSBool) -js_DateIsValid(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return JS_FALSE; - else - return JS_TRUE; -} - -JS_FRIEND_API(int) -js_DateGetYear(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - /* Preserve legacy API behavior of returning 0 for invalid dates. */ - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) YearFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetMonth(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) MonthFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetDate(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) DateFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetHours(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) HourFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetMinutes(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) MinFromTime(LocalTime(*date)); -} - -JS_FRIEND_API(int) -js_DateGetSeconds(JSContext *cx, JSObject* obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (int) SecFromTime(*date); -} - -JS_FRIEND_API(void) -js_DateSetYear(JSContext *cx, JSObject *obj, int year) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - /* reset date if it was NaN */ - if (JSDOUBLE_IS_NaN(local)) - local = 0; - local = date_msecFromDate(year, - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetMonth(JSContext *cx, JSObject *obj, int month) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - /* bail if date was NaN */ - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - month, - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetDate(JSContext *cx, JSObject *obj, int date) -{ - jsdouble local; - jsdouble *datep = date_getProlog(cx, obj, NULL); - if (!datep) - return; - local = LocalTime(*datep); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - date, - HourFromTime(local), - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *datep = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetHours(JSContext *cx, JSObject *obj, int hours) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - hours, - MinFromTime(local), - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - minutes, - SecFromTime(local), - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(void) -js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds) -{ - jsdouble local; - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date) - return; - local = LocalTime(*date); - if (JSDOUBLE_IS_NaN(local)) - return; - local = date_msecFromDate(YearFromTime(local), - MonthFromTime(local), - DateFromTime(local), - HourFromTime(local), - MinFromTime(local), - seconds, - msFromTime(local)); - *date = UTC(local); -} - -JS_FRIEND_API(jsdouble) -js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj) -{ - jsdouble *date = date_getProlog(cx, obj, NULL); - if (!date || JSDOUBLE_IS_NaN(*date)) - return 0; - return (*date); -} diff --git a/spidermonkey/libjs/jsdate.h b/spidermonkey/libjs/jsdate.h deleted file mode 100644 index 88bd5f5..0000000 --- a/spidermonkey/libjs/jsdate.h +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS Date class interface. - */ - -#ifndef jsdate_h___ -#define jsdate_h___ - -JS_BEGIN_EXTERN_C - -extern JSClass js_DateClass; - -extern JSObject * -js_InitDateClass(JSContext *cx, JSObject *obj); - -/* - * These functions provide a C interface to the date/time object - */ - -/* - * Construct a new Date Object from a time value given in milliseconds UTC - * since the epoch. - */ -extern JS_FRIEND_API(JSObject*) -js_NewDateObjectMsec(JSContext* cx, jsdouble msec_time); - -/* - * Construct a new Date Object from an exploded local time value. - */ -extern JS_FRIEND_API(JSObject*) -js_NewDateObject(JSContext* cx, int year, int mon, int mday, - int hour, int min, int sec); - -/* - * Detect whether the internal date value is NaN. (Because failure is - * out-of-band for js_DateGet*) - */ -extern JS_FRIEND_API(JSBool) -js_DateIsValid(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetYear(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetMonth(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetDate(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetHours(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetMinutes(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(int) -js_DateGetSeconds(JSContext *cx, JSObject* obj); - -extern JS_FRIEND_API(void) -js_DateSetYear(JSContext *cx, JSObject *obj, int year); - -extern JS_FRIEND_API(void) -js_DateSetMonth(JSContext *cx, JSObject *obj, int year); - -extern JS_FRIEND_API(void) -js_DateSetDate(JSContext *cx, JSObject *obj, int date); - -extern JS_FRIEND_API(void) -js_DateSetHours(JSContext *cx, JSObject *obj, int hours); - -extern JS_FRIEND_API(void) -js_DateSetMinutes(JSContext *cx, JSObject *obj, int minutes); - -extern JS_FRIEND_API(void) -js_DateSetSeconds(JSContext *cx, JSObject *obj, int seconds); - -extern JS_FRIEND_API(jsdouble) -js_DateGetMsecSinceEpoch(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jsdate_h___ */ diff --git a/spidermonkey/libjs/jsdbgapi.c b/spidermonkey/libjs/jsdbgapi.c deleted file mode 100644 index 8fa0e68..0000000 --- a/spidermonkey/libjs/jsdbgapi.c +++ /dev/null @@ -1,1439 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS debugging API. - */ -#include "jsstddef.h" -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsclist.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -typedef struct JSTrap { - JSCList links; - JSScript *script; - jsbytecode *pc; - JSOp op; - JSTrapHandler handler; - void *closure; -} JSTrap; - -static JSTrap * -FindTrap(JSRuntime *rt, JSScript *script, jsbytecode *pc) -{ - JSTrap *trap; - - for (trap = (JSTrap *)rt->trapList.next; - trap != (JSTrap *)&rt->trapList; - trap = (JSTrap *)trap->links.next) { - if (trap->script == script && trap->pc == pc) - return trap; - } - return NULL; -} - -void -js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op) -{ - JSTrap *trap; - - trap = FindTrap(cx->runtime, script, pc); - if (trap) - trap->op = op; - else - *pc = (jsbytecode)op; -} - -JS_PUBLIC_API(JSBool) -JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler handler, void *closure) -{ - JSRuntime *rt; - JSTrap *trap; - - rt = cx->runtime; - trap = FindTrap(rt, script, pc); - if (trap) { - JS_ASSERT(trap->script == script && trap->pc == pc); - JS_ASSERT(*pc == JSOP_TRAP); - } else { - trap = (JSTrap *) JS_malloc(cx, sizeof *trap); - if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { - if (trap) - JS_free(cx, trap); - return JS_FALSE; - } - JS_APPEND_LINK(&trap->links, &rt->trapList); - trap->script = script; - trap->pc = pc; - trap->op = (JSOp)*pc; - *pc = JSOP_TRAP; - } - trap->handler = handler; - trap->closure = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSOp) -JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - JSTrap *trap; - - trap = FindTrap(cx->runtime, script, pc); - if (!trap) { - JS_ASSERT(0); /* XXX can't happen */ - return JSOP_LIMIT; - } - return trap->op; -} - -static void -DestroyTrap(JSContext *cx, JSTrap *trap) -{ - JS_REMOVE_LINK(&trap->links); - *trap->pc = (jsbytecode)trap->op; - js_RemoveRoot(cx->runtime, &trap->closure); - JS_free(cx, trap); -} - -JS_PUBLIC_API(void) -JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler *handlerp, void **closurep) -{ - JSTrap *trap; - - trap = FindTrap(cx->runtime, script, pc); - if (handlerp) - *handlerp = trap ? trap->handler : NULL; - if (closurep) - *closurep = trap ? trap->closure : NULL; - if (trap) - DestroyTrap(cx, trap); -} - -JS_PUBLIC_API(void) -JS_ClearScriptTraps(JSContext *cx, JSScript *script) -{ - JSRuntime *rt; - JSTrap *trap, *next; - - rt = cx->runtime; - for (trap = (JSTrap *)rt->trapList.next; - trap != (JSTrap *)&rt->trapList; - trap = next) { - next = (JSTrap *)trap->links.next; - if (trap->script == script) - DestroyTrap(cx, trap); - } -} - -JS_PUBLIC_API(void) -JS_ClearAllTraps(JSContext *cx) -{ - JSRuntime *rt; - JSTrap *trap, *next; - - rt = cx->runtime; - for (trap = (JSTrap *)rt->trapList.next; - trap != (JSTrap *)&rt->trapList; - trap = next) { - next = (JSTrap *)trap->links.next; - DestroyTrap(cx, trap); - } -} - -JS_PUBLIC_API(JSTrapStatus) -JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval) -{ - JSTrap *trap; - JSTrapStatus status; - jsint op; - - trap = FindTrap(cx->runtime, script, pc); - if (!trap) { - JS_ASSERT(0); /* XXX can't happen */ - return JSTRAP_ERROR; - } - /* - * It's important that we not use 'trap->' after calling the callback -- - * the callback might remove the trap! - */ - op = (jsint)trap->op; - status = trap->handler(cx, script, pc, rval, trap->closure); - if (status == JSTRAP_CONTINUE) { - /* By convention, return the true op to the interpreter in rval. */ - *rval = INT_TO_JSVAL(op); - } - return status; -} - -JS_PUBLIC_API(JSBool) -JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure) -{ - rt->interruptHandler = handler; - rt->interruptHandlerData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep) -{ - if (handlerp) - *handlerp = (JSTrapHandler)rt->interruptHandler; - if (closurep) - *closurep = rt->interruptHandlerData; - rt->interruptHandler = 0; - rt->interruptHandlerData = 0; - return JS_TRUE; -} - -/************************************************************************/ - -typedef struct JSWatchPoint { - JSCList links; - JSObject *object; /* weak link, see js_FinalizeObject */ - JSScopeProperty *sprop; - JSPropertyOp setter; - JSWatchPointHandler handler; - void *closure; - uintN flags; -} JSWatchPoint; - -#define JSWP_LIVE 0x1 /* live because set and not cleared */ -#define JSWP_HELD 0x2 /* held while running handler/setter */ - -static JSBool -DropWatchPoint(JSContext *cx, JSWatchPoint *wp, uintN flag) -{ - JSBool ok; - JSScopeProperty *sprop; - JSObject *pobj; - JSProperty *prop; - JSPropertyOp setter; - - ok = JS_TRUE; - wp->flags &= ~flag; - if (wp->flags != 0) - return JS_TRUE; - - /* - * Remove wp from the list, then if there are no other watchpoints for - * wp->sprop in any scope, restore wp->sprop->setter from wp. - */ - JS_REMOVE_LINK(&wp->links); - sprop = wp->sprop; - - /* - * If js_ChangeNativePropertyAttrs fails, propagate failure after removing - * wp->closure's root and freeing wp. - */ - setter = js_GetWatchedSetter(cx->runtime, NULL, sprop); - if (!setter) { - ok = js_LookupProperty(cx, wp->object, sprop->id, &pobj, &prop); - - /* - * If the property wasn't found on wp->object or didn't exist, then - * someone else has dealt with this sprop, and we don't need to change - * the property attributes. - */ - if (ok && prop) { - if (pobj == wp->object) { - JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj); - - sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(pobj), sprop, - 0, sprop->attrs, - sprop->getter, - wp->setter); - if (!sprop) - ok = JS_FALSE; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - } - - js_RemoveRoot(cx->runtime, &wp->closure); - JS_free(cx, wp); - return ok; -} - -void -js_MarkWatchPoints(JSContext *cx) -{ - JSRuntime *rt; - JSWatchPoint *wp; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - MARK_SCOPE_PROPERTY(cx, wp->sprop); - if (wp->sprop->attrs & JSPROP_SETTER) - JS_MarkGCThing(cx, wp->setter, "wp->setter", NULL); - } -} - -static JSWatchPoint * -FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) -{ - JSWatchPoint *wp; - - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if (wp->object == scope->object && wp->sprop->id == id) - return wp; - } - return NULL; -} - -JSScopeProperty * -js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id) -{ - JSWatchPoint *wp; - - wp = FindWatchPoint(rt, scope, id); - if (!wp) - return NULL; - return wp->sprop; -} - -JSPropertyOp -js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, - const JSScopeProperty *sprop) -{ - JSWatchPoint *wp; - - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if ((!scope || wp->object == scope->object) && wp->sprop == sprop) - return wp->setter; - } - return NULL; -} - -JSBool JS_DLL_CALLBACK -js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSRuntime *rt; - JSWatchPoint *wp; - JSScopeProperty *sprop; - jsval propid, userid; - JSScope *scope; - JSBool ok; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - sprop = wp->sprop; - if (wp->object == obj && SPROP_USERID(sprop) == id && - !(wp->flags & JSWP_HELD)) { - wp->flags |= JSWP_HELD; - - JS_LOCK_OBJ(cx, obj); - propid = ID_TO_VALUE(sprop->id); - userid = (sprop->flags & SPROP_HAS_SHORTID) - ? INT_TO_JSVAL(sprop->shortid) - : propid; - scope = OBJ_SCOPE(obj); - JS_UNLOCK_OBJ(cx, obj); - - /* NB: wp is held, so we can safely dereference it still. */ - ok = wp->handler(cx, obj, propid, - SPROP_HAS_VALID_SLOT(sprop, scope) - ? OBJ_GET_SLOT(cx, obj, sprop->slot) - : JSVAL_VOID, - vp, wp->closure); - if (ok) { - /* - * Create a pseudo-frame for the setter invocation so that any - * stack-walking security code under the setter will correctly - * identify the guilty party. So that the watcher appears to - * be active to obj_eval and other such code, point frame.pc - * at the JSOP_STOP at the end of the script. - */ - JSObject *closure; - JSClass *clasp; - JSFunction *fun; - JSScript *script; - uintN nslots; - jsval smallv[5]; - jsval *argv; - JSStackFrame frame; - - closure = (JSObject *) wp->closure; - clasp = OBJ_GET_CLASS(cx, closure); - if (clasp == &js_FunctionClass) { - fun = (JSFunction *) JS_GetPrivate(cx, closure); - script = FUN_SCRIPT(fun); - } else if (clasp == &js_ScriptClass) { - fun = NULL; - script = (JSScript *) JS_GetPrivate(cx, closure); - } else { - fun = NULL; - script = NULL; - } - - nslots = 2; - if (fun) { - nslots += fun->nargs; - if (FUN_NATIVE(fun)) - nslots += fun->u.n.extra; - } - - if (nslots <= JS_ARRAY_LENGTH(smallv)) { - argv = smallv; - } else { - argv = JS_malloc(cx, nslots * sizeof(jsval)); - if (!argv) { - DropWatchPoint(cx, wp, JSWP_HELD); - return JS_FALSE; - } - } - - argv[0] = OBJECT_TO_JSVAL(closure); - argv[1] = JSVAL_NULL; - memset(argv + 2, 0, (nslots - 2) * sizeof(jsval)); - - memset(&frame, 0, sizeof(frame)); - frame.script = script; - if (script) { - JS_ASSERT(script->length >= JSOP_STOP_LENGTH); - frame.pc = script->code + script->length - - JSOP_STOP_LENGTH; - } - frame.fun = fun; - frame.argv = argv + 2; - frame.down = cx->fp; - frame.scopeChain = OBJ_GET_PARENT(cx, closure); - - cx->fp = &frame; - ok = !wp->setter || - ((sprop->attrs & JSPROP_SETTER) - ? js_InternalCall(cx, obj, OBJECT_TO_JSVAL(wp->setter), - 1, vp, vp) - : wp->setter(cx, OBJ_THIS_OBJECT(cx, obj), userid, vp)); - cx->fp = frame.down; - if (argv != smallv) - JS_free(cx, argv); - } - return DropWatchPoint(cx, wp, JSWP_HELD) && ok; - } - } - return JS_TRUE; -} - -JSBool JS_DLL_CALLBACK -js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *funobj; - JSFunction *wrapper; - jsval userid; - - funobj = JSVAL_TO_OBJECT(argv[-2]); - JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - wrapper = (JSFunction *) JS_GetPrivate(cx, funobj); - userid = ATOM_KEY(wrapper->atom); - *rval = argv[0]; - return js_watch_set(cx, obj, userid, rval); -} - -JSPropertyOp -js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter) -{ - JSAtom *atom; - JSFunction *wrapper; - - if (!(attrs & JSPROP_SETTER)) - return &js_watch_set; /* & to silence schoolmarmish MSVC */ - - if (JSID_IS_ATOM(id)) { - atom = JSID_TO_ATOM(id); - } else if (JSID_IS_INT(id)) { - atom = js_AtomizeInt(cx, JSID_TO_INT(id), 0); - if (!atom) - return NULL; - } else { - atom = NULL; - } - wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0, - OBJ_GET_PARENT(cx, (JSObject *)setter), - atom); - if (!wrapper) - return NULL; - return (JSPropertyOp) wrapper->object; -} - -JS_PUBLIC_API(JSBool) -JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler handler, void *closure) -{ - JSAtom *atom; - jsid propid; - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - JSRuntime *rt; - JSBool ok; - JSWatchPoint *wp; - JSPropertyOp watcher; - - if (!OBJ_IS_NATIVE(obj)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, - OBJ_GET_CLASS(cx, obj)->name); - return JS_FALSE; - } - - if (JSVAL_IS_INT(id)) { - propid = INT_JSVAL_TO_JSID(id); - atom = NULL; - } else { - atom = js_ValueToStringAtom(cx, id); - if (!atom) - return JS_FALSE; - propid = ATOM_TO_JSID(atom); - } - - if (!js_LookupProperty(cx, obj, propid, &pobj, &prop)) - return JS_FALSE; - sprop = (JSScopeProperty *) prop; - rt = cx->runtime; - if (!sprop) { - /* Check for a deleted symbol watchpoint, which holds its property. */ - sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); - if (!sprop) { - /* Make a new property in obj so we can watch for the first set. */ - if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID, - NULL, NULL, JSPROP_ENUMERATE, - &prop)) { - return JS_FALSE; - } - sprop = (JSScopeProperty *) prop; - } - } else if (pobj != obj) { - /* Clone the prototype property so we can watch the right object. */ - jsval value; - JSPropertyOp getter, setter; - uintN attrs, flags; - intN shortid; - - if (OBJ_IS_NATIVE(pobj)) { - value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) - ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) - : JSVAL_VOID; - getter = sprop->getter; - setter = sprop->setter; - attrs = sprop->attrs; - flags = sprop->flags; - shortid = sprop->shortid; - } else { - if (!OBJ_GET_PROPERTY(cx, pobj, id, &value) || - !OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs)) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - return JS_FALSE; - } - getter = setter = NULL; - flags = 0; - shortid = 0; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* Recall that obj is native, whether or not pobj is native. */ - if (!js_DefineNativeProperty(cx, obj, propid, value, getter, setter, - attrs, flags, shortid, &prop)) { - return JS_FALSE; - } - sprop = (JSScopeProperty *) prop; - } - - /* - * At this point, prop/sprop exists in obj, obj is locked, and we must - * OBJ_DROP_PROPERTY(cx, obj, prop) before returning. - */ - ok = JS_TRUE; - wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); - if (!wp) { - watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); - if (!watcher) { - ok = JS_FALSE; - goto out; - } - - wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); - if (!wp) { - ok = JS_FALSE; - goto out; - } - wp->handler = NULL; - wp->closure = NULL; - ok = js_AddRoot(cx, &wp->closure, "wp->closure"); - if (!ok) { - JS_free(cx, wp); - goto out; - } - wp->object = obj; - JS_ASSERT(sprop->setter != js_watch_set || pobj != obj); - wp->setter = sprop->setter; - wp->flags = JSWP_LIVE; - - /* XXXbe nest in obj lock here */ - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, - sprop->getter, watcher); - if (!sprop) { - /* Self-link so DropWatchPoint can JS_REMOVE_LINK it. */ - JS_INIT_CLIST(&wp->links); - DropWatchPoint(cx, wp, JSWP_LIVE); - ok = JS_FALSE; - goto out; - } - wp->sprop = sprop; - - /* - * Now that wp is fully initialized, append it to rt's wp list. - * Because obj is locked we know that no other thread could have added - * a watchpoint for (obj, propid). - */ - JS_ASSERT(!FindWatchPoint(rt, OBJ_SCOPE(obj), propid)); - JS_APPEND_LINK(&wp->links, &rt->watchPointList); - } - wp->handler = handler; - wp->closure = closure; - -out: - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler *handlerp, void **closurep) -{ - JSRuntime *rt; - JSWatchPoint *wp; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = (JSWatchPoint *)wp->links.next) { - if (wp->object == obj && SPROP_USERID(wp->sprop) == id) { - if (handlerp) - *handlerp = wp->handler; - if (closurep) - *closurep = wp->closure; - return DropWatchPoint(cx, wp, JSWP_LIVE); - } - } - if (handlerp) - *handlerp = NULL; - if (closurep) - *closurep = NULL; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSWatchPoint *wp, *next; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = next) { - next = (JSWatchPoint *)wp->links.next; - if (wp->object == obj && !DropWatchPoint(cx, wp, JSWP_LIVE)) - return JS_FALSE; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_ClearAllWatchPoints(JSContext *cx) -{ - JSRuntime *rt; - JSWatchPoint *wp, *next; - - rt = cx->runtime; - for (wp = (JSWatchPoint *)rt->watchPointList.next; - wp != (JSWatchPoint *)&rt->watchPointList; - wp = next) { - next = (JSWatchPoint *)wp->links.next; - if (!DropWatchPoint(cx, wp, JSWP_LIVE)) - return JS_FALSE; - } - return JS_TRUE; -} - -/************************************************************************/ - -JS_PUBLIC_API(uintN) -JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - return js_PCToLineNumber(cx, script, pc); -} - -JS_PUBLIC_API(jsbytecode *) -JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno) -{ - return js_LineNumberToPC(script, lineno); -} - -JS_PUBLIC_API(JSScript *) -JS_GetFunctionScript(JSContext *cx, JSFunction *fun) -{ - return FUN_SCRIPT(fun); -} - -JS_PUBLIC_API(JSNative) -JS_GetFunctionNative(JSContext *cx, JSFunction *fun) -{ - return FUN_NATIVE(fun); -} - -JS_PUBLIC_API(JSPrincipals *) -JS_GetScriptPrincipals(JSContext *cx, JSScript *script) -{ - return script->principals; -} - -/************************************************************************/ - -/* - * Stack Frame Iterator - */ -JS_PUBLIC_API(JSStackFrame *) -JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp) -{ - *iteratorp = (*iteratorp == NULL) ? cx->fp : (*iteratorp)->down; - return *iteratorp; -} - -JS_PUBLIC_API(JSScript *) -JS_GetFrameScript(JSContext *cx, JSStackFrame *fp) -{ - return fp->script; -} - -JS_PUBLIC_API(jsbytecode *) -JS_GetFramePC(JSContext *cx, JSStackFrame *fp) -{ - return fp->pc; -} - -JS_PUBLIC_API(JSStackFrame *) -JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp) -{ - if (!fp) - fp = cx->fp; - while ((fp = fp->down) != NULL) { - if (fp->script) - return fp; - } - return NULL; -} - -JS_PUBLIC_API(JSPrincipals *) -JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp) -{ - if (fp->fun) { - JSRuntime *rt = cx->runtime; - - if (rt->findObjectPrincipals) { - JSObject *callee = JSVAL_TO_OBJECT(fp->argv[-2]); - - if (fp->fun->object != callee) - return rt->findObjectPrincipals(cx, callee); - /* FALL THROUGH */ - } - } - if (fp->script) - return fp->script->principals; - return NULL; -} - -JS_PUBLIC_API(JSPrincipals *) -JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller) -{ - JSRuntime *rt; - JSObject *callee; - JSPrincipals *principals, *callerPrincipals; - - rt = cx->runtime; - if (rt->findObjectPrincipals) { - callee = JSVAL_TO_OBJECT(fp->argv[-2]); - principals = rt->findObjectPrincipals(cx, callee); - } else { - principals = NULL; - } - if (!caller) - return principals; - callerPrincipals = JS_StackFramePrincipals(cx, caller); - return (callerPrincipals && principals && - callerPrincipals->subsume(callerPrincipals, principals)) - ? principals - : callerPrincipals; -} - -JS_PUBLIC_API(void *) -JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp) -{ - if (fp->annotation && fp->script) { - JSPrincipals *principals = JS_StackFramePrincipals(cx, fp); - - if (principals && principals->globalPrivilegesEnabled(cx, principals)) { - /* - * Give out an annotation only if privileges have not been revoked - * or disabled globally. - */ - return fp->annotation; - } - } - - return NULL; -} - -JS_PUBLIC_API(void) -JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation) -{ - fp->annotation = annotation; -} - -JS_PUBLIC_API(void *) -JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp) -{ - JSPrincipals *principals; - - principals = JS_StackFramePrincipals(cx, fp); - if (!principals) - return NULL; - return principals->getPrincipalArray(cx, principals); -} - -JS_PUBLIC_API(JSBool) -JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp) -{ - return !fp->script; -} - -/* this is deprecated, use JS_GetFrameScopeChain instead */ -JS_PUBLIC_API(JSObject *) -JS_GetFrameObject(JSContext *cx, JSStackFrame *fp) -{ - return fp->scopeChain; -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp) -{ - /* Force creation of argument and call objects if not yet created */ - (void) JS_GetFrameCallObject(cx, fp); - return js_GetScopeChain(cx, fp); -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp) -{ - if (! fp->fun) - return NULL; - - /* Force creation of argument object if not yet created */ - (void) js_GetArgsObject(cx, fp); - - /* - * XXX ill-defined: null return here means error was reported, unlike a - * null returned above or in the #else - */ - return js_GetCallObject(cx, fp, NULL); -} - - -JS_PUBLIC_API(JSObject *) -JS_GetFrameThis(JSContext *cx, JSStackFrame *fp) -{ - return fp->thisp; -} - -JS_PUBLIC_API(JSFunction *) -JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp) -{ - return fp->fun; -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp) -{ - return fp->argv && fp->fun ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp) -{ - return (fp->flags & JSFRAME_CONSTRUCTING) != 0; -} - -JS_PUBLIC_API(JSObject *) -JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp) -{ - return fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : NULL; -} - -JS_PUBLIC_API(JSBool) -JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp) -{ - return (fp->flags & JSFRAME_DEBUGGER) != 0; -} - -JS_PUBLIC_API(jsval) -JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp) -{ - return fp->rval; -} - -JS_PUBLIC_API(void) -JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval) -{ - fp->rval = rval; -} - -/************************************************************************/ - -JS_PUBLIC_API(const char *) -JS_GetScriptFilename(JSContext *cx, JSScript *script) -{ - return script->filename; -} - -JS_PUBLIC_API(uintN) -JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script) -{ - return script->lineno; -} - -JS_PUBLIC_API(uintN) -JS_GetScriptLineExtent(JSContext *cx, JSScript *script) -{ - return js_GetScriptLineExtent(script); -} - -JS_PUBLIC_API(JSVersion) -JS_GetScriptVersion(JSContext *cx, JSScript *script) -{ - return script->version & JSVERSION_MASK; -} - -/***************************************************************************/ - -JS_PUBLIC_API(void) -JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata) -{ - rt->newScriptHook = hook; - rt->newScriptHookData = callerdata; -} - -JS_PUBLIC_API(void) -JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, - void *callerdata) -{ - rt->destroyScriptHook = hook; - rt->destroyScriptHookData = callerdata; -} - -/***************************************************************************/ - -JS_PUBLIC_API(JSBool) -JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - JSObject *scobj; - uint32 flags, options; - JSScript *script; - JSBool ok; - - scobj = JS_GetFrameScopeChain(cx, fp); - if (!scobj) - return JS_FALSE; - - /* - * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL - * flags to the code generator (see js_EmitTree's TOK_SEMI case). - */ - flags = fp->flags; - fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; - options = cx->options; - cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, scobj, - JS_StackFramePrincipals(cx, fp), - chars, length, filename, lineno); - fp->flags = flags; - cx->options = options; - if (!script) - return JS_FALSE; - - ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL, - rval); - js_DestroyScript(cx, script); - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval) -{ - jschar *chars; - JSBool ok; - size_t len = length; - - chars = js_InflateString(cx, bytes, &len); - if (!chars) - return JS_FALSE; - length = (uintN) len; - ok = JS_EvaluateUCInStackFrame(cx, fp, chars, length, filename, lineno, - rval); - JS_free(cx, chars); - - return ok; -} - -/************************************************************************/ - -/* XXXbe this all needs to be reworked to avoid requiring JSScope types. */ - -JS_PUBLIC_API(JSScopeProperty *) -JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp) -{ - JSScopeProperty *sprop; - JSScope *scope; - - sprop = *iteratorp; - scope = OBJ_SCOPE(obj); - - /* XXXbe minor(?) incompatibility: iterate in reverse definition order */ - if (!sprop) { - sprop = SCOPE_LAST_PROP(scope); - } else { - while ((sprop = sprop->parent) != NULL) { - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - if (SCOPE_HAS_PROPERTY(scope, sprop)) - break; - } - } - *iteratorp = sprop; - return sprop; -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, - JSPropertyDesc *pd) -{ - JSPropertyOp getter; - JSScope *scope; - JSScopeProperty *aprop; - jsval lastException; - JSBool wasThrowing; - - pd->id = ID_TO_VALUE(sprop->id); - - wasThrowing = cx->throwing; - if (wasThrowing) { - lastException = cx->exception; - if (JSVAL_IS_GCTHING(lastException) && - !js_AddRoot(cx, &lastException, "lastException")) { - return JS_FALSE; - } - cx->throwing = JS_FALSE; - } - - if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) { - if (!cx->throwing) { - pd->flags = JSPD_ERROR; - pd->value = JSVAL_VOID; - } else { - pd->flags = JSPD_EXCEPTION; - pd->value = cx->exception; - } - } else { - pd->flags = 0; - } - - cx->throwing = wasThrowing; - if (wasThrowing) { - cx->exception = lastException; - if (JSVAL_IS_GCTHING(lastException)) - js_RemoveRoot(cx->runtime, &lastException); - } - - getter = sprop->getter; - pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) - | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) - | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) - | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) - | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) - | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); - - /* for Call Object 'real' getter isn't passed in to us */ - if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && - getter == js_CallClass.getProperty) { - /* - * Property of a heavyweight function's variable object having the - * class-default getter. It's either an argument if permanent, or a - * nested function if impermanent. Local variables have a special - * getter (js_GetCallVariable, tested above) and setter, and not the - * class default. - */ - pd->flags |= (sprop->attrs & JSPROP_PERMANENT) - ? JSPD_ARGUMENT - : JSPD_VARIABLE; - } - - pd->spare = 0; - pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) - ? sprop->shortid - : 0; - pd->alias = JSVAL_VOID; - scope = OBJ_SCOPE(obj); - if (SPROP_HAS_VALID_SLOT(sprop, scope)) { - for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) { - if (aprop != sprop && aprop->slot == sprop->slot) { - pd->alias = ID_TO_VALUE(aprop->id); - break; - } - } - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) -{ - JSClass *clasp; - JSScope *scope; - uint32 i, n; - JSPropertyDesc *pd; - JSScopeProperty *sprop; - - clasp = OBJ_GET_CLASS(cx, obj); - if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_DESCRIBE_PROPS, clasp->name); - return JS_FALSE; - } - if (!clasp->enumerate(cx, obj)) - return JS_FALSE; - - /* have no props, or object's scope has not mutated from that of proto */ - scope = OBJ_SCOPE(obj); - if (scope->object != obj || scope->entryCount == 0) { - pda->length = 0; - pda->array = NULL; - return JS_TRUE; - } - - n = scope->entryCount; - if (n > scope->map.nslots) - n = scope->map.nslots; - pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc)); - if (!pd) - return JS_FALSE; - i = 0; - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) - continue; - if (!js_AddRoot(cx, &pd[i].id, NULL)) - goto bad; - if (!js_AddRoot(cx, &pd[i].value, NULL)) - goto bad; - if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i])) - goto bad; - if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) - goto bad; - if (++i == n) - break; - } - pda->length = i; - pda->array = pd; - return JS_TRUE; - -bad: - pda->length = i + 1; - pda->array = pd; - JS_PutPropertyDescArray(cx, pda); - return JS_FALSE; -} - -JS_PUBLIC_API(void) -JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda) -{ - JSPropertyDesc *pd; - uint32 i; - - pd = pda->array; - for (i = 0; i < pda->length; i++) { - js_RemoveRoot(cx->runtime, &pd[i].id); - js_RemoveRoot(cx->runtime, &pd[i].value); - if (pd[i].flags & JSPD_ALIAS) - js_RemoveRoot(cx->runtime, &pd[i].alias); - } - JS_free(cx, pd); -} - -/************************************************************************/ - -JS_PUBLIC_API(JSBool) -JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure) -{ - rt->debuggerHandler = handler; - rt->debuggerHandlerData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure) -{ - rt->sourceHandler = handler; - rt->sourceHandlerData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) -{ - rt->executeHook = hook; - rt->executeHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure) -{ - rt->callHook = hook; - rt->callHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure) -{ - rt->objectHook = hook; - rt->objectHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure) -{ - rt->throwHook = hook; - rt->throwHookData = closure; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure) -{ - rt->debugErrorHook = hook; - rt->debugErrorHookData = closure; - return JS_TRUE; -} - -/************************************************************************/ - -JS_PUBLIC_API(size_t) -JS_GetObjectTotalSize(JSContext *cx, JSObject *obj) -{ - size_t nbytes; - JSScope *scope; - - nbytes = sizeof *obj + obj->map->nslots * sizeof obj->slots[0]; - if (OBJ_IS_NATIVE(obj)) { - scope = OBJ_SCOPE(obj); - if (scope->object == obj) { - nbytes += sizeof *scope; - nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *); - } - } - return nbytes; -} - -static size_t -GetAtomTotalSize(JSContext *cx, JSAtom *atom) -{ - size_t nbytes; - - nbytes = sizeof *atom; - if (ATOM_IS_STRING(atom)) { - nbytes += sizeof(JSString); - nbytes += (ATOM_TO_STRING(atom)->length + 1) * sizeof(jschar); - } else if (ATOM_IS_DOUBLE(atom)) { - nbytes += sizeof(jsdouble); - } else if (ATOM_IS_OBJECT(atom)) { - nbytes += JS_GetObjectTotalSize(cx, ATOM_TO_OBJECT(atom)); - } - return nbytes; -} - -JS_PUBLIC_API(size_t) -JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun) -{ - size_t nbytes; - - nbytes = sizeof *fun; - if (fun->object) - nbytes += JS_GetObjectTotalSize(cx, fun->object); - if (FUN_INTERPRETED(fun)) - nbytes += JS_GetScriptTotalSize(cx, fun->u.i.script); - if (fun->atom) - nbytes += GetAtomTotalSize(cx, fun->atom); - return nbytes; -} - -#include "jsemit.h" - -JS_PUBLIC_API(size_t) -JS_GetScriptTotalSize(JSContext *cx, JSScript *script) -{ - size_t nbytes, pbytes; - JSObject *obj; - jsatomid i; - jssrcnote *sn, *notes; - JSTryNote *tn, *tnotes; - JSPrincipals *principals; - - nbytes = sizeof *script; - obj = script->object; - if (obj) - nbytes += JS_GetObjectTotalSize(cx, obj); - - nbytes += script->length * sizeof script->code[0]; - nbytes += script->atomMap.length * sizeof script->atomMap.vector[0]; - for (i = 0; i < script->atomMap.length; i++) - nbytes += GetAtomTotalSize(cx, script->atomMap.vector[i]); - - if (script->filename) - nbytes += strlen(script->filename) + 1; - - notes = SCRIPT_NOTES(script); - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) - continue; - nbytes += (sn - notes + 1) * sizeof *sn; - - tnotes = script->trynotes; - if (tnotes) { - for (tn = tnotes; tn->catchStart; tn++) - continue; - nbytes += (tn - tnotes + 1) * sizeof *tn; - } - - principals = script->principals; - if (principals) { - JS_ASSERT(principals->refcount); - pbytes = sizeof *principals; - if (principals->refcount > 1) - pbytes = JS_HOWMANY(pbytes, principals->refcount); - nbytes += pbytes; - } - - return nbytes; -} - -JS_PUBLIC_API(uint32) -JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp) -{ - if (!fp) - fp = cx->fp; - while (fp) { - if (fp->script) { - return JS_GetScriptFilenameFlags(fp->script); - } - fp = fp->down; - } - return 0; - } - -JS_PUBLIC_API(uint32) -JS_GetScriptFilenameFlags(JSScript *script) -{ - JS_ASSERT(script); - if (!script->filename) - return JSFILENAME_NULL; - return js_GetScriptFilenameFlags(script->filename); -} - -JS_PUBLIC_API(JSBool) -JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags) -{ - if (!js_SaveScriptFilenameRT(rt, prefix, flags)) - return JS_FALSE; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_IsSystemObject(JSContext *cx, JSObject *obj) -{ - return (*js_GetGCThingFlags(obj) & GCF_SYSTEM) != 0; -} - -JS_PUBLIC_API(void) -JS_FlagSystemObject(JSContext *cx, JSObject *obj) -{ - uint8 *flagp; - - flagp = js_GetGCThingFlags(obj); - *flagp |= GCF_SYSTEM; -} diff --git a/spidermonkey/libjs/jsdbgapi.h b/spidermonkey/libjs/jsdbgapi.h deleted file mode 100644 index d2e1f1c..0000000 --- a/spidermonkey/libjs/jsdbgapi.h +++ /dev/null @@ -1,406 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsdbgapi_h___ -#define jsdbgapi_h___ -/* - * JS debugger API. - */ -#include "jsapi.h" -#include "jsopcode.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -extern void -js_PatchOpcode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op); - -extern JS_PUBLIC_API(JSBool) -JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler handler, void *closure); - -extern JS_PUBLIC_API(JSOp) -JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc); - -extern JS_PUBLIC_API(void) -JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc, - JSTrapHandler *handlerp, void **closurep); - -extern JS_PUBLIC_API(void) -JS_ClearScriptTraps(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(void) -JS_ClearAllTraps(JSContext *cx); - -extern JS_PUBLIC_API(JSTrapStatus) -JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep); - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_ClearWatchPoint(JSContext *cx, JSObject *obj, jsval id, - JSWatchPointHandler *handlerp, void **closurep); - -extern JS_PUBLIC_API(JSBool) -JS_ClearWatchPointsForObject(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(JSBool) -JS_ClearAllWatchPoints(JSContext *cx); - -#ifdef JS_HAS_OBJ_WATCHPOINT -/* - * Hide these non-API function prototypes by testing whether the internal - * header file "jsconfig.h" has been included. - */ -extern void -js_MarkWatchPoints(JSContext *cx); - -extern JSScopeProperty * -js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id); - -extern JSPropertyOp -js_GetWatchedSetter(JSRuntime *rt, JSScope *scope, - const JSScopeProperty *sprop); - -extern JSBool JS_DLL_CALLBACK -js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool JS_DLL_CALLBACK -js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSPropertyOp -js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter); - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - -/************************************************************************/ - -extern JS_PUBLIC_API(uintN) -JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); - -extern JS_PUBLIC_API(jsbytecode *) -JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno); - -extern JS_PUBLIC_API(JSScript *) -JS_GetFunctionScript(JSContext *cx, JSFunction *fun); - -extern JS_PUBLIC_API(JSNative) -JS_GetFunctionNative(JSContext *cx, JSFunction *fun); - -extern JS_PUBLIC_API(JSPrincipals *) -JS_GetScriptPrincipals(JSContext *cx, JSScript *script); - -/* - * Stack Frame Iterator - * - * Used to iterate through the JS stack frames to extract - * information from the frames. - */ - -extern JS_PUBLIC_API(JSStackFrame *) -JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp); - -extern JS_PUBLIC_API(JSScript *) -JS_GetFrameScript(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(jsbytecode *) -JS_GetFramePC(JSContext *cx, JSStackFrame *fp); - -/* - * Get the closest scripted frame below fp. If fp is null, start from cx->fp. - */ -extern JS_PUBLIC_API(JSStackFrame *) -JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp); - -/* - * Return a weak reference to fp's principals. A null return does not denote - * an error, it means there are no principals. - */ -extern JS_PUBLIC_API(JSPrincipals *) -JS_StackFramePrincipals(JSContext *cx, JSStackFrame *fp); - -/* - * This API is like JS_StackFramePrincipals(cx, caller), except that if - * cx->runtime->findObjectPrincipals is non-null, it returns the weaker of - * the caller's principals and the object principals of fp's callee function - * object (fp->argv[-2]), which is eval, Function, or a similar eval-like - * method. The caller parameter should be JS_GetScriptedCaller(cx, fp). - * - * All eval-like methods must use JS_EvalFramePrincipals to acquire a weak - * reference to the correct principals for the eval call to be secure, given - * an embedding that calls JS_SetObjectPrincipalsFinder (see jsapi.h). - */ -extern JS_PUBLIC_API(JSPrincipals *) -JS_EvalFramePrincipals(JSContext *cx, JSStackFrame *fp, JSStackFrame *caller); - -extern JS_PUBLIC_API(void *) -JS_GetFrameAnnotation(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(void) -JS_SetFrameAnnotation(JSContext *cx, JSStackFrame *fp, void *annotation); - -extern JS_PUBLIC_API(void *) -JS_GetFramePrincipalArray(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSBool) -JS_IsNativeFrame(JSContext *cx, JSStackFrame *fp); - -/* this is deprecated, use JS_GetFrameScopeChain instead */ -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameObject(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameCallObject(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameThis(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSFunction *) -JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp); - -/* XXXrginda Initially published with typo */ -#define JS_IsContructorFrame JS_IsConstructorFrame -extern JS_PUBLIC_API(JSBool) -JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(JSBool) -JS_IsDebuggerFrame(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(jsval) -JS_GetFrameReturnValue(JSContext *cx, JSStackFrame *fp); - -extern JS_PUBLIC_API(void) -JS_SetFrameReturnValue(JSContext *cx, JSStackFrame *fp, jsval rval); - -/** - * Return fp's callee function object (fp->argv[-2]) if it has one. - */ -extern JS_PUBLIC_API(JSObject *) -JS_GetFrameCalleeObject(JSContext *cx, JSStackFrame *fp); - -/************************************************************************/ - -extern JS_PUBLIC_API(const char *) -JS_GetScriptFilename(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(uintN) -JS_GetScriptBaseLineNumber(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(uintN) -JS_GetScriptLineExtent(JSContext *cx, JSScript *script); - -extern JS_PUBLIC_API(JSVersion) -JS_GetScriptVersion(JSContext *cx, JSScript *script); - -/************************************************************************/ - -/* - * Hook setters for script creation and destruction, see jsprvtd.h for the - * typedefs. These macros provide binary compatibility and newer, shorter - * synonyms. - */ -#define JS_SetNewScriptHook JS_SetNewScriptHookProc -#define JS_SetDestroyScriptHook JS_SetDestroyScriptHookProc - -extern JS_PUBLIC_API(void) -JS_SetNewScriptHook(JSRuntime *rt, JSNewScriptHook hook, void *callerdata); - -extern JS_PUBLIC_API(void) -JS_SetDestroyScriptHook(JSRuntime *rt, JSDestroyScriptHook hook, - void *callerdata); - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, - const jschar *chars, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -extern JS_PUBLIC_API(JSBool) -JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp, - const char *bytes, uintN length, - const char *filename, uintN lineno, - jsval *rval); - -/************************************************************************/ - -typedef struct JSPropertyDesc { - jsval id; /* primary id, a string or int */ - jsval value; /* property value */ - uint8 flags; /* flags, see below */ - uint8 spare; /* unused */ - uint16 slot; /* argument/variable slot */ - jsval alias; /* alias id if JSPD_ALIAS flag */ -} JSPropertyDesc; - -#define JSPD_ENUMERATE 0x01 /* visible to for/in loop */ -#define JSPD_READONLY 0x02 /* assignment is error */ -#define JSPD_PERMANENT 0x04 /* property cannot be deleted */ -#define JSPD_ALIAS 0x08 /* property has an alias id */ -#define JSPD_ARGUMENT 0x10 /* argument to function */ -#define JSPD_VARIABLE 0x20 /* local variable in function */ -#define JSPD_EXCEPTION 0x40 /* exception occurred fetching the property, */ - /* value is exception */ -#define JSPD_ERROR 0x80 /* native getter returned JS_FALSE without */ - /* throwing an exception */ - -typedef struct JSPropertyDescArray { - uint32 length; /* number of elements in array */ - JSPropertyDesc *array; /* alloc'd by Get, freed by Put */ -} JSPropertyDescArray; - -extern JS_PUBLIC_API(JSScopeProperty *) -JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp); - -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, - JSPropertyDesc *pd); - -extern JS_PUBLIC_API(JSBool) -JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda); - -extern JS_PUBLIC_API(void) -JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda); - -/************************************************************************/ - -extern JS_PUBLIC_API(JSBool) -JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure); - -extern JS_PUBLIC_API(JSBool) -JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure); - -/************************************************************************/ - -extern JS_PUBLIC_API(size_t) -JS_GetObjectTotalSize(JSContext *cx, JSObject *obj); - -extern JS_PUBLIC_API(size_t) -JS_GetFunctionTotalSize(JSContext *cx, JSFunction *fun); - -extern JS_PUBLIC_API(size_t) -JS_GetScriptTotalSize(JSContext *cx, JSScript *script); - -/* - * Get the top-most running script on cx starting from fp, or from the top of - * cx's frame stack if fp is null, and return its script filename flags. If - * the script has a null filename member, return JSFILENAME_NULL. - */ -extern JS_PUBLIC_API(uint32) -JS_GetTopScriptFilenameFlags(JSContext *cx, JSStackFrame *fp); - -/* - * Get the script filename flags for the script. If the script doesn't have a - * filename, return JSFILENAME_NULL. - */ -extern JS_PUBLIC_API(uint32) -JS_GetScriptFilenameFlags(JSScript *script); - -/* - * Associate flags with a script filename prefix in rt, so that any subsequent - * script compilation will inherit those flags if the script's filename is the - * same as prefix, or if prefix is a substring of the script's filename. - * - * The API defines only one flag bit, JSFILENAME_SYSTEM, leaving the remaining - * 31 bits up to the API client to define. The union of all 32 bits must not - * be a legal combination, however, in order to preserve JSFILENAME_NULL as a - * unique value. API clients may depend on JSFILENAME_SYSTEM being a set bit - * in JSFILENAME_NULL -- a script with a null filename member is presumed to - * be a "system" script. - */ -extern JS_PUBLIC_API(JSBool) -JS_FlagScriptFilenamePrefix(JSRuntime *rt, const char *prefix, uint32 flags); - -#define JSFILENAME_NULL 0xffffffff /* null script filename */ -#define JSFILENAME_SYSTEM 0x00000001 /* "system" script, see below */ - -/* - * Return true if obj is a "system" object, that is, one flagged by a prior - * call to JS_FlagSystemObject(cx, obj). What "system" means is up to the API - * client, but it can be used to coordinate access control policies based on - * script filenames and their prefixes, using JS_FlagScriptFilenamePrefix and - * JS_GetTopScriptFilenameFlags. - */ -extern JS_PUBLIC_API(JSBool) -JS_IsSystemObject(JSContext *cx, JSObject *obj); - -/* - * Flag obj as a "system" object. The API client can flag system objects to - * optimize access control checks. The engine stores but does not interpret - * the per-object flag set by this call. - */ -extern JS_PUBLIC_API(void) -JS_FlagSystemObject(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jsdbgapi_h___ */ diff --git a/spidermonkey/libjs/jsdhash.c b/spidermonkey/libjs/jsdhash.c deleted file mode 100644 index 295883b..0000000 --- a/spidermonkey/libjs/jsdhash.c +++ /dev/null @@ -1,826 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla JavaScript code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1999-2001 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brendan Eich (Original Author) - * Chris Waterson - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Double hashing implementation. - */ -#include -#include -#include -#include "jsbit.h" -#include "jsdhash.h" -#include "jsutil.h" /* for JS_ASSERT */ - -#ifdef JS_DHASHMETER -# if defined MOZILLA_CLIENT && defined DEBUG_XXXbrendan -# include "nsTraceMalloc.h" -# endif -# define METER(x) x -#else -# define METER(x) /* nothing */ -#endif - -/* - * The following DEBUG-only code is used to assert that calls to one of - * table->ops or to an enumerator do not cause re-entry into a call that - * can mutate the table. The recursion level is stored in additional - * space allocated at the end of the entry store to avoid changing - * JSDHashTable, which could cause issues when mixing DEBUG and - * non-DEBUG components. - */ -#ifdef DEBUG - -#define RECURSION_LEVEL(table_) (*(uint32*)(table_->entryStore + \ - JS_DHASH_TABLE_SIZE(table_) * \ - table_->entrySize)) - -#define ENTRY_STORE_EXTRA sizeof(uint32) -#define INCREMENT_RECURSION_LEVEL(table_) (++RECURSION_LEVEL(table_)) -#define DECREMENT_RECURSION_LEVEL(table_) (--RECURSION_LEVEL(table_)) - -#else - -#define ENTRY_STORE_EXTRA 0 -#define INCREMENT_RECURSION_LEVEL(table_) ((void)1) -#define DECREMENT_RECURSION_LEVEL(table_) ((void)0) - -#endif /* defined(DEBUG) */ - -JS_PUBLIC_API(void *) -JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes) -{ - return malloc(nbytes); -} - -JS_PUBLIC_API(void) -JS_DHashFreeTable(JSDHashTable *table, void *ptr) -{ - free(ptr); -} - -JS_PUBLIC_API(JSDHashNumber) -JS_DHashStringKey(JSDHashTable *table, const void *key) -{ - JSDHashNumber h; - const unsigned char *s; - - h = 0; - for (s = key; *s != '\0'; s++) - h = (h >> (JS_DHASH_BITS - 4)) ^ (h << 4) ^ *s; - return h; -} - -JS_PUBLIC_API(const void *) -JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - JSDHashEntryStub *stub = (JSDHashEntryStub *)entry; - - return stub->key; -} - -JS_PUBLIC_API(JSDHashNumber) -JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key) -{ - return (JSDHashNumber)(unsigned long)key >> 2; -} - -JS_PUBLIC_API(JSBool) -JS_DHashMatchEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; - - return stub->key == key; -} - -JS_PUBLIC_API(JSBool) -JS_DHashMatchStringKey(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; - - /* XXX tolerate null keys on account of sloppy Mozilla callers. */ - return stub->key == key || - (stub->key && key && strcmp(stub->key, key) == 0); -} - -JS_PUBLIC_API(void) -JS_DHashMoveEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *from, - JSDHashEntryHdr *to) -{ - memcpy(to, from, table->entrySize); -} - -JS_PUBLIC_API(void) -JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - memset(entry, 0, table->entrySize); -} - -JS_PUBLIC_API(void) -JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - const JSDHashEntryStub *stub = (const JSDHashEntryStub *)entry; - - free((void *) stub->key); - memset(entry, 0, table->entrySize); -} - -JS_PUBLIC_API(void) -JS_DHashFinalizeStub(JSDHashTable *table) -{ -} - -static const JSDHashTableOps stub_ops = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - JS_DHashVoidPtrKeyStub, - JS_DHashMatchEntryStub, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -JS_PUBLIC_API(const JSDHashTableOps *) -JS_DHashGetStubOps(void) -{ - return &stub_ops; -} - -JS_PUBLIC_API(JSDHashTable *) -JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, - uint32 capacity) -{ - JSDHashTable *table; - - table = (JSDHashTable *) malloc(sizeof *table); - if (!table) - return NULL; - if (!JS_DHashTableInit(table, ops, data, entrySize, capacity)) { - free(table); - return NULL; - } - return table; -} - -JS_PUBLIC_API(void) -JS_DHashTableDestroy(JSDHashTable *table) -{ - JS_DHashTableFinish(table); - free(table); -} - -JS_PUBLIC_API(JSBool) -JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, - uint32 entrySize, uint32 capacity) -{ - int log2; - uint32 nbytes; - -#ifdef DEBUG - if (entrySize > 10 * sizeof(void *)) { - fprintf(stderr, - "jsdhash: for the table at address %p, the given entrySize" - " of %lu %s favors chaining over double hashing.\n", - (void *)table, - (unsigned long) entrySize, - (entrySize > 16 * sizeof(void*)) ? "definitely" : "probably"); - } -#endif - - table->ops = ops; - table->data = data; - if (capacity < JS_DHASH_MIN_SIZE) - capacity = JS_DHASH_MIN_SIZE; - - JS_CEILING_LOG2(log2, capacity); - - capacity = JS_BIT(log2); - if (capacity >= JS_DHASH_SIZE_LIMIT) - return JS_FALSE; - table->hashShift = JS_DHASH_BITS - log2; - table->maxAlphaFrac = 0xC0; /* .75 */ - table->minAlphaFrac = 0x40; /* .25 */ - table->entrySize = entrySize; - table->entryCount = table->removedCount = 0; - table->generation = 0; - nbytes = capacity * entrySize; - - table->entryStore = ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); - if (!table->entryStore) - return JS_FALSE; - memset(table->entryStore, 0, nbytes); - METER(memset(&table->stats, 0, sizeof table->stats)); - -#ifdef DEBUG - RECURSION_LEVEL(table) = 0; -#endif - - return JS_TRUE; -} - -/* - * Compute max and min load numbers (entry counts) from table params. - */ -#define MAX_LOAD(table, size) (((table)->maxAlphaFrac * (size)) >> 8) -#define MIN_LOAD(table, size) (((table)->minAlphaFrac * (size)) >> 8) - -JS_PUBLIC_API(void) -JS_DHashTableSetAlphaBounds(JSDHashTable *table, - float maxAlpha, - float minAlpha) -{ - uint32 size; - - /* - * Reject obviously insane bounds, rather than trying to guess what the - * buggy caller intended. - */ - JS_ASSERT(0.5 <= maxAlpha && maxAlpha < 1 && 0 <= minAlpha); - if (maxAlpha < 0.5 || 1 <= maxAlpha || minAlpha < 0) - return; - - /* - * Ensure that at least one entry will always be free. If maxAlpha at - * minimum size leaves no entries free, reduce maxAlpha based on minimum - * size and the precision limit of maxAlphaFrac's fixed point format. - */ - JS_ASSERT(JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) >= 1); - if (JS_DHASH_MIN_SIZE - (maxAlpha * JS_DHASH_MIN_SIZE) < 1) { - maxAlpha = (float) - (JS_DHASH_MIN_SIZE - JS_MAX(JS_DHASH_MIN_SIZE / 256, 1)) - / JS_DHASH_MIN_SIZE; - } - - /* - * Ensure that minAlpha is strictly less than half maxAlpha. Take care - * not to truncate an entry's worth of alpha when storing in minAlphaFrac - * (8-bit fixed point format). - */ - JS_ASSERT(minAlpha < maxAlpha / 2); - if (minAlpha >= maxAlpha / 2) { - size = JS_DHASH_TABLE_SIZE(table); - minAlpha = (size * maxAlpha - JS_MAX(size / 256, 1)) / (2 * size); - } - - table->maxAlphaFrac = (uint8)(maxAlpha * 256); - table->minAlphaFrac = (uint8)(minAlpha * 256); -} - -/* - * Double hashing needs the second hash code to be relatively prime to table - * size, so we simply make hash2 odd. - */ -#define HASH1(hash0, shift) ((hash0) >> (shift)) -#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) - -/* - * Reserve keyHash 0 for free entries and 1 for removed-entry sentinels. Note - * that a removed-entry sentinel need be stored only if the removed entry had - * a colliding entry added after it. Therefore we can use 1 as the collision - * flag in addition to the removed-entry sentinel value. Multiplicative hash - * uses the high order bits of keyHash, so this least-significant reservation - * should not hurt the hash function's effectiveness much. - * - * If you change any of these magic numbers, also update JS_DHASH_ENTRY_IS_LIVE - * in jsdhash.h. It used to be private to jsdhash.c, but then became public to - * assist iterator writers who inspect table->entryStore directly. - */ -#define COLLISION_FLAG ((JSDHashNumber) 1) -#define MARK_ENTRY_FREE(entry) ((entry)->keyHash = 0) -#define MARK_ENTRY_REMOVED(entry) ((entry)->keyHash = 1) -#define ENTRY_IS_REMOVED(entry) ((entry)->keyHash == 1) -#define ENTRY_IS_LIVE(entry) JS_DHASH_ENTRY_IS_LIVE(entry) -#define ENSURE_LIVE_KEYHASH(hash0) if (hash0 < 2) hash0 -= 2; else (void)0 - -/* Match an entry's keyHash against an unstored one computed from a key. */ -#define MATCH_ENTRY_KEYHASH(entry,hash0) \ - (((entry)->keyHash & ~COLLISION_FLAG) == (hash0)) - -/* Compute the address of the indexed entry in table. */ -#define ADDRESS_ENTRY(table, index) \ - ((JSDHashEntryHdr *)((table)->entryStore + (index) * (table)->entrySize)) - -JS_PUBLIC_API(void) -JS_DHashTableFinish(JSDHashTable *table) -{ - char *entryAddr, *entryLimit; - uint32 entrySize; - JSDHashEntryHdr *entry; - -#ifdef DEBUG_XXXbrendan - static FILE *dumpfp = NULL; - if (!dumpfp) dumpfp = fopen("/tmp/jsdhash.bigdump", "w"); - if (dumpfp) { -#ifdef MOZILLA_CLIENT - NS_TraceStack(1, dumpfp); -#endif - JS_DHashTableDumpMeter(table, NULL, dumpfp); - fputc('\n', dumpfp); - } -#endif - - INCREMENT_RECURSION_LEVEL(table); - - /* Call finalize before clearing entries, so it can enumerate them. */ - table->ops->finalize(table); - - /* Clear any remaining live entries. */ - entryAddr = table->entryStore; - entrySize = table->entrySize; - entryLimit = entryAddr + JS_DHASH_TABLE_SIZE(table) * entrySize; - while (entryAddr < entryLimit) { - entry = (JSDHashEntryHdr *)entryAddr; - if (ENTRY_IS_LIVE(entry)) { - METER(table->stats.removeEnums++); - table->ops->clearEntry(table, entry); - } - entryAddr += entrySize; - } - - DECREMENT_RECURSION_LEVEL(table); - JS_ASSERT(RECURSION_LEVEL(table) == 0); - - /* Free entry storage last. */ - table->ops->freeTable(table, table->entryStore); -} - -static JSDHashEntryHdr * JS_DHASH_FASTCALL -SearchTable(JSDHashTable *table, const void *key, JSDHashNumber keyHash, - JSDHashOperator op) -{ - JSDHashNumber hash1, hash2; - int hashShift, sizeLog2; - JSDHashEntryHdr *entry, *firstRemoved; - JSDHashMatchEntry matchEntry; - uint32 sizeMask; - - METER(table->stats.searches++); - JS_ASSERT(!(keyHash & COLLISION_FLAG)); - - /* Compute the primary hash address. */ - hashShift = table->hashShift; - hash1 = HASH1(keyHash, hashShift); - entry = ADDRESS_ENTRY(table, hash1); - - /* Miss: return space for a new entry. */ - if (JS_DHASH_ENTRY_IS_FREE(entry)) { - METER(table->stats.misses++); - return entry; - } - - /* Hit: return entry. */ - matchEntry = table->ops->matchEntry; - if (MATCH_ENTRY_KEYHASH(entry, keyHash) && matchEntry(table, entry, key)) { - METER(table->stats.hits++); - return entry; - } - - /* Collision: double hash. */ - sizeLog2 = JS_DHASH_BITS - table->hashShift; - hash2 = HASH2(keyHash, sizeLog2, hashShift); - sizeMask = JS_BITMASK(sizeLog2); - - /* Save the first removed entry pointer so JS_DHASH_ADD can recycle it. */ - if (ENTRY_IS_REMOVED(entry)) { - firstRemoved = entry; - } else { - firstRemoved = NULL; - if (op == JS_DHASH_ADD) - entry->keyHash |= COLLISION_FLAG; - } - - for (;;) { - METER(table->stats.steps++); - hash1 -= hash2; - hash1 &= sizeMask; - - entry = ADDRESS_ENTRY(table, hash1); - if (JS_DHASH_ENTRY_IS_FREE(entry)) { - METER(table->stats.misses++); - return (firstRemoved && op == JS_DHASH_ADD) ? firstRemoved : entry; - } - - if (MATCH_ENTRY_KEYHASH(entry, keyHash) && - matchEntry(table, entry, key)) { - METER(table->stats.hits++); - return entry; - } - - if (ENTRY_IS_REMOVED(entry)) { - if (!firstRemoved) - firstRemoved = entry; - } else { - if (op == JS_DHASH_ADD) - entry->keyHash |= COLLISION_FLAG; - } - } - - /* NOTREACHED */ - return NULL; -} - -static JSBool -ChangeTable(JSDHashTable *table, int deltaLog2) -{ - int oldLog2, newLog2; - uint32 oldCapacity, newCapacity; - char *newEntryStore, *oldEntryStore, *oldEntryAddr; - uint32 entrySize, i, nbytes; - JSDHashEntryHdr *oldEntry, *newEntry; - JSDHashGetKey getKey; - JSDHashMoveEntry moveEntry; -#ifdef DEBUG - uint32 recursionLevel; -#endif - - /* Look, but don't touch, until we succeed in getting new entry store. */ - oldLog2 = JS_DHASH_BITS - table->hashShift; - newLog2 = oldLog2 + deltaLog2; - oldCapacity = JS_BIT(oldLog2); - newCapacity = JS_BIT(newLog2); - if (newCapacity >= JS_DHASH_SIZE_LIMIT) - return JS_FALSE; - entrySize = table->entrySize; - nbytes = newCapacity * entrySize; - - newEntryStore = table->ops->allocTable(table, nbytes + ENTRY_STORE_EXTRA); - if (!newEntryStore) - return JS_FALSE; - - /* We can't fail from here on, so update table parameters. */ -#ifdef DEBUG - recursionLevel = RECURSION_LEVEL(table); -#endif - table->hashShift = JS_DHASH_BITS - newLog2; - table->removedCount = 0; - table->generation++; - - /* Assign the new entry store to table. */ - memset(newEntryStore, 0, nbytes); - oldEntryAddr = oldEntryStore = table->entryStore; - table->entryStore = newEntryStore; - getKey = table->ops->getKey; - moveEntry = table->ops->moveEntry; -#ifdef DEBUG - RECURSION_LEVEL(table) = recursionLevel; -#endif - - /* Copy only live entries, leaving removed ones behind. */ - for (i = 0; i < oldCapacity; i++) { - oldEntry = (JSDHashEntryHdr *)oldEntryAddr; - if (ENTRY_IS_LIVE(oldEntry)) { - oldEntry->keyHash &= ~COLLISION_FLAG; - newEntry = SearchTable(table, getKey(table, oldEntry), - oldEntry->keyHash, JS_DHASH_ADD); - JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(newEntry)); - moveEntry(table, oldEntry, newEntry); - newEntry->keyHash = oldEntry->keyHash; - } - oldEntryAddr += entrySize; - } - - table->ops->freeTable(table, oldEntryStore); - return JS_TRUE; -} - -JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL -JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op) -{ - JSDHashNumber keyHash; - JSDHashEntryHdr *entry; - uint32 size; - int deltaLog2; - - JS_ASSERT(op == JS_DHASH_LOOKUP || RECURSION_LEVEL(table) == 0); - INCREMENT_RECURSION_LEVEL(table); - - keyHash = table->ops->hashKey(table, key); - keyHash *= JS_DHASH_GOLDEN_RATIO; - - /* Avoid 0 and 1 hash codes, they indicate free and removed entries. */ - ENSURE_LIVE_KEYHASH(keyHash); - keyHash &= ~COLLISION_FLAG; - - switch (op) { - case JS_DHASH_LOOKUP: - METER(table->stats.lookups++); - entry = SearchTable(table, key, keyHash, op); - break; - - case JS_DHASH_ADD: - /* - * If alpha is >= .75, grow or compress the table. If key is already - * in the table, we may grow once more than necessary, but only if we - * are on the edge of being overloaded. - */ - size = JS_DHASH_TABLE_SIZE(table); - if (table->entryCount + table->removedCount >= MAX_LOAD(table, size)) { - /* Compress if a quarter or more of all entries are removed. */ - if (table->removedCount >= size >> 2) { - METER(table->stats.compresses++); - deltaLog2 = 0; - } else { - METER(table->stats.grows++); - deltaLog2 = 1; - } - - /* - * Grow or compress table, returning null if ChangeTable fails and - * falling through might claim the last free entry. - */ - if (!ChangeTable(table, deltaLog2) && - table->entryCount + table->removedCount == size - 1) { - METER(table->stats.addFailures++); - entry = NULL; - break; - } - } - - /* - * Look for entry after possibly growing, so we don't have to add it, - * then skip it while growing the table and re-add it after. - */ - entry = SearchTable(table, key, keyHash, op); - if (!ENTRY_IS_LIVE(entry)) { - /* Initialize the entry, indicating that it's no longer free. */ - METER(table->stats.addMisses++); - if (ENTRY_IS_REMOVED(entry)) { - METER(table->stats.addOverRemoved++); - table->removedCount--; - keyHash |= COLLISION_FLAG; - } - if (table->ops->initEntry && - !table->ops->initEntry(table, entry, key)) { - /* We haven't claimed entry yet; fail with null return. */ - memset(entry + 1, 0, table->entrySize - sizeof *entry); - entry = NULL; - break; - } - entry->keyHash = keyHash; - table->entryCount++; - } - METER(else table->stats.addHits++); - break; - - case JS_DHASH_REMOVE: - entry = SearchTable(table, key, keyHash, op); - if (ENTRY_IS_LIVE(entry)) { - /* Clear this entry and mark it as "removed". */ - METER(table->stats.removeHits++); - JS_DHashTableRawRemove(table, entry); - - /* Shrink if alpha is <= .25 and table isn't too small already. */ - size = JS_DHASH_TABLE_SIZE(table); - if (size > JS_DHASH_MIN_SIZE && - table->entryCount <= MIN_LOAD(table, size)) { - METER(table->stats.shrinks++); - (void) ChangeTable(table, -1); - } - } - METER(else table->stats.removeMisses++); - entry = NULL; - break; - - default: - JS_ASSERT(0); - entry = NULL; - } - - DECREMENT_RECURSION_LEVEL(table); - - return entry; -} - -JS_PUBLIC_API(void) -JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry) -{ - JSDHashNumber keyHash; /* load first in case clearEntry goofs it */ - - JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(entry)); - keyHash = entry->keyHash; - table->ops->clearEntry(table, entry); - if (keyHash & COLLISION_FLAG) { - MARK_ENTRY_REMOVED(entry); - table->removedCount++; - } else { - METER(table->stats.removeFrees++); - MARK_ENTRY_FREE(entry); - } - table->entryCount--; -} - -JS_PUBLIC_API(uint32) -JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg) -{ - char *entryAddr, *entryLimit; - uint32 i, capacity, entrySize, ceiling; - JSBool didRemove; - JSDHashEntryHdr *entry; - JSDHashOperator op; - - INCREMENT_RECURSION_LEVEL(table); - - entryAddr = table->entryStore; - entrySize = table->entrySize; - capacity = JS_DHASH_TABLE_SIZE(table); - entryLimit = entryAddr + capacity * entrySize; - i = 0; - didRemove = JS_FALSE; - while (entryAddr < entryLimit) { - entry = (JSDHashEntryHdr *)entryAddr; - if (ENTRY_IS_LIVE(entry)) { - op = etor(table, entry, i++, arg); - if (op & JS_DHASH_REMOVE) { - METER(table->stats.removeEnums++); - JS_DHashTableRawRemove(table, entry); - didRemove = JS_TRUE; - } - if (op & JS_DHASH_STOP) - break; - } - entryAddr += entrySize; - } - - JS_ASSERT(!didRemove || RECURSION_LEVEL(table) == 1); - - /* - * Shrink or compress if a quarter or more of all entries are removed, or - * if the table is underloaded according to the configured minimum alpha, - * and is not minimal-size already. Do this only if we removed above, so - * non-removing enumerations can count on stable table->entryStore until - * the next non-lookup-Operate or removing-Enumerate. - */ - if (didRemove && - (table->removedCount >= capacity >> 2 || - (capacity > JS_DHASH_MIN_SIZE && - table->entryCount <= MIN_LOAD(table, capacity)))) { - METER(table->stats.enumShrinks++); - capacity = table->entryCount; - capacity += capacity >> 1; - if (capacity < JS_DHASH_MIN_SIZE) - capacity = JS_DHASH_MIN_SIZE; - - JS_CEILING_LOG2(ceiling, capacity); - ceiling -= JS_DHASH_BITS - table->hashShift; - - (void) ChangeTable(table, ceiling); - } - - DECREMENT_RECURSION_LEVEL(table); - - return i; -} - -#ifdef JS_DHASHMETER -#include - -JS_PUBLIC_API(void) -JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp) -{ - char *entryAddr; - uint32 entrySize, entryCount; - int hashShift, sizeLog2; - uint32 i, tableSize, sizeMask, chainLen, maxChainLen, chainCount; - JSDHashNumber hash1, hash2, saveHash1, maxChainHash1, maxChainHash2; - double sqsum, mean, variance, sigma; - JSDHashEntryHdr *entry, *probe; - - entryAddr = table->entryStore; - entrySize = table->entrySize; - hashShift = table->hashShift; - sizeLog2 = JS_DHASH_BITS - hashShift; - tableSize = JS_DHASH_TABLE_SIZE(table); - sizeMask = JS_BITMASK(sizeLog2); - chainCount = maxChainLen = 0; - hash2 = 0; - sqsum = 0; - - for (i = 0; i < tableSize; i++) { - entry = (JSDHashEntryHdr *)entryAddr; - entryAddr += entrySize; - if (!ENTRY_IS_LIVE(entry)) - continue; - hash1 = HASH1(entry->keyHash & ~COLLISION_FLAG, hashShift); - saveHash1 = hash1; - probe = ADDRESS_ENTRY(table, hash1); - chainLen = 1; - if (probe == entry) { - /* Start of a (possibly unit-length) chain. */ - chainCount++; - } else { - hash2 = HASH2(entry->keyHash & ~COLLISION_FLAG, sizeLog2, - hashShift); - do { - chainLen++; - hash1 -= hash2; - hash1 &= sizeMask; - probe = ADDRESS_ENTRY(table, hash1); - } while (probe != entry); - } - sqsum += chainLen * chainLen; - if (chainLen > maxChainLen) { - maxChainLen = chainLen; - maxChainHash1 = saveHash1; - maxChainHash2 = hash2; - } - } - - entryCount = table->entryCount; - if (entryCount && chainCount) { - mean = (double)entryCount / chainCount; - variance = chainCount * sqsum - entryCount * entryCount; - if (variance < 0 || chainCount == 1) - variance = 0; - else - variance /= chainCount * (chainCount - 1); - sigma = sqrt(variance); - } else { - mean = sigma = 0; - } - - fprintf(fp, "Double hashing statistics:\n"); - fprintf(fp, " table size (in entries): %u\n", tableSize); - fprintf(fp, " number of entries: %u\n", table->entryCount); - fprintf(fp, " number of removed entries: %u\n", table->removedCount); - fprintf(fp, " number of searches: %u\n", table->stats.searches); - fprintf(fp, " number of hits: %u\n", table->stats.hits); - fprintf(fp, " number of misses: %u\n", table->stats.misses); - fprintf(fp, " mean steps per search: %g\n", table->stats.searches ? - (double)table->stats.steps - / table->stats.searches : - 0.); - fprintf(fp, " mean hash chain length: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " maximum hash chain length: %u\n", maxChainLen); - fprintf(fp, " number of lookups: %u\n", table->stats.lookups); - fprintf(fp, " adds that made a new entry: %u\n", table->stats.addMisses); - fprintf(fp, "adds that recycled removeds: %u\n", table->stats.addOverRemoved); - fprintf(fp, " adds that found an entry: %u\n", table->stats.addHits); - fprintf(fp, " add failures: %u\n", table->stats.addFailures); - fprintf(fp, " useful removes: %u\n", table->stats.removeHits); - fprintf(fp, " useless removes: %u\n", table->stats.removeMisses); - fprintf(fp, "removes that freed an entry: %u\n", table->stats.removeFrees); - fprintf(fp, " removes while enumerating: %u\n", table->stats.removeEnums); - fprintf(fp, " number of grows: %u\n", table->stats.grows); - fprintf(fp, " number of shrinks: %u\n", table->stats.shrinks); - fprintf(fp, " number of compresses: %u\n", table->stats.compresses); - fprintf(fp, "number of enumerate shrinks: %u\n", table->stats.enumShrinks); - - if (dump && maxChainLen && hash2) { - fputs("Maximum hash chain:\n", fp); - hash1 = maxChainHash1; - hash2 = maxChainHash2; - entry = ADDRESS_ENTRY(table, hash1); - i = 0; - do { - if (dump(table, entry, i++, fp) != JS_DHASH_NEXT) - break; - hash1 -= hash2; - hash1 &= sizeMask; - entry = ADDRESS_ENTRY(table, hash1); - } while (JS_DHASH_ENTRY_IS_BUSY(entry)); - } -} -#endif /* JS_DHASHMETER */ diff --git a/spidermonkey/libjs/jsdhash.h b/spidermonkey/libjs/jsdhash.h deleted file mode 100644 index 76867e5..0000000 --- a/spidermonkey/libjs/jsdhash.h +++ /dev/null @@ -1,581 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla JavaScript code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1999-2001 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brendan Eich (Original Author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsdhash_h___ -#define jsdhash_h___ -/* - * Double hashing, a la Knuth 6. - */ -#include "jstypes.h" - -JS_BEGIN_EXTERN_C - -#if defined(__GNUC__) && defined(__i386__) && (__GNUC__ >= 3) && !defined(XP_OS2) -#define JS_DHASH_FASTCALL __attribute__ ((regparm (3),stdcall)) -#elif defined(XP_WIN) -#define JS_DHASH_FASTCALL __fastcall -#else -#define JS_DHASH_FASTCALL -#endif - -#ifdef DEBUG_XXXbrendan -#define JS_DHASHMETER 1 -#endif - -/* Table size limit, do not equal or exceed (see min&maxAlphaFrac, below). */ -#undef JS_DHASH_SIZE_LIMIT -#define JS_DHASH_SIZE_LIMIT JS_BIT(24) - -/* Minimum table size, or gross entry count (net is at most .75 loaded). */ -#ifndef JS_DHASH_MIN_SIZE -#define JS_DHASH_MIN_SIZE 16 -#elif (JS_DHASH_MIN_SIZE & (JS_DHASH_MIN_SIZE - 1)) != 0 -#error "JS_DHASH_MIN_SIZE must be a power of two!" -#endif - -/* - * Multiplicative hash uses an unsigned 32 bit integer and the golden ratio, - * expressed as a fixed-point 32-bit fraction. - */ -#define JS_DHASH_BITS 32 -#define JS_DHASH_GOLDEN_RATIO 0x9E3779B9U - -/* Primitive and forward-struct typedefs. */ -typedef uint32 JSDHashNumber; -typedef struct JSDHashEntryHdr JSDHashEntryHdr; -typedef struct JSDHashEntryStub JSDHashEntryStub; -typedef struct JSDHashTable JSDHashTable; -typedef struct JSDHashTableOps JSDHashTableOps; - -/* - * Table entry header structure. - * - * In order to allow in-line allocation of key and value, we do not declare - * either here. Instead, the API uses const void *key as a formal parameter, - * and asks each entry for its key when necessary via a getKey callback, used - * when growing or shrinking the table. Other callback types are defined - * below and grouped into the JSDHashTableOps structure, for single static - * initialization per hash table sub-type. - * - * Each hash table sub-type should nest the JSDHashEntryHdr structure at the - * front of its particular entry type. The keyHash member contains the result - * of multiplying the hash code returned from the hashKey callback (see below) - * by JS_DHASH_GOLDEN_RATIO, then constraining the result to avoid the magic 0 - * and 1 values. The stored keyHash value is table size invariant, and it is - * maintained automatically by JS_DHashTableOperate -- users should never set - * it, and its only uses should be via the entry macros below. - * - * The JS_DHASH_ENTRY_IS_LIVE macro tests whether entry is neither free nor - * removed. An entry may be either busy or free; if busy, it may be live or - * removed. Consumers of this API should not access members of entries that - * are not live. - * - * However, use JS_DHASH_ENTRY_IS_BUSY for faster liveness testing of entries - * returned by JS_DHashTableOperate, as JS_DHashTableOperate never returns a - * non-live, busy (i.e., removed) entry pointer to its caller. See below for - * more details on JS_DHashTableOperate's calling rules. - */ -struct JSDHashEntryHdr { - JSDHashNumber keyHash; /* every entry must begin like this */ -}; - -#define JS_DHASH_ENTRY_IS_FREE(entry) ((entry)->keyHash == 0) -#define JS_DHASH_ENTRY_IS_BUSY(entry) (!JS_DHASH_ENTRY_IS_FREE(entry)) -#define JS_DHASH_ENTRY_IS_LIVE(entry) ((entry)->keyHash >= 2) - -/* - * A JSDHashTable is currently 8 words (without the JS_DHASHMETER overhead) - * on most architectures, and may be allocated on the stack or within another - * structure or class (see below for the Init and Finish functions to use). - * - * To decide whether to use double hashing vs. chaining, we need to develop a - * trade-off relation, as follows: - * - * Let alpha be the load factor, esize the entry size in words, count the - * entry count, and pow2 the power-of-two table size in entries. - * - * (JSDHashTable overhead) > (JSHashTable overhead) - * (unused table entry space) > (malloc and .next overhead per entry) + - * (buckets overhead) - * (1 - alpha) * esize * pow2 > 2 * count + pow2 - * - * Notice that alpha is by definition (count / pow2): - * - * (1 - alpha) * esize * pow2 > 2 * alpha * pow2 + pow2 - * (1 - alpha) * esize > 2 * alpha + 1 - * - * esize > (1 + 2 * alpha) / (1 - alpha) - * - * This assumes both tables must keep keyHash, key, and value for each entry, - * where key and value point to separately allocated strings or structures. - * If key and value can be combined into one pointer, then the trade-off is: - * - * esize > (1 + 3 * alpha) / (1 - alpha) - * - * If the entry value can be a subtype of JSDHashEntryHdr, rather than a type - * that must be allocated separately and referenced by an entry.value pointer - * member, and provided key's allocation can be fused with its entry's, then - * k (the words wasted per entry with chaining) is 4. - * - * To see these curves, feed gnuplot input like so: - * - * gnuplot> f(x,k) = (1 + k * x) / (1 - x) - * gnuplot> plot [0:.75] f(x,2), f(x,3), f(x,4) - * - * For k of 2 and a well-loaded table (alpha > .5), esize must be more than 4 - * words for chaining to be more space-efficient than double hashing. - * - * Solving for alpha helps us decide when to shrink an underloaded table: - * - * esize > (1 + k * alpha) / (1 - alpha) - * esize - alpha * esize > 1 + k * alpha - * esize - 1 > (k + esize) * alpha - * (esize - 1) / (k + esize) > alpha - * - * alpha < (esize - 1) / (esize + k) - * - * Therefore double hashing should keep alpha >= (esize - 1) / (esize + k), - * assuming esize is not too large (in which case, chaining should probably be - * used for any alpha). For esize=2 and k=3, we want alpha >= .2; for esize=3 - * and k=2, we want alpha >= .4. For k=4, esize could be 6, and alpha >= .5 - * would still obtain. See the JS_DHASH_MIN_ALPHA macro further below. - * - * The current implementation uses a configurable lower bound on alpha, which - * defaults to .25, when deciding to shrink the table (while still respecting - * JS_DHASH_MIN_SIZE). - * - * Note a qualitative difference between chaining and double hashing: under - * chaining, entry addresses are stable across table shrinks and grows. With - * double hashing, you can't safely hold an entry pointer and use it after an - * ADD or REMOVE operation, unless you sample table->generation before adding - * or removing, and compare the sample after, dereferencing the entry pointer - * only if table->generation has not changed. - * - * The moral of this story: there is no one-size-fits-all hash table scheme, - * but for small table entry size, and assuming entry address stability is not - * required, double hashing wins. - */ -struct JSDHashTable { - const JSDHashTableOps *ops; /* virtual operations, see below */ - void *data; /* ops- and instance-specific data */ - int16 hashShift; /* multiplicative hash shift */ - uint8 maxAlphaFrac; /* 8-bit fixed point max alpha */ - uint8 minAlphaFrac; /* 8-bit fixed point min alpha */ - uint32 entrySize; /* number of bytes in an entry */ - uint32 entryCount; /* number of entries in table */ - uint32 removedCount; /* removed entry sentinels in table */ - uint32 generation; /* entry storage generation number */ - char *entryStore; /* entry storage */ -#ifdef JS_DHASHMETER - struct JSDHashStats { - uint32 searches; /* total number of table searches */ - uint32 steps; /* hash chain links traversed */ - uint32 hits; /* searches that found key */ - uint32 misses; /* searches that didn't find key */ - uint32 lookups; /* number of JS_DHASH_LOOKUPs */ - uint32 addMisses; /* adds that miss, and do work */ - uint32 addOverRemoved; /* adds that recycled a removed entry */ - uint32 addHits; /* adds that hit an existing entry */ - uint32 addFailures; /* out-of-memory during add growth */ - uint32 removeHits; /* removes that hit, and do work */ - uint32 removeMisses; /* useless removes that miss */ - uint32 removeFrees; /* removes that freed entry directly */ - uint32 removeEnums; /* removes done by Enumerate */ - uint32 grows; /* table expansions */ - uint32 shrinks; /* table contractions */ - uint32 compresses; /* table compressions */ - uint32 enumShrinks; /* contractions after Enumerate */ - } stats; -#endif -}; - -/* - * Size in entries (gross, not net of free and removed sentinels) for table. - * We store hashShift rather than sizeLog2 to optimize the collision-free case - * in SearchTable. - */ -#define JS_DHASH_TABLE_SIZE(table) JS_BIT(JS_DHASH_BITS - (table)->hashShift) - -/* - * Table space at entryStore is allocated and freed using these callbacks. - * The allocator should return null on error only (not if called with nbytes - * equal to 0; but note that jsdhash.c code will never call with 0 nbytes). - */ -typedef void * -(* JS_DLL_CALLBACK JSDHashAllocTable)(JSDHashTable *table, uint32 nbytes); - -typedef void -(* JS_DLL_CALLBACK JSDHashFreeTable) (JSDHashTable *table, void *ptr); - -/* - * When a table grows or shrinks, each entry is queried for its key using this - * callback. NB: in that event, entry is not in table any longer; it's in the - * old entryStore vector, which is due to be freed once all entries have been - * moved via moveEntry callbacks. - */ -typedef const void * -(* JS_DLL_CALLBACK JSDHashGetKey) (JSDHashTable *table, - JSDHashEntryHdr *entry); - -/* - * Compute the hash code for a given key to be looked up, added, or removed - * from table. A hash code may have any JSDHashNumber value. - */ -typedef JSDHashNumber -(* JS_DLL_CALLBACK JSDHashHashKey) (JSDHashTable *table, const void *key); - -/* - * Compare the key identifying entry in table with the provided key parameter. - * Return JS_TRUE if keys match, JS_FALSE otherwise. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSDHashMatchEntry)(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key); - -/* - * Copy the data starting at from to the new entry storage at to. Do not add - * reference counts for any strong references in the entry, however, as this - * is a "move" operation: the old entry storage at from will be freed without - * any reference-decrementing callback shortly. - */ -typedef void -(* JS_DLL_CALLBACK JSDHashMoveEntry)(JSDHashTable *table, - const JSDHashEntryHdr *from, - JSDHashEntryHdr *to); - -/* - * Clear the entry and drop any strong references it holds. This callback is - * invoked during a JS_DHASH_REMOVE operation (see below for operation codes), - * but only if the given key is found in the table. - */ -typedef void -(* JS_DLL_CALLBACK JSDHashClearEntry)(JSDHashTable *table, - JSDHashEntryHdr *entry); - -/* - * Called when a table (whether allocated dynamically by itself, or nested in - * a larger structure, or allocated on the stack) is finished. This callback - * allows table->ops-specific code to finalize table->data. - */ -typedef void -(* JS_DLL_CALLBACK JSDHashFinalize) (JSDHashTable *table); - -/* - * Initialize a new entry, apart from keyHash. This function is called when - * JS_DHashTableOperate's JS_DHASH_ADD case finds no existing entry for the - * given key, and must add a new one. At that point, entry->keyHash is not - * set yet, to avoid claiming the last free entry in a severely overloaded - * table. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSDHashInitEntry)(JSDHashTable *table, - JSDHashEntryHdr *entry, - const void *key); - -/* - * Finally, the "vtable" structure for JSDHashTable. The first eight hooks - * must be provided by implementations; they're called unconditionally by the - * generic jsdhash.c code. Hooks after these may be null. - * - * Summary of allocation-related hook usage with C++ placement new emphasis: - * allocTable Allocate raw bytes with malloc, no ctors run. - * freeTable Free raw bytes with free, no dtors run. - * initEntry Call placement new using default key-based ctor. - * Return JS_TRUE on success, JS_FALSE on error. - * moveEntry Call placement new using copy ctor, run dtor on old - * entry storage. - * clearEntry Run dtor on entry. - * finalize Stub unless table->data was initialized and needs to - * be finalized. - * - * Note the reason why initEntry is optional: the default hooks (stubs) clear - * entry storage: On successful JS_DHashTableOperate(tbl, key, JS_DHASH_ADD), - * the returned entry pointer addresses an entry struct whose keyHash member - * has been set non-zero, but all other entry members are still clear (null). - * JS_DHASH_ADD callers can test such members to see whether the entry was - * newly created by the JS_DHASH_ADD call that just succeeded. If placement - * new or similar initialization is required, define an initEntry hook. Of - * course, the clearEntry hook must zero or null appropriately. - * - * XXX assumes 0 is null for pointer types. - */ -struct JSDHashTableOps { - /* Mandatory hooks. All implementations must provide these. */ - JSDHashAllocTable allocTable; - JSDHashFreeTable freeTable; - JSDHashGetKey getKey; - JSDHashHashKey hashKey; - JSDHashMatchEntry matchEntry; - JSDHashMoveEntry moveEntry; - JSDHashClearEntry clearEntry; - JSDHashFinalize finalize; - - /* Optional hooks start here. If null, these are not called. */ - JSDHashInitEntry initEntry; -}; - -/* - * Default implementations for the above ops. - */ -extern JS_PUBLIC_API(void *) -JS_DHashAllocTable(JSDHashTable *table, uint32 nbytes); - -extern JS_PUBLIC_API(void) -JS_DHashFreeTable(JSDHashTable *table, void *ptr); - -extern JS_PUBLIC_API(JSDHashNumber) -JS_DHashStringKey(JSDHashTable *table, const void *key); - -/* A minimal entry contains a keyHash header and a void key pointer. */ -struct JSDHashEntryStub { - JSDHashEntryHdr hdr; - const void *key; -}; - -extern JS_PUBLIC_API(const void *) -JS_DHashGetKeyStub(JSDHashTable *table, JSDHashEntryHdr *entry); - -extern JS_PUBLIC_API(JSDHashNumber) -JS_DHashVoidPtrKeyStub(JSDHashTable *table, const void *key); - -extern JS_PUBLIC_API(JSBool) -JS_DHashMatchEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key); - -extern JS_PUBLIC_API(JSBool) -JS_DHashMatchStringKey(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key); - -extern JS_PUBLIC_API(void) -JS_DHashMoveEntryStub(JSDHashTable *table, - const JSDHashEntryHdr *from, - JSDHashEntryHdr *to); - -extern JS_PUBLIC_API(void) -JS_DHashClearEntryStub(JSDHashTable *table, JSDHashEntryHdr *entry); - -extern JS_PUBLIC_API(void) -JS_DHashFreeStringKey(JSDHashTable *table, JSDHashEntryHdr *entry); - -extern JS_PUBLIC_API(void) -JS_DHashFinalizeStub(JSDHashTable *table); - -/* - * If you use JSDHashEntryStub or a subclass of it as your entry struct, and - * if your entries move via memcpy and clear via memset(0), you can use these - * stub operations. - */ -extern JS_PUBLIC_API(const JSDHashTableOps *) -JS_DHashGetStubOps(void); - -/* - * Dynamically allocate a new JSDHashTable using malloc, initialize it using - * JS_DHashTableInit, and return its address. Return null on malloc failure. - * Note that the entry storage at table->entryStore will be allocated using - * the ops->allocTable callback. - */ -extern JS_PUBLIC_API(JSDHashTable *) -JS_NewDHashTable(const JSDHashTableOps *ops, void *data, uint32 entrySize, - uint32 capacity); - -/* - * Finalize table's data, free its entry storage (via table->ops->freeTable), - * and return the memory starting at table to the malloc heap. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableDestroy(JSDHashTable *table); - -/* - * Initialize table with ops, data, entrySize, and capacity. Capacity is a - * guess for the smallest table size at which the table will usually be less - * than 75% loaded (the table will grow or shrink as needed; capacity serves - * only to avoid inevitable early growth from JS_DHASH_MIN_SIZE). - */ -extern JS_PUBLIC_API(JSBool) -JS_DHashTableInit(JSDHashTable *table, const JSDHashTableOps *ops, void *data, - uint32 entrySize, uint32 capacity); - -/* - * Set maximum and minimum alpha for table. The defaults are 0.75 and .25. - * maxAlpha must be in [0.5, 0.9375] for the default JS_DHASH_MIN_SIZE; or if - * MinSize=JS_DHASH_MIN_SIZE <= 256, in [0.5, (float)(MinSize-1)/MinSize]; or - * else in [0.5, 255.0/256]. minAlpha must be in [0, maxAlpha / 2), so that - * we don't shrink on the very next remove after growing a table upon adding - * an entry that brings entryCount past maxAlpha * tableSize. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableSetAlphaBounds(JSDHashTable *table, - float maxAlpha, - float minAlpha); - -/* - * Call this macro with k, the number of pointer-sized words wasted per entry - * under chaining, to compute the minimum alpha at which double hashing still - * beats chaining. - */ -#define JS_DHASH_MIN_ALPHA(table, k) \ - ((float)((table)->entrySize / sizeof(void *) - 1) \ - / ((table)->entrySize / sizeof(void *) + (k))) - -/* - * Finalize table's data, free its entry storage using table->ops->freeTable, - * and leave its members unchanged from their last live values (which leaves - * pointers dangling). If you want to burn cycles clearing table, it's up to - * your code to call memset. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableFinish(JSDHashTable *table); - -/* - * To consolidate keyHash computation and table grow/shrink code, we use a - * single entry point for lookup, add, and remove operations. The operation - * codes are declared here, along with codes returned by JSDHashEnumerator - * functions, which control JS_DHashTableEnumerate's behavior. - */ -typedef enum JSDHashOperator { - JS_DHASH_LOOKUP = 0, /* lookup entry */ - JS_DHASH_ADD = 1, /* add entry */ - JS_DHASH_REMOVE = 2, /* remove entry, or enumerator says remove */ - JS_DHASH_NEXT = 0, /* enumerator says continue */ - JS_DHASH_STOP = 1 /* enumerator says stop */ -} JSDHashOperator; - -/* - * To lookup a key in table, call: - * - * entry = JS_DHashTableOperate(table, key, JS_DHASH_LOOKUP); - * - * If JS_DHASH_ENTRY_IS_BUSY(entry) is true, key was found and it identifies - * entry. If JS_DHASH_ENTRY_IS_FREE(entry) is true, key was not found. - * - * To add an entry identified by key to table, call: - * - * entry = JS_DHashTableOperate(table, key, JS_DHASH_ADD); - * - * If entry is null upon return, then either the table is severely overloaded, - * and memory can't be allocated for entry storage via table->ops->allocTable; - * Or if table->ops->initEntry is non-null, the table->ops->initEntry op may - * have returned false. - * - * Otherwise, entry->keyHash has been set so that JS_DHASH_ENTRY_IS_BUSY(entry) - * is true, and it is up to the caller to initialize the key and value parts - * of the entry sub-type, if they have not been set already (i.e. if entry was - * not already in the table, and if the optional initEntry hook was not used). - * - * To remove an entry identified by key from table, call: - * - * (void) JS_DHashTableOperate(table, key, JS_DHASH_REMOVE); - * - * If key's entry is found, it is cleared (via table->ops->clearEntry) and - * the entry is marked so that JS_DHASH_ENTRY_IS_FREE(entry). This operation - * returns null unconditionally; you should ignore its return value. - */ -extern JS_PUBLIC_API(JSDHashEntryHdr *) JS_DHASH_FASTCALL -JS_DHashTableOperate(JSDHashTable *table, const void *key, JSDHashOperator op); - -/* - * Remove an entry already accessed via LOOKUP or ADD. - * - * NB: this is a "raw" or low-level routine, intended to be used only where - * the inefficiency of a full JS_DHashTableOperate (which rehashes in order - * to find the entry given its key) is not tolerable. This function does not - * shrink the table if it is underloaded. It does not update stats #ifdef - * JS_DHASHMETER, either. - */ -extern JS_PUBLIC_API(void) -JS_DHashTableRawRemove(JSDHashTable *table, JSDHashEntryHdr *entry); - -/* - * Enumerate entries in table using etor: - * - * count = JS_DHashTableEnumerate(table, etor, arg); - * - * JS_DHashTableEnumerate calls etor like so: - * - * op = etor(table, entry, number, arg); - * - * where number is a zero-based ordinal assigned to live entries according to - * their order in table->entryStore. - * - * The return value, op, is treated as a set of flags. If op is JS_DHASH_NEXT, - * then continue enumerating. If op contains JS_DHASH_REMOVE, then clear (via - * table->ops->clearEntry) and free entry. Then we check whether op contains - * JS_DHASH_STOP; if so, stop enumerating and return the number of live entries - * that were enumerated so far. Return the total number of live entries when - * enumeration completes normally. - * - * If etor calls JS_DHashTableOperate on table with op != JS_DHASH_LOOKUP, it - * must return JS_DHASH_STOP; otherwise undefined behavior results. - * - * If any enumerator returns JS_DHASH_REMOVE, table->entryStore may be shrunk - * or compressed after enumeration, but before JS_DHashTableEnumerate returns. - * Such an enumerator therefore can't safely set aside entry pointers, but an - * enumerator that never returns JS_DHASH_REMOVE can set pointers to entries - * aside, e.g., to avoid copying live entries into an array of the entry type. - * Copying entry pointers is cheaper, and safe so long as the caller of such a - * "stable" Enumerate doesn't use the set-aside pointers after any call either - * to PL_DHashTableOperate, or to an "unstable" form of Enumerate, which might - * grow or shrink entryStore. - * - * If your enumerator wants to remove certain entries, but set aside pointers - * to other entries that it retains, it can use JS_DHashTableRawRemove on the - * entries to be removed, returning JS_DHASH_NEXT to skip them. Likewise, if - * you want to remove entries, but for some reason you do not want entryStore - * to be shrunk or compressed, you can call JS_DHashTableRawRemove safely on - * the entry being enumerated, rather than returning JS_DHASH_REMOVE. - */ -typedef JSDHashOperator -(* JS_DLL_CALLBACK JSDHashEnumerator)(JSDHashTable *table, JSDHashEntryHdr *hdr, - uint32 number, void *arg); - -extern JS_PUBLIC_API(uint32) -JS_DHashTableEnumerate(JSDHashTable *table, JSDHashEnumerator etor, void *arg); - -#ifdef JS_DHASHMETER -#include - -extern JS_PUBLIC_API(void) -JS_DHashTableDumpMeter(JSDHashTable *table, JSDHashEnumerator dump, FILE *fp); -#endif - -JS_END_EXTERN_C - -#endif /* jsdhash_h___ */ diff --git a/spidermonkey/libjs/jsdtoa.c b/spidermonkey/libjs/jsdtoa.c deleted file mode 100644 index 5b0b09f..0000000 --- a/spidermonkey/libjs/jsdtoa.c +++ /dev/null @@ -1,3132 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * Portable double to alphanumeric string and back converters. - */ -#include "jsstddef.h" -#include "jslibmath.h" -#include "jstypes.h" -#include "jsdtoa.h" -#include "jsprf.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jspubtd.h" -#include "jsnum.h" - -#ifdef JS_THREADSAFE -#include "prlock.h" -#endif - -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -/* Please send bug reports to - David M. Gay - Bell Laboratories, Room 2C-463 - 600 Mountain Avenue - Murray Hill, NJ 07974-0636 - U.S.A. - dmg@bell-labs.com - */ - -/* On a machine with IEEE extended-precision registers, it is - * necessary to specify double-precision (53-bit) rounding precision - * before invoking strtod or dtoa. If the machine uses (the equivalent - * of) Intel 80x87 arithmetic, the call - * _control87(PC_53, MCW_PC); - * does this with many compilers. Whether this or another call is - * appropriate depends on the compiler; for this to work, it may be - * necessary to #include "float.h" or another system-dependent header - * file. - */ - -/* strtod for IEEE-arithmetic machines. - * - * This strtod returns a nearest machine number to the input decimal - * string (or sets err to JS_DTOA_ERANGE or JS_DTOA_ENOMEM). With IEEE - * arithmetic, ties are broken by the IEEE round-even rule. Otherwise - * ties are broken by biased rounding (add half and chop). - * - * Inspired loosely by William D. Clinger's paper "How to Read Floating - * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * - * 1. We only require IEEE double-precision - * arithmetic (not IEEE double-extended). - * 2. We get by with floating-point arithmetic in a case that - * Clinger missed -- when we're computing d * 10^n - * for a small integer d and the integer n is not too - * much larger than 22 (the maximum integer k for which - * we can represent 10^k exactly), we may be able to - * compute (d*10^k) * 10^(e-k) with just one roundoff. - * 3. Rather than a bit-at-a-time adjustment of the binary - * result in the hard case, we use floating-point - * arithmetic to determine the adjustment to within - * one bit; only in really hard cases do we need to - * compute a second residual. - * 4. Because of 3., we don't need a large table of powers of 10 - * for ten-to-e (just some small tables, e.g. of 10^k - * for 0 <= k <= 22). - */ - -/* - * #define IEEE_8087 for IEEE-arithmetic machines where the least - * significant byte has the lowest address. - * #define IEEE_MC68k for IEEE-arithmetic machines where the most - * significant byte has the lowest address. - * #define Long int on machines with 32-bit ints and 64-bit longs. - * #define Sudden_Underflow for IEEE-format machines without gradual - * underflow (i.e., that flush to zero on underflow). - * #define No_leftright to omit left-right logic in fast floating-point - * computation of js_dtoa. - * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. - * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines - * that use extended-precision instructions to compute rounded - * products and quotients) with IBM. - * #define ROUND_BIASED for IEEE-format with biased rounding. - * #define Inaccurate_Divide for IEEE-format with correctly rounded - * products but inaccurate quotients, e.g., for Intel i860. - * #define JS_HAVE_LONG_LONG on machines that have a "long long" - * integer type (of >= 64 bits). If long long is available and the name is - * something other than "long long", #define Llong to be the name, - * and if "unsigned Llong" does not work as an unsigned version of - * Llong, #define #ULLong to be the corresponding unsigned type. - * #define Bad_float_h if your system lacks a float.h or if it does not - * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, - * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. - * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) - * if memory is available and otherwise does something you deem - * appropriate. If MALLOC is undefined, malloc will be invoked - * directly -- and assumed always to succeed. - * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making - * memory allocations from a private pool of memory when possible. - * When used, the private pool is PRIVATE_MEM bytes long: 2000 bytes, - * unless #defined to be a different length. This default length - * suffices to get rid of MALLOC calls except for unusual cases, - * such as decimal-to-binary conversion of a very long string of - * digits. - * #define INFNAN_CHECK on IEEE systems to cause strtod to check for - * Infinity and NaN (case insensitively). On some systems (e.g., - * some HP systems), it may be necessary to #define NAN_WORD0 - * appropriately -- to the most significant word of a quiet NaN. - * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) - * #define MULTIPLE_THREADS if the system offers preemptively scheduled - * multiple threads. In this case, you must provide (or suitably - * #define) two locks, acquired by ACQUIRE_DTOA_LOCK() and released - * by RELEASE_DTOA_LOCK(). (The second lock, accessed - * in pow5mult, ensures lazy evaluation of only one copy of high - * powers of 5; omitting this lock would introduce a small - * probability of wasting memory, but would otherwise be harmless.) - * You must also invoke freedtoa(s) to free the value s returned by - * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. - * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that - * avoids underflows on inputs whose result does not underflow. - */ -#ifdef IS_LITTLE_ENDIAN -#define IEEE_8087 -#else -#define IEEE_MC68k -#endif - -#ifndef Long -#define Long int32 -#endif - -#ifndef ULong -#define ULong uint32 -#endif - -#define Bug(errorMessageString) JS_ASSERT(!errorMessageString) - -#include "stdlib.h" -#include "string.h" - -#ifdef MALLOC -extern void *MALLOC(size_t); -#else -#define MALLOC malloc -#endif - -#define Omit_Private_Memory -/* Private memory currently doesn't work with JS_THREADSAFE */ -#ifndef Omit_Private_Memory -#ifndef PRIVATE_MEM -#define PRIVATE_MEM 2000 -#endif -#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) -static double private_mem[PRIVATE_mem], *pmem_next = private_mem; -#endif - -#ifdef Bad_float_h -#undef __STDC__ - -#define DBL_DIG 15 -#define DBL_MAX_10_EXP 308 -#define DBL_MAX_EXP 1024 -#define FLT_RADIX 2 -#define FLT_ROUNDS 1 -#define DBL_MAX 1.7976931348623157e+308 - - - -#ifndef LONG_MAX -#define LONG_MAX 2147483647 -#endif - -#else /* ifndef Bad_float_h */ -#include "float.h" -#endif /* Bad_float_h */ - -#ifndef __MATH_H__ -#include "math.h" -#endif - -#ifndef CONST -#define CONST const -#endif - -#if defined(IEEE_8087) + defined(IEEE_MC68k) != 1 -Exactly one of IEEE_8087 or IEEE_MC68k should be defined. -#endif - -#define word0(x) JSDOUBLE_HI32(x) -#define set_word0(x, y) JSDOUBLE_SET_HI32(x, y) -#define word1(x) JSDOUBLE_LO32(x) -#define set_word1(x, y) JSDOUBLE_SET_LO32(x, y) - -#define Storeinc(a,b,c) (*(a)++ = (b) << 16 | (c) & 0xffff) - -/* #define P DBL_MANT_DIG */ -/* Ten_pmax = floor(P*log(2)/log(5)) */ -/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ -/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ -/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ - -#define Exp_shift 20 -#define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 -#define P 53 -#define Bias 1023 -#define Emin (-1022) -#define Exp_1 0x3ff00000 -#define Exp_11 0x3ff00000 -#define Ebits 11 -#define Frac_mask 0xfffff -#define Frac_mask1 0xfffff -#define Ten_pmax 22 -#define Bletch 0x10 -#define Bndry_mask 0xfffff -#define Bndry_mask1 0xfffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 1 -#define Tiny0 0 -#define Tiny1 1 -#define Quick_max 14 -#define Int_max 14 -#define Infinite(x) (word0(x) == 0x7ff00000) /* sufficient test for here */ -#ifndef NO_IEEE_Scale -#define Avoid_Underflow -#endif - - - -#ifdef RND_PRODQUOT -#define rounded_product(a,b) a = rnd_prod(a, b) -#define rounded_quotient(a,b) a = rnd_quot(a, b) -extern double rnd_prod(double, double), rnd_quot(double, double); -#else -#define rounded_product(a,b) a *= b -#define rounded_quotient(a,b) a /= b -#endif - -#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) -#define Big1 0xffffffff - -#ifndef JS_HAVE_LONG_LONG -#undef ULLong -#else /* long long available */ -#ifndef Llong -#define Llong JSInt64 -#endif -#ifndef ULLong -#define ULLong JSUint64 -#endif -#endif /* JS_HAVE_LONG_LONG */ - -#ifdef JS_THREADSAFE -#define MULTIPLE_THREADS -static PRLock *freelist_lock; -#define ACQUIRE_DTOA_LOCK() \ - JS_BEGIN_MACRO \ - if (!initialized) \ - InitDtoa(); \ - PR_Lock(freelist_lock); \ - JS_END_MACRO -#define RELEASE_DTOA_LOCK() PR_Unlock(freelist_lock) -#else -#undef MULTIPLE_THREADS -#define ACQUIRE_DTOA_LOCK() /*nothing*/ -#define RELEASE_DTOA_LOCK() /*nothing*/ -#endif - -#define Kmax 15 - -struct Bigint { - struct Bigint *next; /* Free list link */ - int32 k; /* lg2(maxwds) */ - int32 maxwds; /* Number of words allocated for x */ - int32 sign; /* Zero if positive, 1 if negative. Ignored by most Bigint routines! */ - int32 wds; /* Actual number of words. If value is nonzero, the most significant word must be nonzero. */ - ULong x[1]; /* wds words of number in little endian order */ -}; - -#ifdef ENABLE_OOM_TESTING -/* Out-of-memory testing. Use a good testcase (over and over) and then use - * these routines to cause a memory failure on every possible Balloc allocation, - * to make sure that all out-of-memory paths can be followed. See bug 14044. - */ - -static int allocationNum; /* which allocation is next? */ -static int desiredFailure; /* which allocation should fail? */ - -/** - * js_BigintTestingReset - * - * Call at the beginning of a test run to set the allocation failure position. - * (Set to 0 to just have the engine count allocations without failing.) - */ -JS_PUBLIC_API(void) -js_BigintTestingReset(int newFailure) -{ - allocationNum = 0; - desiredFailure = newFailure; -} - -/** - * js_BigintTestingWhere - * - * Report the current allocation position. This is really only useful when you - * want to learn how many allocations a test run has. - */ -JS_PUBLIC_API(int) -js_BigintTestingWhere() -{ - return allocationNum; -} - - -/* - * So here's what you do: Set up a fantastic test case that exercises the - * elements of the code you wish. Set the failure point at 0 and run the test, - * then get the allocation position. This number is the number of allocations - * your test makes. Now loop from 1 to that number, setting the failure point - * at each loop count, and run the test over and over, causing failures at each - * step. Any memory failure *should* cause a Out-Of-Memory exception; if it - * doesn't, then there's still an error here. - */ -#endif - -typedef struct Bigint Bigint; - -static Bigint *freelist[Kmax+1]; - -/* - * Allocate a Bigint with 2^k words. - * This is not threadsafe. The caller must use thread locks - */ -static Bigint *Balloc(int32 k) -{ - int32 x; - Bigint *rv; -#ifndef Omit_Private_Memory - uint32 len; -#endif - -#ifdef ENABLE_OOM_TESTING - if (++allocationNum == desiredFailure) { - printf("Forced Failing Allocation number %d\n", allocationNum); - return NULL; - } -#endif - - if ((rv = freelist[k]) != NULL) - freelist[k] = rv->next; - if (rv == NULL) { - x = 1 << k; -#ifdef Omit_Private_Memory - rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); -#else - len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) - /sizeof(double); - if (pmem_next - private_mem + len <= PRIVATE_mem) { - rv = (Bigint*)pmem_next; - pmem_next += len; - } - else - rv = (Bigint*)MALLOC(len*sizeof(double)); -#endif - if (!rv) - return NULL; - rv->k = k; - rv->maxwds = x; - } - rv->sign = rv->wds = 0; - return rv; -} - -static void Bfree(Bigint *v) -{ - if (v) { - v->next = freelist[v->k]; - freelist[v->k] = v; - } -} - -#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ - y->wds*sizeof(Long) + 2*sizeof(int32)) - -/* Return b*m + a. Deallocate the old b. Both a and m must be between 0 and - * 65535 inclusive. NOTE: old b is deallocated on memory failure. - */ -static Bigint *multadd(Bigint *b, int32 m, int32 a) -{ - int32 i, wds; -#ifdef ULLong - ULong *x; - ULLong carry, y; -#else - ULong carry, *x, y; - ULong xi, z; -#endif - Bigint *b1; - -#ifdef ENABLE_OOM_TESTING - if (++allocationNum == desiredFailure) { - /* Faux allocation, because I'm not getting all of the failure paths - * without it. - */ - printf("Forced Failing Allocation number %d\n", allocationNum); - Bfree(b); - return NULL; - } -#endif - - wds = b->wds; - x = b->x; - i = 0; - carry = a; - do { -#ifdef ULLong - y = *x * (ULLong)m + carry; - carry = y >> 32; - *x++ = (ULong)(y & 0xffffffffUL); -#else - xi = *x; - y = (xi & 0xffff) * m + carry; - z = (xi >> 16) * m + (y >> 16); - carry = z >> 16; - *x++ = (z << 16) + (y & 0xffff); -#endif - } - while(++i < wds); - if (carry) { - if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); - if (!b1) { - Bfree(b); - return NULL; - } - Bcopy(b1, b); - Bfree(b); - b = b1; - } - b->x[wds++] = (ULong)carry; - b->wds = wds; - } - return b; -} - -static Bigint *s2b(CONST char *s, int32 nd0, int32 nd, ULong y9) -{ - Bigint *b; - int32 i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; - b = Balloc(k); - if (!b) - return NULL; - b->x[0] = y9; - b->wds = 1; - - i = 9; - if (9 < nd0) { - s += 9; - do { - b = multadd(b, 10, *s++ - '0'); - if (!b) - return NULL; - } while(++i < nd0); - s++; - } - else - s += 10; - for(; i < nd; i++) { - b = multadd(b, 10, *s++ - '0'); - if (!b) - return NULL; - } - return b; -} - - -/* Return the number (0 through 32) of most significant zero bits in x. */ -static int32 hi0bits(register ULong x) -{ - register int32 k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; -} - - -/* Return the number (0 through 32) of least significant zero bits in y. - * Also shift y to the right past these 0 through 32 zeros so that y's - * least significant bit will be set unless y was originally zero. */ -static int32 lo0bits(ULong *y) -{ - register int32 k; - register ULong x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x & 1) - return 32; - } - *y = x; - return k; -} - -/* Return a new Bigint with the given integer value, which must be nonnegative. */ -static Bigint *i2b(int32 i) -{ - Bigint *b; - - b = Balloc(1); - if (!b) - return NULL; - b->x[0] = i; - b->wds = 1; - return b; -} - -/* Return a newly allocated product of a and b. */ -static Bigint *mult(CONST Bigint *a, CONST Bigint *b) -{ - CONST Bigint *t; - Bigint *c; - int32 k, wa, wb, wc; - ULong y; - ULong *xc, *xc0, *xce; - CONST ULong *x, *xa, *xae, *xb, *xbe; -#ifdef ULLong - ULLong carry, z; -#else - ULong carry, z; - ULong z2; -#endif - - if (a->wds < b->wds) { - t = a; - a = b; - b = t; - } - k = a->k; - wa = a->wds; - wb = b->wds; - wc = wa + wb; - if (wc > a->maxwds) - k++; - c = Balloc(k); - if (!c) - return NULL; - for(xc = c->x, xce = xc + wc; xc < xce; xc++) - *xc = 0; - xa = a->x; - xae = xa + wa; - xb = b->x; - xbe = xb + wb; - xc0 = c->x; -#ifdef ULLong - for(; xb < xbe; xc0++) { - if ((y = *xb++) != 0) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * (ULLong)y + *xc + carry; - carry = z >> 32; - *xc++ = (ULong)(z & 0xffffffffUL); - } - while(x < xae); - *xc = (ULong)carry; - } - } -#else - for(; xb < xbe; xb++, xc0++) { - if ((y = *xb & 0xffff) != 0) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; - } - if ((y = *xb >> 16) != 0) { - x = xa; - xc = xc0; - carry = 0; - z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - Storeinc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } - while(x < xae); - *xc = z2; - } - } -#endif - for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; - c->wds = wc; - return c; -} - -/* - * 'p5s' points to a linked list of Bigints that are powers of 5. - * This list grows on demand, and it can only grow: it won't change - * in any other way. So if we read 'p5s' or the 'next' field of - * some Bigint on the list, and it is not NULL, we know it won't - * change to NULL or some other value. Only when the value of - * 'p5s' or 'next' is NULL do we need to acquire the lock and add - * a new Bigint to the list. - */ - -static Bigint *p5s; - -#ifdef JS_THREADSAFE -static PRLock *p5s_lock; -#endif - -/* Return b * 5^k. Deallocate the old b. k must be nonnegative. */ -/* NOTE: old b is deallocated on memory failure. */ -static Bigint *pow5mult(Bigint *b, int32 k) -{ - Bigint *b1, *p5, *p51; - int32 i; - static CONST int32 p05[3] = { 5, 25, 125 }; - - if ((i = k & 3) != 0) { - b = multadd(b, p05[i-1], 0); - if (!b) - return NULL; - } - - if (!(k >>= 2)) - return b; - if (!(p5 = p5s)) { -#ifdef JS_THREADSAFE - /* - * We take great care to not call i2b() and Bfree() - * while holding the lock. - */ - Bigint *wasted_effort = NULL; - p5 = i2b(625); - if (!p5) { - Bfree(b); - return NULL; - } - /* lock and check again */ - PR_Lock(p5s_lock); - if (!p5s) { - /* first time */ - p5s = p5; - p5->next = 0; - } else { - /* some other thread just beat us */ - wasted_effort = p5; - p5 = p5s; - } - PR_Unlock(p5s_lock); - if (wasted_effort) { - Bfree(wasted_effort); - } -#else - /* first time */ - p5 = p5s = i2b(625); - if (!p5) { - Bfree(b); - return NULL; - } - p5->next = 0; -#endif - } - for(;;) { - if (k & 1) { - b1 = mult(b, p5); - Bfree(b); - if (!b1) - return NULL; - b = b1; - } - if (!(k >>= 1)) - break; - if (!(p51 = p5->next)) { -#ifdef JS_THREADSAFE - Bigint *wasted_effort = NULL; - p51 = mult(p5, p5); - if (!p51) { - Bfree(b); - return NULL; - } - PR_Lock(p5s_lock); - if (!p5->next) { - p5->next = p51; - p51->next = 0; - } else { - wasted_effort = p51; - p51 = p5->next; - } - PR_Unlock(p5s_lock); - if (wasted_effort) { - Bfree(wasted_effort); - } -#else - p51 = mult(p5,p5); - if (!p51) { - Bfree(b); - return NULL; - } - p51->next = 0; - p5->next = p51; -#endif - } - p5 = p51; - } - return b; -} - -/* Return b * 2^k. Deallocate the old b. k must be nonnegative. - * NOTE: on memory failure, old b is deallocated. */ -static Bigint *lshift(Bigint *b, int32 k) -{ - int32 i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; - - n = k >> 5; - k1 = b->k; - n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) - k1++; - b1 = Balloc(k1); - if (!b1) - goto done; - x1 = b1->x; - for(i = 0; i < n; i++) - *x1++ = 0; - x = b->x; - xe = x + b->wds; - if (k &= 0x1f) { - k1 = 32 - k; - z = 0; - do { - *x1++ = *x << k | z; - z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z) != 0) - ++n1; - } - else do - *x1++ = *x++; - while(x < xe); - b1->wds = n1 - 1; -done: - Bfree(b); - return b1; -} - -/* Return -1, 0, or 1 depending on whether ab, respectively. */ -static int32 cmp(Bigint *a, Bigint *b) -{ - ULong *xa, *xa0, *xb, *xb0; - int32 i, j; - - i = a->wds; - j = b->wds; -#ifdef DEBUG - if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); - if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); -#endif - if (i -= j) - return i; - xa0 = a->x; - xa = xa0 + j; - xb0 = b->x; - xb = xb0 + j; - for(;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; -} - -static Bigint *diff(Bigint *a, Bigint *b) -{ - Bigint *c; - int32 i, wa, wb; - ULong *xa, *xae, *xb, *xbe, *xc; -#ifdef ULLong - ULLong borrow, y; -#else - ULong borrow, y; - ULong z; -#endif - - i = cmp(a,b); - if (!i) { - c = Balloc(0); - if (!c) - return NULL; - c->wds = 1; - c->x[0] = 0; - return c; - } - if (i < 0) { - c = a; - a = b; - b = c; - i = 1; - } - else - i = 0; - c = Balloc(a->k); - if (!c) - return NULL; - c->sign = i; - wa = a->wds; - xa = a->x; - xae = xa + wa; - wb = b->wds; - xb = b->x; - xbe = xb + wb; - xc = c->x; - borrow = 0; -#ifdef ULLong - do { - y = (ULLong)*xa++ - *xb++ - borrow; - borrow = y >> 32 & 1UL; - *xc++ = (ULong)(y & 0xffffffffUL); - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = y >> 32 & 1UL; - *xc++ = (ULong)(y & 0xffffffffUL); - } -#else - do { - y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } - while(xb < xbe); - while(xa < xae) { - y = (*xa & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } -#endif - while(!*--xc) - wa--; - c->wds = wa; - return c; -} - -/* Return the absolute difference between x and the adjacent greater-magnitude double number (ignoring exponent overflows). */ -static double ulp(double x) -{ - register Long L; - double a = 0; - - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; -#ifndef Sudden_Underflow - if (L > 0) { -#endif - set_word0(a, L); - set_word1(a, 0); -#ifndef Sudden_Underflow - } - else { - L = -L >> Exp_shift; - if (L < Exp_shift) { - set_word0(a, 0x80000 >> L); - set_word1(a, 0); - } - else { - set_word0(a, 0); - L -= Exp_shift; - set_word1(a, L >= 31 ? 1 : 1 << (31 - L)); - } - } -#endif - return a; -} - - -static double b2d(Bigint *a, int32 *e) -{ - ULong *xa, *xa0, w, y, z; - int32 k; - double d = 0; -#define d0 word0(d) -#define d1 word1(d) -#define set_d0(x) set_word0(d, x) -#define set_d1(x) set_word1(d, x) - - xa0 = a->x; - xa = xa0 + a->wds; - y = *--xa; -#ifdef DEBUG - if (!y) Bug("zero y in b2d"); -#endif - k = hi0bits(y); - *e = 32 - k; - if (k < Ebits) { - set_d0(Exp_1 | y >> (Ebits - k)); - w = xa > xa0 ? *--xa : 0; - set_d1(y << (32-Ebits + k) | w >> (Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - if (k -= Ebits) { - set_d0(Exp_1 | y << k | z >> (32 - k)); - y = xa > xa0 ? *--xa : 0; - set_d1(z << k | y >> (32 - k)); - } - else { - set_d0(Exp_1 | y); - set_d1(z); - } - ret_d: -#undef d0 -#undef d1 -#undef set_d0 -#undef set_d1 - return d; -} - - -/* Convert d into the form b*2^e, where b is an odd integer. b is the returned - * Bigint and e is the returned binary exponent. Return the number of significant - * bits in b in bits. d must be finite and nonzero. */ -static Bigint *d2b(double d, int32 *e, int32 *bits) -{ - Bigint *b; - int32 de, i, k; - ULong *x, y, z; -#define d0 word0(d) -#define d1 word1(d) -#define set_d0(x) set_word0(d, x) -#define set_d1(x) set_word1(d, x) - - b = Balloc(1); - if (!b) - return NULL; - x = b->x; - - z = d0 & Frac_mask; - set_d0(d0 & 0x7fffffff); /* clear sign bit, which we ignore */ -#ifdef Sudden_Underflow - de = (int32)(d0 >> Exp_shift); - z |= Exp_msk11; -#else - if ((de = (int32)(d0 >> Exp_shift)) != 0) - z |= Exp_msk1; -#endif - if ((y = d1) != 0) { - if ((k = lo0bits(&y)) != 0) { - x[0] = y | z << (32 - k); - z >>= k; - } - else - x[0] = y; - i = b->wds = (x[1] = z) ? 2 : 1; - } - else { - JS_ASSERT(z); - k = lo0bits(&z); - x[0] = z; - i = b->wds = 1; - k += 32; - } -#ifndef Sudden_Underflow - if (de) { -#endif - *e = de - Bias - (P-1) + k; - *bits = P - k; -#ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; - *bits = 32*i - hi0bits(x[i-1]); - } -#endif - return b; -} -#undef d0 -#undef d1 -#undef set_d0 -#undef set_d1 - - -static double ratio(Bigint *a, Bigint *b) -{ - double da, db; - int32 k, ka, kb; - - da = b2d(a, &ka); - db = b2d(b, &kb); - k = ka - kb + 32*(a->wds - b->wds); - if (k > 0) - set_word0(da, word0(da) + k*Exp_msk1); - else { - k = -k; - set_word0(db, word0(db) + k*Exp_msk1); - } - return da / db; -} - -static CONST double -tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -}; - -static CONST double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, -#ifdef Avoid_Underflow - 9007199254740992.e-256 -#else - 1e-256 -#endif - }; -/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ -/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ -#define Scale_Bit 0x10 -#define n_bigtens 5 - - -#ifdef INFNAN_CHECK - -#ifndef NAN_WORD0 -#define NAN_WORD0 0x7ff80000 -#endif - -#ifndef NAN_WORD1 -#define NAN_WORD1 0 -#endif - -static int match(CONST char **sp, char *t) -{ - int c, d; - CONST char *s = *sp; - - while(d = *t++) { - if ((c = *++s) >= 'A' && c <= 'Z') - c += 'a' - 'A'; - if (c != d) - return 0; - } - *sp = s + 1; - return 1; - } -#endif /* INFNAN_CHECK */ - - -#ifdef JS_THREADSAFE -static JSBool initialized = JS_FALSE; - -/* hacked replica of nspr _PR_InitDtoa */ -static void InitDtoa(void) -{ - freelist_lock = PR_NewLock(); - p5s_lock = PR_NewLock(); - initialized = JS_TRUE; -} -#endif - -void js_FinishDtoa(void) -{ - int count; - Bigint *temp; - -#ifdef JS_THREADSAFE - if (initialized == JS_TRUE) { - PR_DestroyLock(freelist_lock); - PR_DestroyLock(p5s_lock); - initialized = JS_FALSE; - } -#endif - - /* clear down the freelist array and p5s */ - - /* static Bigint *freelist[Kmax+1]; */ - for (count = 0; count <= Kmax; count++) { - Bigint **listp = &freelist[count]; - while ((temp = *listp) != NULL) { - *listp = temp->next; - free(temp); - } - freelist[count] = NULL; - } - - /* static Bigint *p5s; */ - while (p5s) { - temp = p5s; - p5s = p5s->next; - free(temp); - } -} - -/* nspr2 watcom bug ifdef omitted */ - -JS_FRIEND_API(double) -JS_strtod(CONST char *s00, char **se, int *err) -{ - int32 scale; - int32 bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, - e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; - CONST char *s, *s0, *s1; - double aadj, aadj1, adj, rv, rv0; - Long L; - ULong y, z; - Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; - - *err = 0; - - bb = bd = bs = delta = NULL; - sign = nz0 = nz = 0; - rv = 0.; - - /* Locking for Balloc's shared buffers that will be used in this block */ - ACQUIRE_DTOA_LOCK(); - - for(s = s00;;s++) switch(*s) { - case '-': - sign = 1; - /* no break */ - case '+': - if (*++s) - goto break2; - /* no break */ - case 0: - s = s00; - goto ret; - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - case ' ': - continue; - default: - goto break2; - } -break2: - - if (*s == '0') { - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; - } - s0 = s; - y = z = 0; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - if (nd < 9) - y = 10*y + c - '0'; - else if (nd < 16) - z = 10*z + c - '0'; - nd0 = nd; - if (c == '.') { - c = *++s; - if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - for(i = 1; i < nz; i++) - if (nd++ < 9) - y *= 10; - else if (nd <= DBL_DIG + 1) - z *= 10; - if (nd++ < 9) - y = 10*y + c; - else if (nd <= DBL_DIG + 1) - z = 10*z + c; - nz = 0; - } - } - } -dig_done: - e = 0; - if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - s = s00; - goto ret; - } - s00 = s; - esign = 0; - switch(c = *++s) { - case '-': - esign = 1; - case '+': - c = *++s; - } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = 19999; /* safe for 16 bit ints */ - else - e = (int32)L; - if (esign) - e = -e; - } - else - e = 0; - } - else - s = s00; - } - if (!nd) { - if (!nz && !nz0) { -#ifdef INFNAN_CHECK - /* Check for Nan and Infinity */ - switch(c) { - case 'i': - case 'I': - if (match(&s,"nfinity")) { - set_word0(rv, 0x7ff00000); - set_word1(rv, 0); - goto ret; - } - break; - case 'n': - case 'N': - if (match(&s, "an")) { - set_word0(rv, NAN_WORD0); - set_word1(rv, NAN_WORD1); - goto ret; - } - } -#endif /* INFNAN_CHECK */ - s = s00; - } - goto ret; - } - e1 = e -= nf; - - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - if (!nd0) - nd0 = nd; - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - rv = y; - if (k > 9) - rv = tens[k - 9] * rv + z; - bd0 = 0; - if (nd <= DBL_DIG -#ifndef RND_PRODQUOT - && FLT_ROUNDS == 1 -#endif - ) { - if (!e) - goto ret; - if (e > 0) { - if (e <= Ten_pmax) { - /* rv = */ rounded_product(rv, tens[e]); - goto ret; - } - i = DBL_DIG - nd; - if (e <= Ten_pmax + i) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ - e -= i; - rv *= tens[i]; - /* rv = */ rounded_product(rv, tens[e]); - goto ret; - } - } -#ifndef Inaccurate_Divide - else if (e >= -Ten_pmax) { - /* rv = */ rounded_quotient(rv, tens[-e]); - goto ret; - } -#endif - } - e1 += nd - k; - - scale = 0; - - /* Get starting approximation = rv * 10**e1 */ - - if (e1 > 0) { - if ((i = e1 & 15) != 0) - rv *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: - *err = JS_DTOA_ERANGE; -#ifdef __STDC__ - rv = HUGE_VAL; -#else - /* Can't trust HUGE_VAL */ - set_word0(rv, Exp_mask); - set_word1(rv, 0); -#endif - if (bd0) - goto retfree; - goto ret; - } - e1 >>= 4; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - rv *= bigtens[j]; - /* The last multiplication could overflow. */ - set_word0(rv, word0(rv) - P*Exp_msk1); - rv *= bigtens[j]; - if ((z = word0(rv) & Exp_mask) > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - set_word0(rv, Big0); - set_word1(rv, Big1); - } - else - set_word0(rv, word0(rv) + P*Exp_msk1); - } - } - else if (e1 < 0) { - e1 = -e1; - if ((i = e1 & 15) != 0) - rv /= tens[i]; - if (e1 &= ~15) { - e1 >>= 4; - if (e1 >= 1 << n_bigtens) - goto undfl; -#ifdef Avoid_Underflow - if (e1 & Scale_Bit) - scale = P; - for(j = 0; e1 > 0; j++, e1 >>= 1) - if (e1 & 1) - rv *= tinytens[j]; - if (scale && (j = P + 1 - ((word0(rv) & Exp_mask) - >> Exp_shift)) > 0) { - /* scaled rv is denormal; zap j low bits */ - if (j >= 32) { - set_word1(rv, 0); - set_word0(rv, word0(rv) & (0xffffffff << (j-32))); - if (!word0(rv)) - set_word0(rv, 1); - } - else - set_word1(rv, word1(rv) & (0xffffffff << j)); - } -#else - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - rv *= tinytens[j]; - /* The last multiplication could underflow. */ - rv0 = rv; - rv *= tinytens[j]; - if (!rv) { - rv = 2.*rv0; - rv *= tinytens[j]; -#endif - if (!rv) { - undfl: - rv = 0.; - *err = JS_DTOA_ERANGE; - if (bd0) - goto retfree; - goto ret; - } -#ifndef Avoid_Underflow - set_word0(rv, Tiny0); - set_word1(rv, Tiny1); - /* The refinement below will clean - * this approximation up. - */ - } -#endif - } - } - - /* Now the hard part -- adjusting rv to the correct value.*/ - - /* Put digits into bd: true value = bd * 10^e */ - - bd0 = s2b(s0, nd0, nd, y); - if (!bd0) - goto nomem; - - for(;;) { - bd = Balloc(bd0->k); - if (!bd) - goto nomem; - Bcopy(bd, bd0); - bb = d2b(rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ - if (!bb) - goto nomem; - bs = i2b(1); - if (!bs) - goto nomem; - - if (e >= 0) { - bb2 = bb5 = 0; - bd2 = bd5 = e; - } - else { - bb2 = bb5 = -e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; -#ifdef Sudden_Underflow - j = P + 1 - bbbits; -#else -#ifdef Avoid_Underflow - j = bbe - scale; -#else - j = bbe; -#endif - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#endif - bb2 += j; - bd2 += j; -#ifdef Avoid_Underflow - bd2 += scale; -#endif - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - if (!bs) - goto nomem; - bb1 = mult(bs, bb); - if (!bb1) - goto nomem; - Bfree(bb); - bb = bb1; - } - if (bb2 > 0) { - bb = lshift(bb, bb2); - if (!bb) - goto nomem; - } - if (bd5 > 0) { - bd = pow5mult(bd, bd5); - if (!bd) - goto nomem; - } - if (bd2 > 0) { - bd = lshift(bd, bd2); - if (!bd) - goto nomem; - } - if (bs2 > 0) { - bs = lshift(bs, bs2); - if (!bs) - goto nomem; - } - delta = diff(bb, bd); - if (!delta) - goto nomem; - dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask -#ifdef Avoid_Underflow - || (word0(rv) & Exp_mask) <= Exp_msk1 + P*Exp_msk1 -#else - || (word0(rv) & Exp_mask) <= Exp_msk1 -#endif - ) { -#ifdef Avoid_Underflow - if (!delta->x[0] && delta->wds == 1) - dsign = 2; -#endif - break; - } - delta = lshift(delta,Log2P); - if (!delta) - goto nomem; - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (dsign) { - if ((word0(rv) & Bndry_mask1) == Bndry_mask1 - && word1(rv) == 0xffffffff) { - /*boundary case -- increment exponent*/ - set_word0(rv, (word0(rv) & Exp_mask) + Exp_msk1); - set_word1(rv, 0); -#ifdef Avoid_Underflow - dsign = 0; -#endif - break; - } - } - else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { -#ifdef Avoid_Underflow - dsign = 2; -#endif - drop_down: - /* boundary case -- decrement exponent */ -#ifdef Sudden_Underflow - L = word0(rv) & Exp_mask; - if (L <= Exp_msk1) - goto undfl; - L -= Exp_msk1; -#else - L = (word0(rv) & Exp_mask) - Exp_msk1; -#endif - set_word0(rv, L | Bndry_mask1); - set_word1(rv, 0xffffffff); - break; - } -#ifndef ROUND_BIASED - if (!(word1(rv) & LSB)) - break; -#endif - if (dsign) - rv += ulp(rv); -#ifndef ROUND_BIASED - else { - rv -= ulp(rv); -#ifndef Sudden_Underflow - if (!rv) - goto undfl; -#endif - } -#ifdef Avoid_Underflow - dsign = 1 - dsign; -#endif -#endif - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (dsign) - aadj = aadj1 = 1.; - else if (word1(rv) || word0(rv) & Bndry_mask) { -#ifndef Sudden_Underflow - if (word1(rv) == Tiny1 && !word0(rv)) - goto undfl; -#endif - aadj = 1.; - aadj1 = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ - - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - aadj1 = -aadj; - } - } - else { - aadj *= 0.5; - aadj1 = dsign ? aadj : -aadj; -#ifdef Check_FLT_ROUNDS - switch(FLT_ROUNDS) { - case 2: /* towards +infinity */ - aadj1 -= 0.5; - break; - case 0: /* towards 0 */ - case 3: /* towards -infinity */ - aadj1 += 0.5; - } -#else - if (FLT_ROUNDS == 0) - aadj1 += 0.5; -#endif - } - y = word0(rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - rv0 = rv; - set_word0(rv, word0(rv) - P*Exp_msk1); - adj = aadj1 * ulp(rv); - rv += adj; - if ((word0(rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(rv0) == Big0 && word1(rv0) == Big1) - goto ovfl; - set_word0(rv, Big0); - set_word1(rv, Big1); - goto cont; - } - else - set_word0(rv, word0(rv) + P*Exp_msk1); - } - else { -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { - rv0 = rv; - set_word0(rv, word0(rv) + P*Exp_msk1); - adj = aadj1 * ulp(rv); - rv += adj; - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) - { - if (word0(rv0) == Tiny0 - && word1(rv0) == Tiny1) - goto undfl; - set_word0(rv, Tiny0); - set_word1(rv, Tiny1); - goto cont; - } - else - set_word0(rv, word0(rv) - P*Exp_msk1); - } - else { - adj = aadj1 * ulp(rv); - rv += adj; - } -#else - /* Compute adj so that the IEEE rounding rules will - * correctly round rv + adj in some half-way cases. - * If rv * ulp(rv) is denormalized (i.e., - * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid - * trouble from bits lost to denormalization; - * example: 1.2e-307 . - */ -#ifdef Avoid_Underflow - if (y <= P*Exp_msk1 && aadj > 1.) -#else - if (y <= (P-1)*Exp_msk1 && aadj > 1.) -#endif - { - aadj1 = (double)(int32)(aadj + 0.5); - if (!dsign) - aadj1 = -aadj1; - } -#ifdef Avoid_Underflow - if (scale && y <= P*Exp_msk1) - set_word0(aadj1, word0(aadj1) + (P+1)*Exp_msk1 - y); -#endif - adj = aadj1 * ulp(rv); - rv += adj; -#endif - } - z = word0(rv) & Exp_mask; -#ifdef Avoid_Underflow - if (!scale) -#endif - if (y == z) { - /* Can we stop now? */ - L = (Long)aadj; - aadj -= L; - /* The tolerances below are conservative. */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); - bb = bd = bs = delta = NULL; - } -#ifdef Avoid_Underflow - if (scale) { - rv0 = 0.; - set_word0(rv0, Exp_1 - P*Exp_msk1); - set_word1(rv0, 0); - if ((word0(rv) & Exp_mask) <= P*Exp_msk1 - && word1(rv) & 1 - && dsign != 2) { - if (dsign) { -#ifdef Sudden_Underflow - /* rv will be 0, but this would give the */ - /* right result if only rv *= rv0 worked. */ - set_word0(rv, word0(rv) + P*Exp_msk1); - set_word0(rv0, Exp_1 - 2*P*Exp_msk1); -#endif - rv += ulp(rv); - } - else - set_word1(rv, word1(rv) & ~1); - } - rv *= rv0; - } -#endif /* Avoid_Underflow */ -retfree: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); -ret: - RELEASE_DTOA_LOCK(); - if (se) - *se = (char *)s; - return sign ? -rv : rv; - -nomem: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - RELEASE_DTOA_LOCK(); - *err = JS_DTOA_ENOMEM; - return 0; -} - - -/* Return floor(b/2^k) and set b to be the remainder. The returned quotient must be less than 2^32. */ -static uint32 quorem2(Bigint *b, int32 k) -{ - ULong mask; - ULong result; - ULong *bx, *bxe; - int32 w; - int32 n = k >> 5; - k &= 0x1F; - mask = (1<wds - n; - if (w <= 0) - return 0; - JS_ASSERT(w <= 2); - bx = b->x; - bxe = bx + n; - result = *bxe >> k; - *bxe &= mask; - if (w == 2) { - JS_ASSERT(!(bxe[1] & ~mask)); - if (k) - result |= bxe[1] << (32 - k); - } - n++; - while (!*bxe && bxe != bx) { - n--; - bxe--; - } - b->wds = n; - return result; -} - -/* Return floor(b/S) and set b to be the remainder. As added restrictions, b must not have - * more words than S, the most significant word of S must not start with a 1 bit, and the - * returned quotient must be less than 36. */ -static int32 quorem(Bigint *b, Bigint *S) -{ - int32 n; - ULong *bx, *bxe, q, *sx, *sxe; -#ifdef ULLong - ULLong borrow, carry, y, ys; -#else - ULong borrow, carry, y, ys; - ULong si, z, zs; -#endif - - n = S->wds; - JS_ASSERT(b->wds <= n); - if (b->wds < n) - return 0; - sx = S->x; - sxe = sx + --n; - bx = b->x; - bxe = bx + n; - JS_ASSERT(*sxe <= 0x7FFFFFFF); - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ - JS_ASSERT(q < 36); - if (q) { - borrow = 0; - carry = 0; - do { -#ifdef ULLong - ys = *sx++ * (ULLong)q + carry; - carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; - borrow = y >> 32 & 1UL; - *bx++ = (ULong)(y & 0xffffffffUL); -#else - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#endif - } - while(sx <= sxe); - if (!*bxe) { - bx = b->x; - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b->x; - sx = S->x; - do { -#ifdef ULLong - ys = *sx++ + carry; - carry = ys >> 32; - y = *bx - (ys & 0xffffffffUL) - borrow; - borrow = y >> 32 & 1UL; - *bx++ = (ULong)(y & 0xffffffffUL); -#else - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#endif - } while(sx <= sxe); - bx = b->x; - bxe = bx + n; - if (!*bxe) { - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - return (int32)q; -} - -/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - -/* Always emits at least one digit. */ -/* If biasUp is set, then rounding in modes 2 and 3 will round away from zero - * when the number is exactly halfway between two representable values. For example, - * rounding 2.5 to zero digits after the decimal point will return 3 and not 2. - * 2.49 will still round to 2, and 2.51 will still round to 3. */ -/* bufsize should be at least 20 for modes 0 and 1. For the other modes, - * bufsize should be two greater than the maximum number of output characters expected. */ -static JSBool -js_dtoa(double d, int mode, JSBool biasUp, int ndigits, - int *decpt, int *sign, char **rve, char *buf, size_t bufsize) -{ - /* Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4-9 should give the same return values as 2-3, i.e., - 4 <= mode <= 9 ==> same return as mode - 2 + (mode & 1). These modes are mainly for - debugging; often they run slower but sometimes - faster than modes 2-3. - 4,5,8,9 ==> left-to-right digit generation. - 6-9 ==> don't try fast floating-point estimate - (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int32 bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, - j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, - spec_case, try_quick; - Long L; -#ifndef Sudden_Underflow - int32 denorm; - ULong x; -#endif - Bigint *b, *b1, *delta, *mlo, *mhi, *S; - double d2, ds, eps; - char *s; - - if (word0(d) & Sign_bit) { - /* set sign for everything, including 0's and NaNs */ - *sign = 1; - set_word0(d, word0(d) & ~Sign_bit); /* clear sign bit */ - } - else - *sign = 0; - - if ((word0(d) & Exp_mask) == Exp_mask) { - /* Infinity or NaN */ - *decpt = 9999; - s = !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"; - if ((s[0] == 'I' && bufsize < 9) || (s[0] == 'N' && bufsize < 4)) { - JS_ASSERT(JS_FALSE); -/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ - return JS_FALSE; - } - strcpy(buf, s); - if (rve) { - *rve = buf[3] ? buf + 8 : buf + 3; - JS_ASSERT(**rve == '\0'); - } - return JS_TRUE; - } - - b = NULL; /* initialize for abort protection */ - S = NULL; - mlo = mhi = NULL; - - if (!d) { - no_digits: - *decpt = 1; - if (bufsize < 2) { - JS_ASSERT(JS_FALSE); -/* JS_SetError(JS_BUFFER_OVERFLOW_ERROR, 0); */ - return JS_FALSE; - } - buf[0] = '0'; buf[1] = '\0'; /* copy "0" to buffer */ - if (rve) - *rve = buf + 1; - /* We might have jumped to "no_digits" from below, so we need - * to be sure to free the potentially allocated Bigints to avoid - * memory leaks. */ - Bfree(b); - Bfree(S); - if (mlo != mhi) - Bfree(mlo); - Bfree(mhi); - return JS_TRUE; - } - - b = d2b(d, &be, &bbits); - if (!b) - goto nomem; -#ifdef Sudden_Underflow - i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); -#else - if ((i = (int32)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1))) != 0) { -#endif - d2 = d; - set_word0(d2, word0(d2) & Frac_mask1); - set_word0(d2, word0(d2) | Exp_11); - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; -#ifndef Sudden_Underflow - denorm = 0; - } - else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P-1) - 1); - x = i > 32 ? word0(d) << (64 - i) | word1(d) >> (i - 32) : word1(d) << (32 - i); - d2 = x; - set_word0(d2, word0(d2) - 31*Exp_msk1); /* adjust exponent */ - i -= (Bias + (P-1) - 1) + 1; - denorm = 1; - } -#endif - /* At this point d = f*2^i, where 1 <= f < 2. d2 is an approximation of f. */ - ds = (d2-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = (int32)ds; - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (d < tens[k]) - k--; - k_check = 0; - } - /* At this point floor(log10(d)) <= k <= floor(log10(d))+1. - If k_check is zero, we're guaranteed that k = floor(log10(d)). */ - j = bbits - i - 1; - /* At this point d = b/2^j, where b is an odd integer. */ - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - /* At this point d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5), where b is an odd integer, - b2 >= 0, b5 >= 0, s2 >= 0, and s5 >= 0. */ - if (mode < 0 || mode > 9) - mode = 0; - try_quick = 1; - if (mode > 5) { - mode -= 4; - try_quick = 0; - } - leftright = 1; - ilim = ilim1 = 0; - switch(mode) { - case 0: - case 1: - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - break; - case 2: - leftright = 0; - /* no break */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = 0; - /* no break */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - /* ilim is the maximum number of significant digits we want, based on k and ndigits. */ - /* ilim1 is the maximum number of significant digits we want, based on k and ndigits, - when it turns out that k was computed too high by one. */ - - /* Ensure space for at least i+1 characters, including trailing null. */ - if (bufsize <= (size_t)i) { - Bfree(b); - JS_ASSERT(JS_FALSE); - return JS_FALSE; - } - s = buf; - - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - d2 = d; - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - /* Divide d by 10^k, keeping track of the roundoff error and avoiding overflows. */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - d /= bigtens[n_bigtens-1]; - ieps++; - } - for(; j; j >>= 1, i++) - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - d /= ds; - } - else if ((j1 = -k) != 0) { - d *= tens[j1 & 0xf]; - for(j = j1 >> 4; j; j >>= 1, i++) - if (j & 1) { - ieps++; - d *= bigtens[i]; - } - } - /* Check that k was computed correctly. */ - if (k_check && d < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fast_failed; - ilim = ilim1; - k--; - d *= 10.; - ieps++; - } - /* eps bounds the cumulative error. */ - eps = ieps*d + 7.; - set_word0(eps, word0(eps) - (P-1)*Exp_msk1); - if (ilim == 0) { - S = mhi = 0; - d -= 5.; - if (d > eps) - goto one_digit; - if (d < -eps) - goto no_digits; - goto fast_failed; - } -#ifndef No_leftright - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - eps = 0.5/tens[ilim-1] - eps; - for(i = 0;;) { - L = (Long)d; - d -= L; - *s++ = '0' + (char)L; - if (d < eps) - goto ret1; - if (1. - d < eps) - goto bump_up; - if (++i >= ilim) - break; - eps *= 10.; - d *= 10.; - } - } - else { -#endif - /* Generate ilim digits, then fix them up. */ - eps *= tens[ilim-1]; - for(i = 1;; i++, d *= 10.) { - L = (Long)d; - d -= L; - *s++ = '0' + (char)L; - if (i == ilim) { - if (d > 0.5 + eps) - goto bump_up; - else if (d < 0.5 - eps) { - while(*--s == '0') ; - s++; - goto ret1; - } - break; - } - } -#ifndef No_leftright - } -#endif - fast_failed: - s = buf; - d = d2; - k = k0; - ilim = ilim0; - } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = 0; - if (ilim < 0 || d < 5*ds || (!biasUp && d == 5*ds)) - goto no_digits; - goto one_digit; - } - - /* Use true number of digits to limit looping. */ - for(i = 1; i<=k+1; i++) { - L = (Long) (d / ds); - d -= L*ds; -#ifdef Check_FLT_ROUNDS - /* If FLT_ROUNDS == 2, L will usually be high by 1 */ - if (d < 0) { - L--; - d += ds; - } -#endif - *s++ = '0' + (char)L; - if (i == ilim) { - d += d; - if ((d > ds) || (d == ds && (L & 1 || biasUp))) { - bump_up: - while(*--s == '9') - if (s == buf) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; - } - d *= 10.; - } - goto ret1; - } - - m2 = b2; - m5 = b5; - if (leftright) { - if (mode < 2) { - i = -#ifndef Sudden_Underflow - denorm ? be + (Bias + (P-1) - 1 + 1) : -#endif - 1 + P - bbits; - /* i is 1 plus the number of trailing zero bits in d's significand. Thus, - (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 lsb of d)/10^k. */ - } - else { - j = ilim - 1; - if (m5 >= j) - m5 -= j; - else { - s5 += j -= m5; - b5 += j; - m5 = 0; - } - if ((i = ilim) < 0) { - m2 -= i; - i = 0; - } - /* (2^m2 * 5^m5) / (2^(s2+i) * 5^s5) = (1/2 * 10^(1-ilim))/10^k. */ - } - b2 += i; - s2 += i; - mhi = i2b(1); - if (!mhi) - goto nomem; - /* (mhi * 2^m2 * 5^m5) / (2^s2 * 5^s5) = one-half of last printed (when mode >= 2) or - input (when mode < 2) significant digit, divided by 10^k. */ - } - /* We still have d/10^k = (b * 2^b2 * 5^b5) / (2^s2 * 5^s5). Reduce common factors in - b2, m2, and s2 without changing the equalities. */ - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - - /* Fold b5 into b and m5 into mhi. */ - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - if (!mhi) - goto nomem; - b1 = mult(mhi, b); - if (!b1) - goto nomem; - Bfree(b); - b = b1; - } - if ((j = b5 - m5) != 0) { - b = pow5mult(b, j); - if (!b) - goto nomem; - } - } - else { - b = pow5mult(b, b5); - if (!b) - goto nomem; - } - } - /* Now we have d/10^k = (b * 2^b2) / (2^s2 * 5^s5) and - (mhi * 2^m2) / (2^s2 * 5^s5) = one-half of last printed or input significant digit, divided by 10^k. */ - - S = i2b(1); - if (!S) - goto nomem; - if (s5 > 0) { - S = pow5mult(S, s5); - if (!S) - goto nomem; - } - /* Now we have d/10^k = (b * 2^b2) / (S * 2^s2) and - (mhi * 2^m2) / (S * 2^s2) = one-half of last printed or input significant digit, divided by 10^k. */ - - /* Check for special case that d is a normalized power of 2. */ - spec_case = 0; - if (mode < 2) { - if (!word1(d) && !(word0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(d) & (Exp_mask & Exp_mask << 1) -#endif - ) { - /* The special case. Here we want to be within a quarter of the last input - significant digit instead of one half of it when the decimal output string's value is less than d. */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; - } - } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f) != 0) - i = 32 - i; - /* i is the number of leading zero bits in the most significant word of S*2^s2. */ - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } - else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - /* Now S*2^s2 has exactly four leading zero bits in its most significant word. */ - if (b2 > 0) { - b = lshift(b, b2); - if (!b) - goto nomem; - } - if (s2 > 0) { - S = lshift(S, s2); - if (!S) - goto nomem; - } - /* Now we have d/10^k = b/S and - (mhi * 2^m2) / S = maximum acceptable error, divided by 10^k. */ - if (k_check) { - if (cmp(b,S) < 0) { - k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ - if (!b) - goto nomem; - if (leftright) { - mhi = multadd(mhi, 10, 0); - if (!mhi) - goto nomem; - } - ilim = ilim1; - } - } - /* At this point 1 <= d/10^k = b/S < 10. */ - - if (ilim <= 0 && mode > 2) { - /* We're doing fixed-mode output and d is less than the minimum nonzero output in this mode. - Output either zero or the minimum nonzero output depending on which is closer to d. */ - if (ilim < 0) - goto no_digits; - S = multadd(S,5,0); - if (!S) - goto nomem; - i = cmp(b,S); - if (i < 0 || (i == 0 && !biasUp)) { - /* Always emit at least one digit. If the number appears to be zero - using the current mode, then emit one '0' digit and set decpt to 1. */ - /*no_digits: - k = -1 - ndigits; - goto ret; */ - goto no_digits; - } - one_digit: - *s++ = '1'; - k++; - goto ret; - } - if (leftright) { - if (m2 > 0) { - mhi = lshift(mhi, m2); - if (!mhi) - goto nomem; - } - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = Balloc(mhi->k); - if (!mhi) - goto nomem; - Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); - if (!mhi) - goto nomem; - } - /* mlo/S = maximum acceptable error, divided by 10^k, if the output is less than d. */ - /* mhi/S = maximum acceptable error, divided by 10^k, if the output is greater than d. */ - - for(i = 1;;i++) { - dig = quorem(b,S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - /* j is b/S compared with mlo/S. */ - delta = diff(S, mhi); - if (!delta) - goto nomem; - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); - /* j1 is b/S compared with 1 - mhi/S. */ -#ifndef ROUND_BIASED - if (j1 == 0 && !mode && !(word1(d) & 1)) { - if (dig == '9') - goto round_9_up; - if (j > 0) - dig++; - *s++ = (char)dig; - goto ret; - } -#endif - if ((j < 0) || (j == 0 && !mode -#ifndef ROUND_BIASED - && !(word1(d) & 1) -#endif - )) { - if (j1 > 0) { - /* Either dig or dig+1 would work here as the least significant decimal digit. - Use whichever would produce a decimal value closer to d. */ - b = lshift(b, 1); - if (!b) - goto nomem; - j1 = cmp(b, S); - if (((j1 > 0) || (j1 == 0 && (dig & 1 || biasUp))) - && (dig++ == '9')) - goto round_9_up; - } - *s++ = (char)dig; - goto ret; - } - if (j1 > 0) { - if (dig == '9') { /* possible if i == 1 */ - round_9_up: - *s++ = '9'; - goto roundoff; - } - *s++ = (char)dig + 1; - goto ret; - } - *s++ = (char)dig; - if (i == ilim) - break; - b = multadd(b, 10, 0); - if (!b) - goto nomem; - if (mlo == mhi) { - mlo = mhi = multadd(mhi, 10, 0); - if (!mhi) - goto nomem; - } - else { - mlo = multadd(mlo, 10, 0); - if (!mlo) - goto nomem; - mhi = multadd(mhi, 10, 0); - if (!mhi) - goto nomem; - } - } - } - else - for(i = 1;; i++) { - *s++ = (char)(dig = quorem(b,S) + '0'); - if (i >= ilim) - break; - b = multadd(b, 10, 0); - if (!b) - goto nomem; - } - - /* Round off last digit */ - - b = lshift(b, 1); - if (!b) - goto nomem; - j = cmp(b, S); - if ((j > 0) || (j == 0 && (dig & 1 || biasUp))) { - roundoff: - while(*--s == '9') - if (s == buf) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } - else { - /* Strip trailing zeros */ - while(*--s == '0') ; - s++; - } - ret: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - ret1: - Bfree(b); - JS_ASSERT(s < buf + bufsize); - *s = '\0'; - if (rve) - *rve = s; - *decpt = k + 1; - return JS_TRUE; - -nomem: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - Bfree(b); - return JS_FALSE; -} - - -/* Mapping of JSDToStrMode -> js_dtoa mode */ -static const int dtoaModes[] = { - 0, /* DTOSTR_STANDARD */ - 0, /* DTOSTR_STANDARD_EXPONENTIAL, */ - 3, /* DTOSTR_FIXED, */ - 2, /* DTOSTR_EXPONENTIAL, */ - 2}; /* DTOSTR_PRECISION */ - -JS_FRIEND_API(char *) -JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double d) -{ - int decPt; /* Position of decimal point relative to first digit returned by js_dtoa */ - int sign; /* Nonzero if the sign bit was set in d */ - int nDigits; /* Number of significand digits returned by js_dtoa */ - char *numBegin = buffer+2; /* Pointer to the digits returned by js_dtoa; the +2 leaves space for */ - /* the sign and/or decimal point */ - char *numEnd; /* Pointer past the digits returned by js_dtoa */ - JSBool dtoaRet; - - JS_ASSERT(bufferSize >= (size_t)(mode <= DTOSTR_STANDARD_EXPONENTIAL ? DTOSTR_STANDARD_BUFFER_SIZE : - DTOSTR_VARIABLE_BUFFER_SIZE(precision))); - - if (mode == DTOSTR_FIXED && (d >= 1e21 || d <= -1e21)) - mode = DTOSTR_STANDARD; /* Change mode here rather than below because the buffer may not be large enough to hold a large integer. */ - - /* Locking for Balloc's shared buffers */ - ACQUIRE_DTOA_LOCK(); - dtoaRet = js_dtoa(d, dtoaModes[mode], mode >= DTOSTR_FIXED, precision, &decPt, &sign, &numEnd, numBegin, bufferSize-2); - RELEASE_DTOA_LOCK(); - if (!dtoaRet) - return 0; - - nDigits = numEnd - numBegin; - - /* If Infinity, -Infinity, or NaN, return the string regardless of the mode. */ - if (decPt != 9999) { - JSBool exponentialNotation = JS_FALSE; - int minNDigits = 0; /* Minimum number of significand digits required by mode and precision */ - char *p; - char *q; - - switch (mode) { - case DTOSTR_STANDARD: - if (decPt < -5 || decPt > 21) - exponentialNotation = JS_TRUE; - else - minNDigits = decPt; - break; - - case DTOSTR_FIXED: - if (precision >= 0) - minNDigits = decPt + precision; - else - minNDigits = decPt; - break; - - case DTOSTR_EXPONENTIAL: - JS_ASSERT(precision > 0); - minNDigits = precision; - /* Fall through */ - case DTOSTR_STANDARD_EXPONENTIAL: - exponentialNotation = JS_TRUE; - break; - - case DTOSTR_PRECISION: - JS_ASSERT(precision > 0); - minNDigits = precision; - if (decPt < -5 || decPt > precision) - exponentialNotation = JS_TRUE; - break; - } - - /* If the number has fewer than minNDigits, pad it with zeros at the end */ - if (nDigits < minNDigits) { - p = numBegin + minNDigits; - nDigits = minNDigits; - do { - *numEnd++ = '0'; - } while (numEnd != p); - *numEnd = '\0'; - } - - if (exponentialNotation) { - /* Insert a decimal point if more than one significand digit */ - if (nDigits != 1) { - numBegin--; - numBegin[0] = numBegin[1]; - numBegin[1] = '.'; - } - JS_snprintf(numEnd, bufferSize - (numEnd - buffer), "e%+d", decPt-1); - } else if (decPt != nDigits) { - /* Some kind of a fraction in fixed notation */ - JS_ASSERT(decPt <= nDigits); - if (decPt > 0) { - /* dd...dd . dd...dd */ - p = --numBegin; - do { - *p = p[1]; - p++; - } while (--decPt); - *p = '.'; - } else { - /* 0 . 00...00dd...dd */ - p = numEnd; - numEnd += 1 - decPt; - q = numEnd; - JS_ASSERT(numEnd < buffer + bufferSize); - *numEnd = '\0'; - while (p != numBegin) - *--q = *--p; - for (p = numBegin + 1; p != q; p++) - *p = '0'; - *numBegin = '.'; - *--numBegin = '0'; - } - } - } - - /* If negative and neither -0.0 nor NaN, output a leading '-'. */ - if (sign && - !(word0(d) == Sign_bit && word1(d) == 0) && - !((word0(d) & Exp_mask) == Exp_mask && - (word1(d) || (word0(d) & Frac_mask)))) { - *--numBegin = '-'; - } - return numBegin; -} - - -/* Let b = floor(b / divisor), and return the remainder. b must be nonnegative. - * divisor must be between 1 and 65536. - * This function cannot run out of memory. */ -static uint32 -divrem(Bigint *b, uint32 divisor) -{ - int32 n = b->wds; - uint32 remainder = 0; - ULong *bx; - ULong *bp; - - JS_ASSERT(divisor > 0 && divisor <= 65536); - - if (!n) - return 0; /* b is zero */ - bx = b->x; - bp = bx + n; - do { - ULong a = *--bp; - ULong dividend = remainder << 16 | a >> 16; - ULong quotientHi = dividend / divisor; - ULong quotientLo; - - remainder = dividend - quotientHi*divisor; - JS_ASSERT(quotientHi <= 0xFFFF && remainder < divisor); - dividend = remainder << 16 | (a & 0xFFFF); - quotientLo = dividend / divisor; - remainder = dividend - quotientLo*divisor; - JS_ASSERT(quotientLo <= 0xFFFF && remainder < divisor); - *bp = quotientHi << 16 | quotientLo; - } while (bp != bx); - /* Decrease the size of the number if its most significant word is now zero. */ - if (bx[n-1] == 0) - b->wds--; - return remainder; -} - - -/* "-0.0000...(1073 zeros after decimal point)...0001\0" is the longest string that we could produce, - * which occurs when printing -5e-324 in binary. We could compute a better estimate of the size of - * the output string and malloc fewer bytes depending on d and base, but why bother? */ -#define DTOBASESTR_BUFFER_SIZE 1078 -#define BASEDIGIT(digit) ((char)(((digit) >= 10) ? 'a' - 10 + (digit) : '0' + (digit))) - -JS_FRIEND_API(char *) -JS_dtobasestr(int base, double d) -{ - char *buffer; /* The output string */ - char *p; /* Pointer to current position in the buffer */ - char *pInt; /* Pointer to the beginning of the integer part of the string */ - char *q; - uint32 digit; - double di; /* d truncated to an integer */ - double df; /* The fractional part of d */ - - JS_ASSERT(base >= 2 && base <= 36); - - buffer = (char*) malloc(DTOBASESTR_BUFFER_SIZE); - if (buffer) { - p = buffer; - if (d < 0.0 -#if defined(XP_WIN) || defined(XP_OS2) - && !((word0(d) & Exp_mask) == Exp_mask && ((word0(d) & Frac_mask) || word1(d))) /* Visual C++ doesn't know how to compare against NaN */ -#endif - ) { - *p++ = '-'; - d = -d; - } - - /* Check for Infinity and NaN */ - if ((word0(d) & Exp_mask) == Exp_mask) { - strcpy(p, !word1(d) && !(word0(d) & Frac_mask) ? "Infinity" : "NaN"); - return buffer; - } - - /* Locking for Balloc's shared buffers */ - ACQUIRE_DTOA_LOCK(); - - /* Output the integer part of d with the digits in reverse order. */ - pInt = p; - di = fd_floor(d); - if (di <= 4294967295.0) { - uint32 n = (uint32)di; - if (n) - do { - uint32 m = n / base; - digit = n - m*base; - n = m; - JS_ASSERT(digit < (uint32)base); - *p++ = BASEDIGIT(digit); - } while (n); - else *p++ = '0'; - } else { - int32 e; - int32 bits; /* Number of significant bits in di; not used. */ - Bigint *b = d2b(di, &e, &bits); - if (!b) - goto nomem1; - b = lshift(b, e); - if (!b) { - nomem1: - Bfree(b); - RELEASE_DTOA_LOCK(); - free(buffer); - return NULL; - } - do { - digit = divrem(b, base); - JS_ASSERT(digit < (uint32)base); - *p++ = BASEDIGIT(digit); - } while (b->wds); - Bfree(b); - } - /* Reverse the digits of the integer part of d. */ - q = p-1; - while (q > pInt) { - char ch = *pInt; - *pInt++ = *q; - *q-- = ch; - } - - df = d - di; - if (df != 0.0) { - /* We have a fraction. */ - int32 e, bbits, s2, done; - Bigint *b, *s, *mlo, *mhi; - - b = s = mlo = mhi = NULL; - - *p++ = '.'; - b = d2b(df, &e, &bbits); - if (!b) { - nomem2: - Bfree(b); - Bfree(s); - if (mlo != mhi) - Bfree(mlo); - Bfree(mhi); - RELEASE_DTOA_LOCK(); - free(buffer); - return NULL; - } - JS_ASSERT(e < 0); - /* At this point df = b * 2^e. e must be less than zero because 0 < df < 1. */ - - s2 = -(int32)(word0(d) >> Exp_shift1 & Exp_mask>>Exp_shift1); -#ifndef Sudden_Underflow - if (!s2) - s2 = -1; -#endif - s2 += Bias + P; - /* 1/2^s2 = (nextDouble(d) - d)/2 */ - JS_ASSERT(-s2 < e); - mlo = i2b(1); - if (!mlo) - goto nomem2; - mhi = mlo; - if (!word1(d) && !(word0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(d) & (Exp_mask & Exp_mask << 1) -#endif - ) { - /* The special case. Here we want to be within a quarter of the last input - significant digit instead of one half of it when the output string's value is less than d. */ - s2 += Log2P; - mhi = i2b(1< df = b/2^s2 > 0; - * (d - prevDouble(d))/2 = mlo/2^s2; - * (nextDouble(d) - d)/2 = mhi/2^s2. */ - - done = JS_FALSE; - do { - int32 j, j1; - Bigint *delta; - - b = multadd(b, base, 0); - if (!b) - goto nomem2; - digit = quorem2(b, s2); - if (mlo == mhi) { - mlo = mhi = multadd(mlo, base, 0); - if (!mhi) - goto nomem2; - } - else { - mlo = multadd(mlo, base, 0); - if (!mlo) - goto nomem2; - mhi = multadd(mhi, base, 0); - if (!mhi) - goto nomem2; - } - - /* Do we yet have the shortest string that will round to d? */ - j = cmp(b, mlo); - /* j is b/2^s2 compared with mlo/2^s2. */ - delta = diff(s, mhi); - if (!delta) - goto nomem2; - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); - /* j1 is b/2^s2 compared with 1 - mhi/2^s2. */ - -#ifndef ROUND_BIASED - if (j1 == 0 && !(word1(d) & 1)) { - if (j > 0) - digit++; - done = JS_TRUE; - } else -#endif - if (j < 0 || (j == 0 -#ifndef ROUND_BIASED - && !(word1(d) & 1) -#endif - )) { - if (j1 > 0) { - /* Either dig or dig+1 would work here as the least significant digit. - Use whichever would produce an output value closer to d. */ - b = lshift(b, 1); - if (!b) - goto nomem2; - j1 = cmp(b, s); - if (j1 > 0) /* The even test (|| (j1 == 0 && (digit & 1))) is not here because it messes up odd base output - * such as 3.5 in base 3. */ - digit++; - } - done = JS_TRUE; - } else if (j1 > 0) { - digit++; - done = JS_TRUE; - } - JS_ASSERT(digit < (uint32)base); - *p++ = BASEDIGIT(digit); - } while (!done); - Bfree(b); - Bfree(s); - if (mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - JS_ASSERT(p < buffer + DTOBASESTR_BUFFER_SIZE); - *p = '\0'; - RELEASE_DTOA_LOCK(); - } - return buffer; -} diff --git a/spidermonkey/libjs/jsdtoa.h b/spidermonkey/libjs/jsdtoa.h deleted file mode 100644 index 409f454..0000000 --- a/spidermonkey/libjs/jsdtoa.h +++ /dev/null @@ -1,130 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsdtoa_h___ -#define jsdtoa_h___ -/* - * Public interface to portable double-precision floating point to string - * and back conversion package. - */ - -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -/* - * JS_strtod() returns as a double-precision floating-point number - * the value represented by the character string pointed to by - * s00. The string is scanned up to the first unrecognized - * character. - * If the value of se is not (char **)NULL, a pointer to - * the character terminating the scan is returned in the location pointed - * to by se. If no number can be formed, se is set to s00r, and - * zero is returned. - * - * *err is set to zero on success; it's set to JS_DTOA_ERANGE on range - * errors and JS_DTOA_ENOMEM on memory failure. - */ -#define JS_DTOA_ERANGE 1 -#define JS_DTOA_ENOMEM 2 -JS_FRIEND_API(double) -JS_strtod(const char *s00, char **se, int *err); - -/* - * Modes for converting floating-point numbers to strings. - * - * Some of the modes can round-trip; this means that if the number is converted to - * a string using one of these mode and then converted back to a number, the result - * will be identical to the original number (except that, due to ECMA, -0 will get converted - * to +0). These round-trip modes return the minimum number of significand digits that - * permit the round trip. - * - * Some of the modes take an integer parameter . - */ -/* NB: Keep this in sync with number_constants[]. */ -typedef enum JSDToStrMode { - DTOSTR_STANDARD, /* Either fixed or exponential format; round-trip */ - DTOSTR_STANDARD_EXPONENTIAL, /* Always exponential format; round-trip */ - DTOSTR_FIXED, /* Round to digits after the decimal point; exponential if number is large */ - DTOSTR_EXPONENTIAL, /* Always exponential format; significant digits */ - DTOSTR_PRECISION /* Either fixed or exponential format; significant digits */ -} JSDToStrMode; - - -/* Maximum number of characters (including trailing null) that a DTOSTR_STANDARD or DTOSTR_STANDARD_EXPONENTIAL - * conversion can produce. This maximum is reached for a number like -0.0000012345678901234567. */ -#define DTOSTR_STANDARD_BUFFER_SIZE 26 - -/* Maximum number of characters (including trailing null) that one of the other conversions - * can produce. This maximum is reached for TO_FIXED, which can generate up to 21 digits before the decimal point. */ -#define DTOSTR_VARIABLE_BUFFER_SIZE(precision) ((precision)+24 > DTOSTR_STANDARD_BUFFER_SIZE ? (precision)+24 : DTOSTR_STANDARD_BUFFER_SIZE) - -/* - * Convert dval according to the given mode and return a pointer to the resulting ASCII string. - * The result is held somewhere in buffer, but not necessarily at the beginning. The size of - * buffer is given in bufferSize, and must be at least as large as given by the above macros. - * - * Return NULL if out of memory. - */ -JS_FRIEND_API(char *) -JS_dtostr(char *buffer, size_t bufferSize, JSDToStrMode mode, int precision, double dval); - -/* - * Convert d to a string in the given base. The integral part of d will be printed exactly - * in that base, regardless of how large it is, because there is no exponential notation for non-base-ten - * numbers. The fractional part will be rounded to as few digits as possible while still preserving - * the round-trip property (analogous to that of printing decimal numbers). In other words, if one were - * to read the resulting string in via a hypothetical base-number-reading routine that rounds to the nearest - * IEEE double (and to an even significand if there are two equally near doubles), then the result would - * equal d (except for -0.0, which converts to "0", and NaN, which is not equal to itself). - * - * Return NULL if out of memory. If the result is not NULL, it must be released via free(). - */ -JS_FRIEND_API(char *) -JS_dtobasestr(int base, double d); - -/* - * Clean up any persistent RAM allocated during the execution of DtoA - * routines, and remove any locks that might have been created. - */ -extern void js_FinishDtoa(void); - -JS_END_EXTERN_C - -#endif /* jsdtoa_h___ */ diff --git a/spidermonkey/libjs/jsemit.c b/spidermonkey/libjs/jsemit.c deleted file mode 100644 index f8a06be..0000000 --- a/spidermonkey/libjs/jsemit.c +++ /dev/null @@ -1,6845 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS bytecode generation. - */ -#include "jsstddef.h" -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsbit.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" - -/* Allocation chunk counts, must be powers of two in general. */ -#define BYTECODE_CHUNK 256 /* code allocation increment */ -#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */ -#define TRYNOTE_CHUNK 64 /* trynote allocation increment */ - -/* Macros to compute byte sizes from typed element counts. */ -#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode)) -#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote)) -#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote)) - -JS_FRIEND_API(JSBool) -js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, - JSArenaPool *codePool, JSArenaPool *notePool, - const char *filename, uintN lineno, - JSPrincipals *principals) -{ - memset(cg, 0, sizeof *cg); - TREE_CONTEXT_INIT(&cg->treeContext); - cg->treeContext.flags |= TCF_COMPILING; - cg->codePool = codePool; - cg->notePool = notePool; - cg->codeMark = JS_ARENA_MARK(codePool); - cg->noteMark = JS_ARENA_MARK(notePool); - cg->tempMark = JS_ARENA_MARK(&cx->tempPool); - cg->current = &cg->main; - cg->filename = filename; - cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno; - cg->principals = principals; - ATOM_LIST_INIT(&cg->atomList); - cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1; - ATOM_LIST_INIT(&cg->constList); - return JS_TRUE; -} - -JS_FRIEND_API(void) -js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg) -{ - TREE_CONTEXT_FINISH(&cg->treeContext); - JS_ARENA_RELEASE(cg->codePool, cg->codeMark); - JS_ARENA_RELEASE(cg->notePool, cg->noteMark); - JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark); -} - -static ptrdiff_t -EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta) -{ - jsbytecode *base, *limit, *next; - ptrdiff_t offset, length; - size_t incr, size; - - base = CG_BASE(cg); - next = CG_NEXT(cg); - limit = CG_LIMIT(cg); - offset = PTRDIFF(next, base, jsbytecode); - if (next + delta > limit) { - length = offset + delta; - length = (length <= BYTECODE_CHUNK) - ? BYTECODE_CHUNK - : JS_BIT(JS_CeilingLog2(length)); - incr = BYTECODE_SIZE(length); - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr); - } else { - size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode)); - incr -= size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); - } - if (!base) { - JS_ReportOutOfMemory(cx); - return -1; - } - CG_BASE(cg) = base; - CG_LIMIT(cg) = base + length; - CG_NEXT(cg) = base + offset; - } - return offset; -} - -static void -UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target) -{ - jsbytecode *pc; - const JSCodeSpec *cs; - intN nuses; - - pc = CG_CODE(cg, target); - cs = &js_CodeSpec[pc[0]]; - nuses = cs->nuses; - if (nuses < 0) - nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */ - cg->stackDepth -= nuses; - JS_ASSERT(cg->stackDepth >= 0); - if (cg->stackDepth < 0) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", target); - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, - js_GetErrorMessage, NULL, - JSMSG_STACK_UNDERFLOW, - cg->filename ? cg->filename : "stdin", - numBuf); - } - cg->stackDepth += cs->ndefs; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; -} - -ptrdiff_t -js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 1); - - if (offset >= 0) { - *CG_NEXT(cg)++ = (jsbytecode)op; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 2); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = op1; - CG_NEXT(cg) = next + 2; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2) -{ - ptrdiff_t offset = EmitCheck(cx, cg, op, 3); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - next[0] = (jsbytecode)op; - next[1] = op1; - next[2] = op2; - CG_NEXT(cg) = next + 3; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -ptrdiff_t -js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra) -{ - ptrdiff_t length = 1 + (ptrdiff_t)extra; - ptrdiff_t offset = EmitCheck(cx, cg, op, length); - - if (offset >= 0) { - jsbytecode *next = CG_NEXT(cg); - *next = (jsbytecode)op; - memset(next + 1, 0, BYTECODE_SIZE(extra)); - CG_NEXT(cg) = next + length; - UpdateDepth(cx, cg, offset); - } - return offset; -} - -/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */ -const char js_with_statement_str[] = "with statement"; -const char js_finally_block_str[] = "finally block"; -const char js_script_str[] = "script"; - -static const char *statementName[] = { - "label statement", /* LABEL */ - "if statement", /* IF */ - "else statement", /* ELSE */ - "switch statement", /* SWITCH */ - "block", /* BLOCK */ - js_with_statement_str, /* WITH */ - "catch block", /* CATCH */ - "try block", /* TRY */ - js_finally_block_str, /* FINALLY */ - js_finally_block_str, /* SUBROUTINE */ - "do loop", /* DO_LOOP */ - "for loop", /* FOR_LOOP */ - "for/in loop", /* FOR_IN_LOOP */ - "while loop", /* WHILE_LOOP */ -}; - -static const char * -StatementName(JSCodeGenerator *cg) -{ - if (!cg->treeContext.topStmt) - return js_script_str; - return statementName[cg->treeContext.topStmt->type]; -} - -static void -ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg) -{ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET, - StatementName(cg)); -} - -/** - Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) - and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided - into unconditional (gotos and gosubs), and conditional jumps or branches - (which pop a value, test it, and jump depending on its value). Most jumps - have just one immediate operand, a signed offset from the jump opcode's pc - to the target bytecode. The lookup and table switch opcodes may contain - many jump offsets. - - Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was - fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is - suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for - the extended form of the JSOP_OR branch opcode). The unextended or short - formats have 16-bit signed immediate offset operands, the extended or long - formats have 32-bit signed immediates. The span-dependency problem consists - of selecting as few long instructions as possible, or about as few -- since - jumps can span other jumps, extending one jump may cause another to need to - be extended. - - Most JS scripts are short, so need no extended jumps. We optimize for this - case by generating short jumps until we know a long jump is needed. After - that point, we keep generating short jumps, but each jump's 16-bit immediate - offset operand is actually an unsigned index into cg->spanDeps, an array of - JSSpanDep structs. Each struct tells the top offset in the script of the - opcode, the "before" offset of the jump (which will be the same as top for - simplex jumps, but which will index further into the bytecode array for a - non-initial jump offset in a lookup or table switch), the after "offset" - adjusted during span-dependent instruction selection (initially the same - value as the "before" offset), and the jump target (more below). - - Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must - ensure that all bytecode generated so far can be inspected to discover where - the jump offset immediate operands lie within CG_CODE(cg). But the bonus is - that we generate span-dependency records sorted by their offsets, so we can - binary-search when trying to find a JSSpanDep for a given bytecode offset, - or the nearest JSSpanDep at or above a given pc. - - To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows - 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This - tells us that we need to binary-search for the cg->spanDeps entry by the - jump opcode's bytecode offset (sd->before). - - Jump targets need to be maintained in a data structure that lets us look - up an already-known target by its address (jumps may have a common target), - and that also lets us update the addresses (script-relative, a.k.a. absolute - offsets) of targets that come after a jump target (for when a jump below - that target needs to be extended). We use an AVL tree, implemented using - recursion, but with some tricky optimizations to its height-balancing code - (see http://www.cmcrossroads.com/bradapp/ftp/src/libs/C++/AvlTrees.html). - - A final wrinkle: backpatch chains are linked by jump-to-jump offsets with - positive sign, even though they link "backward" (i.e., toward lower bytecode - address). We don't want to waste space and search time in the AVL tree for - such temporary backpatch deltas, so we use a single-bit wildcard scheme to - tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas - in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known - target, or is still awaiting backpatching. - - Note that backpatch chains would present a problem for BuildSpanDepTable, - which inspects bytecode to build cg->spanDeps on demand, when the first - short jump offset overflows. To solve this temporary problem, we emit a - proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose - nuses/ndefs counts help keep the stack balanced, but whose opcode format - distinguishes its backpatch delta immediate operand from a normal jump - offset. - */ -static int -BalanceJumpTargets(JSJumpTarget **jtp) -{ - JSJumpTarget *jt, *jt2, *root; - int dir, otherDir, heightChanged; - JSBool doubleRotate; - - jt = *jtp; - JS_ASSERT(jt->balance != 0); - - if (jt->balance < -1) { - dir = JT_RIGHT; - doubleRotate = (jt->kids[JT_LEFT]->balance > 0); - } else if (jt->balance > 1) { - dir = JT_LEFT; - doubleRotate = (jt->kids[JT_RIGHT]->balance < 0); - } else { - return 0; - } - - otherDir = JT_OTHER_DIR(dir); - if (doubleRotate) { - jt2 = jt->kids[otherDir]; - *jtp = root = jt2->kids[dir]; - - jt->kids[otherDir] = root->kids[dir]; - root->kids[dir] = jt; - - jt2->kids[dir] = root->kids[otherDir]; - root->kids[otherDir] = jt2; - - heightChanged = 1; - root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0); - root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0); - root->balance = 0; - } else { - *jtp = root = jt->kids[otherDir]; - jt->kids[otherDir] = root->kids[dir]; - root->kids[dir] = jt; - - heightChanged = (root->balance != 0); - jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance); - } - - return heightChanged; -} - -typedef struct AddJumpTargetArgs { - JSContext *cx; - JSCodeGenerator *cg; - ptrdiff_t offset; - JSJumpTarget *node; -} AddJumpTargetArgs; - -static int -AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp) -{ - JSJumpTarget *jt; - int balanceDelta; - - jt = *jtp; - if (!jt) { - JSCodeGenerator *cg = args->cg; - - jt = cg->jtFreeList; - if (jt) { - cg->jtFreeList = jt->kids[JT_LEFT]; - } else { - JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool, - sizeof *jt); - if (!jt) { - JS_ReportOutOfMemory(args->cx); - return 0; - } - } - jt->offset = args->offset; - jt->balance = 0; - jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL; - cg->numJumpTargets++; - args->node = jt; - *jtp = jt; - return 1; - } - - if (jt->offset == args->offset) { - args->node = jt; - return 0; - } - - if (args->offset < jt->offset) - balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]); - else - balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]); - if (!args->node) - return 0; - - jt->balance += balanceDelta; - return (balanceDelta && jt->balance) - ? 1 - BalanceJumpTargets(jtp) - : 0; -} - -#ifdef DEBUG_brendan -static int AVLCheck(JSJumpTarget *jt) -{ - int lh, rh; - - if (!jt) return 0; - JS_ASSERT(-1 <= jt->balance && jt->balance <= 1); - lh = AVLCheck(jt->kids[JT_LEFT]); - rh = AVLCheck(jt->kids[JT_RIGHT]); - JS_ASSERT(jt->balance == rh - lh); - return 1 + JS_MAX(lh, rh); -} -#endif - -static JSBool -SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd, - ptrdiff_t off) -{ - AddJumpTargetArgs args; - - if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - args.cx = cx; - args.cg = cg; - args.offset = sd->top + off; - args.node = NULL; - AddJumpTarget(&args, &cg->jumpTargets); - if (!args.node) - return JS_FALSE; - -#ifdef DEBUG_brendan - AVLCheck(cg->jumpTargets); -#endif - - SD_SET_TARGET(sd, args.node); - return JS_TRUE; -} - -#define SPANDEPS_MIN 256 -#define SPANDEPS_SIZE(n) ((n) * sizeof(JSSpanDep)) -#define SPANDEPS_SIZE_MIN SPANDEPS_SIZE(SPANDEPS_MIN) - -static JSBool -AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2, - ptrdiff_t off) -{ - uintN index; - JSSpanDep *sdbase, *sd; - size_t size; - - index = cg->numSpanDeps; - if (index + 1 == 0) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - if ((index & (index - 1)) == 0 && - (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) { - if (!sdbase) { - size = SPANDEPS_SIZE_MIN; - JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size); - } else { - size = SPANDEPS_SIZE(index); - JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size); - } - if (!sdbase) - return JS_FALSE; - cg->spanDeps = sdbase; - } - - cg->numSpanDeps = index + 1; - sd = cg->spanDeps + index; - sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode); - sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode); - - if (js_CodeSpec[*pc].format & JOF_BACKPATCH) { - /* Jump offset will be backpatched if off is a non-zero "bpdelta". */ - if (off != 0) { - JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN); - if (off > BPDELTA_MAX) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - } - SD_SET_BPDELTA(sd, off); - } else if (off == 0) { - /* Jump offset will be patched directly, without backpatch chaining. */ - SD_SET_TARGET(sd, NULL); - } else { - /* The jump offset in off is non-zero, therefore it's already known. */ - if (!SetSpanDepTarget(cx, cg, sd, off)) - return JS_FALSE; - } - - if (index > SPANDEP_INDEX_MAX) - index = SPANDEP_INDEX_HUGE; - SET_SPANDEP_INDEX(pc2, index); - return JS_TRUE; -} - -static JSBool -BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg) -{ - jsbytecode *pc, *end; - JSOp op; - const JSCodeSpec *cs; - ptrdiff_t len, off; - - pc = CG_BASE(cg) + cg->spanDepTodo; - end = CG_NEXT(cg); - while (pc < end) { - op = (JSOp)*pc; - cs = &js_CodeSpec[op]; - len = (ptrdiff_t)cs->length; - - switch (cs->format & JOF_TYPEMASK) { - case JOF_JUMP: - off = GET_JUMP_OFFSET(pc); - if (!AddSpanDep(cx, cg, pc, pc, off)) - return JS_FALSE; - break; - - case JOF_TABLESWITCH: - { - jsbytecode *pc2; - jsint i, low, high; - - pc2 = pc; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - for (i = low; i <= high; i++) { - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - } - len = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - { - jsbytecode *pc2; - jsint npairs; - - pc2 = pc; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - npairs = (jsint) GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - while (npairs) { - pc2 += ATOM_INDEX_LEN; - off = GET_JUMP_OFFSET(pc2); - if (!AddSpanDep(cx, cg, pc, pc2, off)) - return JS_FALSE; - pc2 += JUMP_OFFSET_LEN; - npairs--; - } - len = 1 + pc2 - pc; - break; - } - } - - JS_ASSERT(len > 0); - pc += len; - } - - return JS_TRUE; -} - -static JSSpanDep * -GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc) -{ - uintN index; - ptrdiff_t offset; - int lo, hi, mid; - JSSpanDep *sd; - - index = GET_SPANDEP_INDEX(pc); - if (index != SPANDEP_INDEX_HUGE) - return cg->spanDeps + index; - - offset = PTRDIFF(pc, CG_BASE(cg), jsbytecode); - lo = 0; - hi = cg->numSpanDeps - 1; - while (lo <= hi) { - mid = (lo + hi) / 2; - sd = cg->spanDeps + mid; - if (sd->before == offset) - return sd; - if (sd->before < offset) - lo = mid + 1; - else - hi = mid - 1; - } - - JS_ASSERT(0); - return NULL; -} - -static JSBool -SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t delta) -{ - JSSpanDep *sd; - - JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); - if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) { - SET_JUMP_OFFSET(pc, delta); - return JS_TRUE; - } - - if (delta > BPDELTA_MAX) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - if (!cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return JS_FALSE; - - sd = GetSpanDep(cg, pc); - JS_ASSERT(SD_GET_BPDELTA(sd) == 0); - SD_SET_BPDELTA(sd, delta); - return JS_TRUE; -} - -static void -UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta) -{ - if (jt->offset > pivot) { - jt->offset += delta; - if (jt->kids[JT_LEFT]) - UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta); - } - if (jt->kids[JT_RIGHT]) - UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta); -} - -static JSSpanDep * -FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo, - JSSpanDep *guard) -{ - int num, hi, mid; - JSSpanDep *sdbase, *sd; - - num = cg->numSpanDeps; - JS_ASSERT(num > 0); - hi = num - 1; - sdbase = cg->spanDeps; - while (lo <= hi) { - mid = (lo + hi) / 2; - sd = sdbase + mid; - if (sd->before == offset) - return sd; - if (sd->before < offset) - lo = mid + 1; - else - hi = mid - 1; - } - if (lo == num) - return guard; - sd = sdbase + lo; - JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset)); - return sd; -} - -static void -FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt) -{ - if (jt->kids[JT_LEFT]) - FreeJumpTargets(cg, jt->kids[JT_LEFT]); - if (jt->kids[JT_RIGHT]) - FreeJumpTargets(cg, jt->kids[JT_RIGHT]); - jt->kids[JT_LEFT] = cg->jtFreeList; - cg->jtFreeList = jt; -} - -static JSBool -OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg) -{ - jsbytecode *pc, *oldpc, *base, *limit, *next; - JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard; - ptrdiff_t offset, growth, delta, top, pivot, span, length, target; - JSBool done; - JSOp op; - uint32 type; - size_t size, incr; - jssrcnote *sn, *snlimit; - JSSrcNoteSpec *spec; - uintN i, n, noteIndex; - JSTryNote *tn, *tnlimit; -#ifdef DEBUG_brendan - int passes = 0; -#endif - - base = CG_BASE(cg); - sdbase = cg->spanDeps; - sdlimit = sdbase + cg->numSpanDeps; - offset = CG_OFFSET(cg); - growth = 0; - - do { - done = JS_TRUE; - delta = 0; - top = pivot = -1; - sdtop = NULL; - pc = NULL; - op = JSOP_NOP; - type = 0; -#ifdef DEBUG_brendan - passes++; -#endif - - for (sd = sdbase; sd < sdlimit; sd++) { - JS_ASSERT(JT_HAS_TAG(sd->target)); - sd->offset += delta; - - if (sd->top != top) { - sdtop = sd; - top = sd->top; - JS_ASSERT(top == sd->before); - pivot = sd->offset; - pc = base + top; - op = (JSOp) *pc; - type = (js_CodeSpec[op].format & JOF_TYPEMASK); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { - /* - * We already extended all the jump offset operands for - * the opcode at sd->top. Jumps and branches have only - * one jump offset operand, but switches have many, all - * of which are adjacent in cg->spanDeps. - */ - continue; - } - - JS_ASSERT(type == JOF_JUMP || - type == JOF_TABLESWITCH || - type == JOF_LOOKUPSWITCH); - } - - if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = SD_SPAN(sd, pivot); - if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { - ptrdiff_t deltaFromTop = 0; - - done = JS_FALSE; - - switch (op) { - case JSOP_GOTO: op = JSOP_GOTOX; break; - case JSOP_IFEQ: op = JSOP_IFEQX; break; - case JSOP_IFNE: op = JSOP_IFNEX; break; - case JSOP_OR: op = JSOP_ORX; break; - case JSOP_AND: op = JSOP_ANDX; break; - case JSOP_GOSUB: op = JSOP_GOSUBX; break; - case JSOP_CASE: op = JSOP_CASEX; break; - case JSOP_DEFAULT: op = JSOP_DEFAULTX; break; - case JSOP_TABLESWITCH: op = JSOP_TABLESWITCHX; break; - case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break; - default: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - *pc = (jsbytecode) op; - - for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) { - if (sd2 <= sd) { - /* - * sd2->offset already includes delta as it stood - * before we entered this loop, but it must also - * include the delta relative to top due to all the - * extended jump offset immediates for the opcode - * starting at top, which we extend in this loop. - * - * If there is only one extended jump offset, then - * sd2->offset won't change and this for loop will - * iterate once only. - */ - sd2->offset += deltaFromTop; - deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; - } else { - /* - * sd2 comes after sd, and won't be revisited by - * the outer for loop, so we have to increase its - * offset by delta, not merely by deltaFromTop. - */ - sd2->offset += delta; - } - - delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN; - UpdateJumpTargets(cg->jumpTargets, sd2->offset, - JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); - } - sd = sd2 - 1; - } - } - } - - growth += delta; - } while (!done); - - if (growth) { -#ifdef DEBUG_brendan - printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n", - cg->filename ? cg->filename : "stdin", cg->firstLine, - growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps, - passes, offset + growth, offset, growth); -#endif - - /* - * Ensure that we have room for the extended jumps, but don't round up - * to a power of two -- we're done generating code, so we cut to fit. - */ - limit = CG_LIMIT(cg); - length = offset + growth; - next = base + length; - if (next > limit) { - JS_ASSERT(length > BYTECODE_CHUNK); - size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode)); - incr = BYTECODE_SIZE(length) - size; - JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr); - if (!base) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - CG_BASE(cg) = base; - CG_LIMIT(cg) = next = base + length; - } - CG_NEXT(cg) = next; - - /* - * Set up a fake span dependency record to guard the end of the code - * being generated. This guard record is returned as a fencepost by - * FindNearestSpanDep if there is no real spandep at or above a given - * unextended code offset. - */ - guard.top = -1; - guard.offset = offset + growth; - guard.before = offset; - guard.target = NULL; - } - - /* - * Now work backwards through the span dependencies, copying chunks of - * bytecode between each extended jump toward the end of the grown code - * space, and restoring immediate offset operands for all jump bytecodes. - * The first chunk of bytecodes, starting at base and ending at the first - * extended jump offset (NB: this chunk includes the operation bytecode - * just before that immediate jump offset), doesn't need to be copied. - */ - JS_ASSERT(sd == sdlimit); - top = -1; - while (--sd >= sdbase) { - if (sd->top != top) { - top = sd->top; - op = (JSOp) base[top]; - type = (js_CodeSpec[op].format & JOF_TYPEMASK); - - for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--) - continue; - sd2++; - pivot = sd2->offset; - JS_ASSERT(top == sd2->before); - } - - oldpc = base + sd->before; - span = SD_SPAN(sd, pivot); - - /* - * If this jump didn't need to be extended, restore its span immediate - * offset operand now, overwriting the index of sd within cg->spanDeps - * that was stored temporarily after *pc when BuildSpanDepTable ran. - * - * Note that span might fit in 16 bits even for an extended jump op, - * if the op has multiple span operands, not all of which overflowed - * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in - * range for a short jump, but others are not). - */ - if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) { - JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX); - SET_JUMP_OFFSET(oldpc, span); - continue; - } - - /* - * Set up parameters needed to copy the next run of bytecode starting - * at offset (which is a cursor into the unextended, original bytecode - * vector), down to sd->before (a cursor of the same scale as offset, - * it's the index of the original jump pc). Reuse delta to count the - * nominal number of bytes to copy. - */ - pc = base + sd->offset; - delta = offset - sd->before; - JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN); - - /* - * Don't bother copying the jump offset we're about to reset, but do - * copy the bytecode at oldpc (which comes just before its immediate - * jump offset operand), on the next iteration through the loop, by - * including it in offset's new value. - */ - offset = sd->before + 1; - size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN)); - if (size) { - memmove(pc + 1 + JUMPX_OFFSET_LEN, - oldpc + 1 + JUMP_OFFSET_LEN, - size); - } - - SET_JUMPX_OFFSET(pc, span); - } - - if (growth) { - /* - * Fix source note deltas. Don't hardwire the delta fixup adjustment, - * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN - * at each sd that moved. The future may bring different offset sizes - * for span-dependent instruction operands. However, we fix only main - * notes here, not prolog notes -- we know that prolog opcodes are not - * span-dependent, and aren't likely ever to be. - */ - offset = growth = 0; - sd = sdbase; - for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount; - sn < snlimit; - sn = SN_NEXT(sn)) { - /* - * Recall that the offset of a given note includes its delta, and - * tells the offset of the annotated bytecode from the main entry - * point of the script. - */ - offset += SN_DELTA(sn); - while (sd < sdlimit && sd->before < offset) { - /* - * To compute the delta to add to sn, we need to look at the - * spandep after sd, whose offset - (before + growth) tells by - * how many bytes sd's instruction grew. - */ - sd2 = sd + 1; - if (sd2 == sdlimit) - sd2 = &guard; - delta = sd2->offset - (sd2->before + growth); - if (delta > 0) { - JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN); - sn = js_AddToSrcNoteDelta(cx, cg, sn, delta); - if (!sn) - return JS_FALSE; - snlimit = cg->main.notes + cg->main.noteCount; - growth += delta; - } - sd++; - } - - /* - * If sn has span-dependent offset operands, check whether each - * covers further span-dependencies, and increase those operands - * accordingly. Some source notes measure offset not from the - * annotated pc, but from that pc plus some small bias. NB: we - * assume that spec->offsetBias can't itself span span-dependent - * instructions! - */ - spec = &js_SrcNoteSpec[SN_TYPE(sn)]; - if (spec->isSpanDep) { - pivot = offset + spec->offsetBias; - n = spec->arity; - for (i = 0; i < n; i++) { - span = js_GetSrcNoteOffset(sn, i); - if (span == 0) - continue; - target = pivot + span * spec->isSpanDep; - sd2 = FindNearestSpanDep(cg, target, - (target >= pivot) - ? sd - sdbase - : 0, - &guard); - - /* - * Increase target by sd2's before-vs-after offset delta, - * which is absolute (i.e., relative to start of script, - * as is target). Recompute the span by subtracting its - * adjusted pivot from target. - */ - target += sd2->offset - sd2->before; - span = target - (pivot + growth); - span *= spec->isSpanDep; - noteIndex = sn - cg->main.notes; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span)) - return JS_FALSE; - sn = cg->main.notes + noteIndex; - snlimit = cg->main.notes + cg->main.noteCount; - } - } - } - cg->main.lastNoteOffset += growth; - - /* - * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's - * not clear how we can beat that). - */ - for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) { - /* - * First, look for the nearest span dependency at/above tn->start. - * There may not be any such spandep, in which case the guard will - * be returned. - */ - offset = tn->start; - sd = FindNearestSpanDep(cg, offset, 0, &guard); - delta = sd->offset - sd->before; - tn->start = offset + delta; - - /* - * Next, find the nearest spandep at/above tn->start + tn->length. - * Use its delta minus tn->start's delta to increase tn->length. - */ - length = tn->length; - sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard); - if (sd2 != sd) - tn->length = length + sd2->offset - sd2->before - delta; - - /* - * Finally, adjust tn->catchStart upward only if it is non-zero, - * and provided there are spandeps below it that grew. - */ - offset = tn->catchStart; - if (offset != 0) { - sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard); - tn->catchStart = offset + sd->offset - sd->before; - } - } - } - -#ifdef DEBUG_brendan - { - uintN bigspans = 0; - top = -1; - for (sd = sdbase; sd < sdlimit; sd++) { - offset = sd->offset; - - /* NB: sd->top cursors into the original, unextended bytecode vector. */ - if (sd->top != top) { - JS_ASSERT(top == -1 || - !JOF_TYPE_IS_EXTENDED_JUMP(type) || - bigspans != 0); - bigspans = 0; - top = sd->top; - JS_ASSERT(top == sd->before); - op = (JSOp) base[offset]; - type = (js_CodeSpec[op].format & JOF_TYPEMASK); - JS_ASSERT(type == JOF_JUMP || - type == JOF_JUMPX || - type == JOF_TABLESWITCH || - type == JOF_TABLESWITCHX || - type == JOF_LOOKUPSWITCH || - type == JOF_LOOKUPSWITCHX); - pivot = offset; - } - - pc = base + offset; - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) { - span = GET_JUMPX_OFFSET(pc); - if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) { - bigspans++; - } else { - JS_ASSERT(type == JOF_TABLESWITCHX || - type == JOF_LOOKUPSWITCHX); - } - } else { - span = GET_JUMP_OFFSET(pc); - } - JS_ASSERT(SD_SPAN(sd, pivot) == span); - } - JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0); - } -#endif - - /* - * Reset so we optimize at most once -- cg may be used for further code - * generation of successive, independent, top-level statements. No jump - * can span top-level statements, because JS lacks goto. - */ - size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps))); - JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps, - JS_MAX(size, SPANDEPS_SIZE_MIN)); - cg->spanDeps = NULL; - FreeJumpTargets(cg, cg->jumpTargets); - cg->jumpTargets = NULL; - cg->numSpanDeps = cg->numJumpTargets = 0; - cg->spanDepTodo = CG_OFFSET(cg); - return JS_TRUE; -} - -static JSBool -EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off) -{ - JSBool extend; - ptrdiff_t jmp; - jsbytecode *pc; - - extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off; - if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg)) - return JS_FALSE; - - jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off)); - if (jmp >= 0 && (extend || cg->spanDeps)) { - pc = CG_CODE(cg, jmp); - if (!AddSpanDep(cx, cg, pc, pc, off)) - return JS_FALSE; - } - return jmp; -} - -static ptrdiff_t -GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc) -{ - JSSpanDep *sd; - JSJumpTarget *jt; - ptrdiff_t top; - - if (!cg->spanDeps) - return GET_JUMP_OFFSET(pc); - - sd = GetSpanDep(cg, pc); - jt = sd->target; - if (!JT_HAS_TAG(jt)) - return JT_TO_BPDELTA(jt); - - top = sd->top; - while (--sd >= cg->spanDeps && sd->top == top) - continue; - sd++; - return JT_CLR_TAG(jt)->offset - sd->offset; -} - -JSBool -js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t off) -{ - if (!cg->spanDeps) { - if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) { - SET_JUMP_OFFSET(pc, off); - return JS_TRUE; - } - - if (!BuildSpanDepTable(cx, cg)) - return JS_FALSE; - } - - return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off); -} - -JSBool -js_InStatement(JSTreeContext *tc, JSStmtType type) -{ - JSStmtInfo *stmt; - - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == type) - return JS_TRUE; - } - return JS_FALSE; -} - -JSBool -js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp) -{ - JSStmtInfo *stmt; - JSObject *obj; - JSScope *scope; - - *loopyp = JS_FALSE; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == STMT_WITH) - return JS_FALSE; - if (STMT_IS_LOOP(stmt)) { - *loopyp = JS_TRUE; - continue; - } - if (stmt->flags & SIF_SCOPE) { - obj = ATOM_TO_OBJECT(stmt->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - scope = OBJ_SCOPE(obj); - if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom))) - return JS_FALSE; - } - } - return JS_TRUE; -} - -void -js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, - ptrdiff_t top) -{ - stmt->type = type; - stmt->flags = 0; - SET_STATEMENT_TOP(stmt, top); - stmt->atom = NULL; - stmt->down = tc->topStmt; - tc->topStmt = stmt; - if (STMT_LINKS_SCOPE(stmt)) { - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - } else { - stmt->downScope = NULL; - } -} - -void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, - ptrdiff_t top) -{ - JSObject *blockObj; - - js_PushStatement(tc, stmt, STMT_BLOCK, top); - stmt->flags |= SIF_SCOPE; - blockObj = ATOM_TO_OBJECT(blockAtom); - blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); - stmt->downScope = tc->topScopeStmt; - tc->topScopeStmt = stmt; - tc->blockChain = blockObj; - stmt->atom = blockAtom; -} - -/* - * Emit a backpatch op with offset pointing to the previous jump of this type, - * so that we can walk back up the chain fixing up the op and jump offset. - */ -static ptrdiff_t -EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp) -{ - ptrdiff_t offset, delta; - - offset = CG_OFFSET(cg); - delta = offset - *lastp; - *lastp = offset; - JS_ASSERT(delta > 0); - return EmitJump(cx, cg, op, delta); -} - -/* - * Macro to emit a bytecode followed by a uint16 immediate operand stored in - * big-endian order, used for arg and var numbers as well as for atomIndexes. - * NB: We use cx and cg from our caller's lexical environment, and return - * false on error. - */ -#define EMIT_UINT16_IMM_OP(op, i) \ - JS_BEGIN_MACRO \ - if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \ - return JS_FALSE; \ - JS_END_MACRO - -/* Emit additional bytecode(s) for non-local jumps. */ -static JSBool -EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, - JSOp *returnop) -{ - intN depth; - JSStmtInfo *stmt; - ptrdiff_t jmp; - - /* - * Return from within a try block that has a finally clause must be split - * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval; - * and JSOP_RETRVAL, which makes control flow go back to the caller, who - * picks up fp->rval as usual. Otherwise, the stack will be unbalanced - * when executing the finally clause. - * - * We mutate *returnop once only if we find an enclosing try-block (viz, - * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one - * or more JSOP_GOSUBs and other fixup opcodes emitted by this function. - * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop. - * The fixup opcodes and gosubs must interleave in the proper order, from - * inner statement to outer, so that finally clauses run at the correct - * stack depth. - */ - if (returnop) { - JS_ASSERT(*returnop == JSOP_RETURN); - for (stmt = cg->treeContext.topStmt; stmt != toStmt; - stmt = stmt->down) { - if (stmt->type == STMT_FINALLY || - ((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) && - STMT_MAYBE_SCOPE(stmt))) { - if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0) - return JS_FALSE; - *returnop = JSOP_RETRVAL; - break; - } - } - - /* - * If there are no try-with-finally blocks open around this return - * statement, we can generate a return forthwith and skip generating - * any fixup code. - */ - if (*returnop == JSOP_RETURN) - return JS_TRUE; - } - - /* - * The non-local jump fixup we emit will unbalance cg->stackDepth, because - * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the - * end of a with statement, so we save cg->stackDepth here and restore it - * just before a successful return. - */ - depth = cg->stackDepth; - for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) { - switch (stmt->type) { - case STMT_FINALLY: - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)); - if (jmp < 0) - return JS_FALSE; - break; - - case STMT_WITH: - /* There's a With object on the stack that we need to pop. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - break; - - case STMT_FOR_IN_LOOP: - /* - * The iterator and the object being iterated need to be popped. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) - return JS_FALSE; - break; - - case STMT_SUBROUTINE: - /* - * There's a [exception or hole, retsub pc-index] pair on the - * stack that we need to pop. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP2) < 0) - return JS_FALSE; - break; - - default:; - } - - if (stmt->flags & SIF_SCOPE) { - uintN i; - - /* There is a Block object with locals on the stack to pop. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - i = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(stmt->atom)); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i); - } - } - - cg->stackDepth = depth; - return JS_TRUE; -} - -static ptrdiff_t -EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt, - ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType) -{ - intN index; - - if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL)) - return -1; - - if (label) - index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label)); - else if (noteType != SRC_NULL) - index = js_NewSrcNote(cx, cg, noteType); - else - index = 0; - if (index < 0) - return -1; - - return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp); -} - -static JSBool -BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last, - jsbytecode *target, jsbytecode op) -{ - jsbytecode *pc, *stop; - ptrdiff_t delta, span; - - pc = CG_CODE(cg, last); - stop = CG_CODE(cg, -1); - while (pc != stop) { - delta = GetJumpOffset(cg, pc); - span = PTRDIFF(target, pc, jsbytecode); - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span); - - /* - * Set *pc after jump offset in case bpdelta didn't overflow, but span - * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable - * and need to see the JSOP_BACKPATCH* op at *pc). - */ - *pc = op; - pc -= delta; - } - return JS_TRUE; -} - -void -js_PopStatement(JSTreeContext *tc) -{ - JSStmtInfo *stmt; - JSObject *blockObj; - - stmt = tc->topStmt; - tc->topStmt = stmt->down; - if (STMT_LINKS_SCOPE(stmt)) { - tc->topScopeStmt = stmt->downScope; - if (stmt->flags & SIF_SCOPE) { - blockObj = ATOM_TO_OBJECT(stmt->atom); - tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]); - } - } -} - -JSBool -js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg) -{ - JSStmtInfo *stmt; - - stmt = cg->treeContext.topStmt; - if (!STMT_IS_TRYING(stmt) && - (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) || - !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update), - JSOP_GOTO))) { - return JS_FALSE; - } - js_PopStatement(&cg->treeContext); - return JS_TRUE; -} - -JSBool -js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - JSParseNode *pn) -{ - jsdouble dval; - jsint ival; - JSAtom *valueAtom; - JSAtomListElement *ale; - - /* XXX just do numbers for now */ - if (pn->pn_type == TOK_NUMBER) { - dval = pn->pn_dval; - valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) - ? js_AtomizeInt(cx, ival, 0) - : js_AtomizeDouble(cx, dval, 0); - if (!valueAtom) - return JS_FALSE; - ale = js_IndexAtom(cx, atom, &cg->constList); - if (!ale) - return JS_FALSE; - ALE_SET_VALUE(ale, ATOM_KEY(valueAtom)); - } - return JS_TRUE; -} - -JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl) -{ - JSStmtInfo *stmt; - JSObject *obj; - JSScope *scope; - JSScopeProperty *sprop; - jsval v; - - for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) { - if (stmt->type == STMT_WITH) { - /* Ignore with statements enclosing a single let declaration. */ - if (letdecl) - continue; - break; - } - - /* Skip "maybe scope" statements that don't contain let bindings. */ - if (!(stmt->flags & SIF_SCOPE)) - continue; - - obj = ATOM_TO_OBJECT(stmt->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - scope = OBJ_SCOPE(obj); - sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); - if (sprop) { - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - - if (slotp) { - /* - * Use LOCKED_OBJ_GET_SLOT since we know obj is single- - * threaded and owned by this compiler activation. - */ - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH); - JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0); - *slotp = JSVAL_TO_INT(v) + sprop->shortid; - } - return stmt; - } - } - - if (slotp) - *slotp = -1; - return stmt; -} - -JSBool -js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - jsval *vp) -{ - JSBool ok; - JSStackFrame *fp; - JSStmtInfo *stmt; - jsint slot; - JSAtomListElement *ale; - JSObject *obj, *pobj; - JSProperty *prop; - uintN attrs; - - /* - * fp chases cg down the stack, but only until we reach the outermost cg. - * This enables propagating consts from top-level into switch cases in a - * function compiled along with the top-level script. All stack frames - * with matching code generators should be flagged with JSFRAME_COMPILING; - * we check sanity here. - */ - *vp = JSVAL_VOID; - ok = JS_TRUE; - fp = cx->fp; - do { - JS_ASSERT(fp->flags & JSFRAME_COMPILING); - - obj = fp->varobj; - if (obj == fp->scopeChain) { - /* XXX this will need revising when 'let const' is added. */ - stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE); - if (stmt) - return JS_TRUE; - - ATOM_LIST_SEARCH(ale, &cg->constList, atom); - if (ale) { - *vp = ALE_VALUE(ale); - return JS_TRUE; - } - - /* - * Try looking in the variable object for a direct property that - * is readonly and permanent. We know such a property can't be - * shadowed by another property on obj's prototype chain, or a - * with object or catch variable; nor can prop's value be changed, - * nor can prop be deleted. - */ - prop = NULL; - if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) { - ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &pobj, &prop); - if (!ok) - break; - if (prop) { -#ifdef DEBUG - JSScopeProperty *sprop = (JSScopeProperty *)prop; - - /* - * Any hidden property must be a formal arg or local var, - * which will shadow a global const of the same name. - */ - JS_ASSERT(sprop->getter == js_GetArgument || - sprop->getter == js_GetLocalVariable); -#endif - OBJ_DROP_PROPERTY(cx, pobj, prop); - break; - } - } - - ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - if (ok) { - if (pobj == obj && - (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { - /* - * We're compiling code that will be executed immediately, - * not re-executed against a different scope chain and/or - * variable object. Therefore we can get constant values - * from our variable object here. - */ - ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop, - &attrs); - if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT))) - ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp); - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - if (!ok || prop) - break; - } - fp = fp->down; - } while ((cg = cg->parent) != NULL); - return ok; -} - -/* - * Allocate an index invariant for all activations of the code being compiled - * in cg, that can be used to store and fetch a reference to a cloned RegExp - * object that shares the same JSRegExp private data created for the object - * literal in pn->pn_atom. We need clones to hold lastIndex and other direct - * properties that should not be shared among threads sharing a precompiled - * function or script. - * - * If the code being compiled is function code, allocate a reserved slot in - * the cloned function object that shares its precompiled script with other - * cloned function objects and with the compiler-created clone-parent. There - * are fun->nregexps such reserved slots in each function object cloned from - * fun->object. NB: during compilation, funobj slots must never be allocated, - * because js_AllocSlot could hand out one of the slots that should be given - * to a regexp clone. - * - * If the code being compiled is global code, reserve the fp->vars slot at - * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least - * one more than this index. For global code, fp->vars is parallel to the - * global script->atomMap.vector array, but possibly shorter for the common - * case (where var declarations and regexp literals cluster toward the front - * of the script or function body). - * - * Global variable name literals in script->atomMap have fast-global slot - * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array - * element. The atomIndex for a regexp object literal thus also addresses an - * fp->vars element that is not used by any optimized global variable, so we - * use that GC-scanned element to keep the regexp object clone alive, as well - * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode. - * - * In no case can cx->fp->varobj be a Call object here, because that implies - * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL) - * is true, and js_GetToken will have already selected JSOP_OBJECT instead of - * JSOP_REGEXP, to avoid all this RegExp object cloning business. - * - * Why clone regexp objects? ECMA specifies that when a regular expression - * literal is scanned, a RegExp object is created. In the spec, compilation - * and execution happen indivisibly, but in this implementation and many of - * its embeddings, code is precompiled early and re-executed in multiple - * threads, or using multiple global objects, or both, for efficiency. - * - * In such cases, naively following ECMA leads to wrongful sharing of RegExp - * objects, which makes for collisions on the lastIndex property (especially - * for global regexps) and on any ad-hoc properties. Also, __proto__ and - * __parent__ refer to the pre-compilation prototype and global objects, a - * pigeon-hole problem for instanceof tests. - */ -static JSBool -IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale, - JSCodeGenerator *cg) -{ - JSObject *varobj, *reobj; - JSClass *clasp; - JSFunction *fun; - JSRegExp *re; - uint16 *countPtr; - uintN cloneIndex; - - JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))); - - varobj = cx->fp->varobj; - clasp = OBJ_GET_CLASS(cx, varobj); - if (clasp == &js_FunctionClass) { - fun = (JSFunction *) JS_GetPrivate(cx, varobj); - countPtr = &fun->u.i.nregexps; - cloneIndex = *countPtr; - } else { - JS_ASSERT(clasp != &js_CallClass); - countPtr = &cg->treeContext.numGlobalVars; - cloneIndex = ALE_INDEX(ale); - } - - if ((cloneIndex + 1) >> 16) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NEED_DIET, js_script_str); - return JS_FALSE; - } - if (cloneIndex >= *countPtr) - *countPtr = cloneIndex + 1; - - reobj = ATOM_TO_OBJECT(pn->pn_atom); - JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass); - re = (JSRegExp *) JS_GetPrivate(cx, reobj); - re->cloneIndex = cloneIndex; - return JS_TRUE; -} - -/* - * Emit a bytecode and its 2-byte constant (atom) index immediate operand. - * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit - * immediate operand indexes the atom in script->atomMap. - * - * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in - * the scope chain in which the literal name was found, followed by the name - * as a string. This enables us to use the JOF_ELEM counterpart to op. - * - * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push - * the atom's value key. For JOF_PROP ops, the object being operated on has - * already been pushed, and JSOP_LITERAL will push the id, leaving the stack - * in the proper state for a JOF_ELEM counterpart. - * - * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special - * dispatch on op, but getting op's atom index from the stack instead of from - * an unsigned 16-bit immediate operand. - */ -static JSBool -EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg) -{ - uint32 mode; - JSOp prefixOp; - ptrdiff_t off; - jsbytecode *pc; - - if (atomIndex >= JS_BIT(16)) { - mode = (js_CodeSpec[op].format & JOF_MODEMASK); - if (op != JSOP_SETNAME) { - prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) || -#if JS_HAS_XML_SUPPORT - op == JSOP_GETMETHOD || - op == JSOP_SETMETHOD || -#endif - op == JSOP_SETCONST) - ? JSOP_LITOPX - : (mode == JOF_NAME) - ? JSOP_FINDNAME - : JSOP_LITERAL; - off = js_EmitN(cx, cg, prefixOp, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_LITERAL_INDEX(pc, atomIndex); - } - - switch (op) { - case JSOP_DECNAME: op = JSOP_DECELEM; break; - case JSOP_DECPROP: op = JSOP_DECELEM; break; - case JSOP_DELNAME: op = JSOP_DELELEM; break; - case JSOP_DELPROP: op = JSOP_DELELEM; break; - case JSOP_FORNAME: op = JSOP_FORELEM; break; - case JSOP_FORPROP: op = JSOP_FORELEM; break; - case JSOP_GETPROP: op = JSOP_GETELEM; break; - case JSOP_GETXPROP: op = JSOP_GETXELEM; break; - case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break; - case JSOP_INCNAME: op = JSOP_INCELEM; break; - case JSOP_INCPROP: op = JSOP_INCELEM; break; - case JSOP_INITPROP: op = JSOP_INITELEM; break; - case JSOP_NAME: op = JSOP_GETELEM; break; - case JSOP_NAMEDEC: op = JSOP_ELEMDEC; break; - case JSOP_NAMEINC: op = JSOP_ELEMINC; break; - case JSOP_PROPDEC: op = JSOP_ELEMDEC; break; - case JSOP_PROPINC: op = JSOP_ELEMINC; break; - case JSOP_BINDNAME: return JS_TRUE; - case JSOP_SETNAME: op = JSOP_SETELEM; break; - case JSOP_SETPROP: op = JSOP_SETELEM; break; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: - ReportStatementTooLarge(cx, cg); - return JS_FALSE; -#endif - default: -#if JS_HAS_XML_SUPPORT - JS_ASSERT(mode == 0 || op == JSOP_SETCONST || - op == JSOP_GETMETHOD || op == JSOP_SETMETHOD); -#else - JS_ASSERT(mode == 0 || op == JSOP_SETCONST); -#endif - break; - } - - return js_Emit1(cx, cg, op) >= 0; - } - - EMIT_UINT16_IMM_OP(op, atomIndex); - return JS_TRUE; -} - -/* - * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro - * caller's lexical environment, and embedding a false return on error. - * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?! - */ -#define EMIT_ATOM_INDEX_OP(op, atomIndex) \ - JS_BEGIN_MACRO \ - if (!EmitAtomIndexOp(cx, op, atomIndex, cg)) \ - return JS_FALSE; \ - JS_END_MACRO - -static JSBool -EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - JSAtomListElement *ale; - - ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg)) - return JS_FALSE; - return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg); -} - -/* - * This routine tries to optimize name gets and sets to stack slot loads and - * stores, given the variables object and scope chain in cx's top frame, the - * compile-time context in tc, and a TOK_NAME node pn. It returns false on - * error, true on success. - * - * The caller can inspect pn->pn_slot for a non-negative slot number to tell - * whether optimization occurred, in which case BindNameToSlot also updated - * pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless - * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether - * or not pn->pn_op was modified, if this function finds an argument or local - * variable name, pn->pn_attrs will contain the property's attributes after a - * successful return. - * - * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget - * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases - * in js_EmitTree. - */ -static JSBool -BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, - JSBool letdecl) -{ - JSAtom *atom; - JSStmtInfo *stmt; - jsint slot; - JSOp op; - JSStackFrame *fp; - JSObject *obj, *pobj; - JSClass *clasp; - JSBool optimizeGlobals; - JSPropertyOp getter; - uintN attrs; - JSAtomListElement *ale; - JSProperty *prop; - JSScopeProperty *sprop; - - JS_ASSERT(pn->pn_type == TOK_NAME); - if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS) - return JS_TRUE; - - /* QNAME references can never be optimized to use arg/var storage. */ - if (pn->pn_op == JSOP_QNAMEPART) - return JS_TRUE; - - /* - * We can't optimize if we are compiling a with statement and its body, - * or we're in a catch block whose exception variable has the same name - * as this node. FIXME: we should be able to optimize catch vars to be - * block-locals. - */ - atom = pn->pn_atom; - stmt = js_LexicalLookup(tc, atom, &slot, letdecl); - if (stmt) { - if (stmt->type == STMT_WITH) - return JS_TRUE; - - JS_ASSERT(stmt->flags & SIF_SCOPE); - JS_ASSERT(slot >= 0); - op = pn->pn_op; - switch (op) { - case JSOP_NAME: op = JSOP_GETLOCAL; break; - case JSOP_SETNAME: op = JSOP_SETLOCAL; break; - case JSOP_INCNAME: op = JSOP_INCLOCAL; break; - case JSOP_NAMEINC: op = JSOP_LOCALINC; break; - case JSOP_DECNAME: op = JSOP_DECLOCAL; break; - case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break; - case JSOP_FORNAME: op = JSOP_FORLOCAL; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - if (op != pn->pn_op) { - pn->pn_op = op; - pn->pn_slot = slot; - } - return JS_TRUE; - } - - /* - * A Script object can be used to split an eval into a compile step done - * at construction time, and an execute step done separately, possibly in - * a different scope altogether. We therefore cannot do any name-to-slot - * optimizations, but must lookup names at runtime. Note that script_exec - * ensures that its caller's frame has a Call object, so arg and var name - * lookups will succeed. - */ - fp = cx->fp; - if (fp->flags & JSFRAME_SCRIPT_OBJECT) - return JS_TRUE; - - /* - * We can't optimize if var and closure (a local function not in a larger - * expression and not at top-level within another's body) collide. - * XXX suboptimal: keep track of colliding names and deoptimize only those - */ - if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) - return JS_TRUE; - - /* - * We can't optimize if we're not compiling a function body, whether via - * eval, or directly when compiling a function statement or expression. - */ - obj = fp->varobj; - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp != &js_FunctionClass && clasp != &js_CallClass) { - /* Check for an eval or debugger frame. */ - if (fp->flags & JSFRAME_SPECIAL) - return JS_TRUE; - - /* - * Optimize global variable accesses if there are at least 100 uses - * in unambiguous contexts, or failing that, if least half of all the - * uses of global vars/consts/functions are in loops. - */ - optimizeGlobals = (tc->globalUses >= 100 || - (tc->loopyGlobalUses && - tc->loopyGlobalUses >= tc->globalUses / 2)); - if (!optimizeGlobals) - return JS_TRUE; - } else { - optimizeGlobals = JS_FALSE; - } - - /* - * We can't optimize if we are in an eval called inside a with statement. - */ - if (fp->scopeChain != obj) - return JS_TRUE; - - op = pn->pn_op; - getter = NULL; -#ifdef __GNUC__ - attrs = slot = 0; /* quell GCC overwarning */ -#endif - if (optimizeGlobals) { - /* - * We are optimizing global variables, and there is no pre-existing - * global property named atom. If atom was declared via const or var, - * optimize pn to access fp->vars using the appropriate JOF_QVAR op. - */ - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (!ale) { - /* Use precedes declaration, or name is never declared. */ - return JS_TRUE; - } - - attrs = (ALE_JSOP(ale) == JSOP_DEFCONST) - ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT - : JSPROP_ENUMERATE | JSPROP_PERMANENT; - - /* Index atom so we can map fast global number to name. */ - JS_ASSERT(tc->flags & TCF_COMPILING); - ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList); - if (!ale) - return JS_FALSE; - - /* Defend against tc->numGlobalVars 16-bit overflow. */ - slot = ALE_INDEX(ale); - if ((slot + 1) >> 16) - return JS_TRUE; - - if ((uint16)(slot + 1) > tc->numGlobalVars) - tc->numGlobalVars = (uint16)(slot + 1); - } else { - /* - * We may be able to optimize name to stack slot. Look for an argument - * or variable property in the function, or its call object, not found - * in any prototype object. Rewrite pn_op and update pn accordingly. - * NB: We know that JSOP_DELNAME on an argument or variable evaluates - * to false, due to JSPROP_PERMANENT. - */ - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - sprop = (JSScopeProperty *) prop; - if (sprop) { - if (pobj == obj) { - getter = sprop->getter; - attrs = sprop->attrs; - slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - } - - if (optimizeGlobals || getter) { - if (optimizeGlobals) { - switch (op) { - case JSOP_NAME: op = JSOP_GETGVAR; break; - case JSOP_SETNAME: op = JSOP_SETGVAR; break; - case JSOP_SETCONST: /* NB: no change */ break; - case JSOP_INCNAME: op = JSOP_INCGVAR; break; - case JSOP_NAMEINC: op = JSOP_GVARINC; break; - case JSOP_DECNAME: op = JSOP_DECGVAR; break; - case JSOP_NAMEDEC: op = JSOP_GVARDEC; break; - case JSOP_FORNAME: /* NB: no change */ break; - case JSOP_DELNAME: /* NB: no change */ break; - default: JS_ASSERT(0); - } - } else if (getter == js_GetLocalVariable || - getter == js_GetCallVariable) { - switch (op) { - case JSOP_NAME: op = JSOP_GETVAR; break; - case JSOP_SETNAME: op = JSOP_SETVAR; break; - case JSOP_SETCONST: op = JSOP_SETVAR; break; - case JSOP_INCNAME: op = JSOP_INCVAR; break; - case JSOP_NAMEINC: op = JSOP_VARINC; break; - case JSOP_DECNAME: op = JSOP_DECVAR; break; - case JSOP_NAMEDEC: op = JSOP_VARDEC; break; - case JSOP_FORNAME: op = JSOP_FORVAR; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - } else if (getter == js_GetArgument || - (getter == js_CallClass.getProperty && - fp->fun && (uintN) slot < fp->fun->nargs)) { - switch (op) { - case JSOP_NAME: op = JSOP_GETARG; break; - case JSOP_SETNAME: op = JSOP_SETARG; break; - case JSOP_INCNAME: op = JSOP_INCARG; break; - case JSOP_NAMEINC: op = JSOP_ARGINC; break; - case JSOP_DECNAME: op = JSOP_DECARG; break; - case JSOP_NAMEDEC: op = JSOP_ARGDEC; break; - case JSOP_FORNAME: op = JSOP_FORARG; break; - case JSOP_DELNAME: op = JSOP_FALSE; break; - default: JS_ASSERT(0); - } - } - if (op != pn->pn_op) { - pn->pn_op = op; - pn->pn_slot = slot; - } - pn->pn_attrs = attrs; - } - - if (pn->pn_slot < 0) { - /* - * We couldn't optimize pn, so it's not a global or local slot name. - * Now we must check for the predefined arguments variable. It may be - * overridden by assignment, in which case the function is heavyweight - * and the interpreter will look up 'arguments' in the function's call - * object. - */ - if (pn->pn_op == JSOP_NAME && - atom == cx->runtime->atomState.argumentsAtom) { - pn->pn_op = JSOP_ARGUMENTS; - return JS_TRUE; - } - - tc->flags |= TCF_FUN_USES_NONLOCALS; - } - return JS_TRUE; -} - -/* - * If pn contains a useful expression, return true with *answer set to true. - * If pn contains a useless expression, return true with *answer set to false. - * Return false on error. - * - * The caller should initialize *answer to false and invoke this function on - * an expression statement or similar subtree to decide whether the tree could - * produce code that has any side effects. For an expression statement, we - * define useless code as code with no side effects, because the main effect, - * the value left on the stack after the code executes, will be discarded by a - * pop bytecode. - */ -static JSBool -CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, - JSBool *answer) -{ - JSBool ok; - JSFunction *fun; - JSParseNode *pn2; - - ok = JS_TRUE; - if (!pn || *answer) - return ok; - - switch (pn->pn_arity) { - case PN_FUNC: - /* - * A named function is presumed useful: we can't yet know that it is - * not called. The side effects are the creation of a scope object - * to parent this function object, and the binding of the function's - * name in that scope object. See comments at case JSOP_NAMEDFUNOBJ: - * in jsinterp.c. - */ - fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); - if (fun->atom) - *answer = JS_TRUE; - break; - - case PN_LIST: - if (pn->pn_type == TOK_NEW || - pn->pn_type == TOK_LP || - pn->pn_type == TOK_LB || - pn->pn_type == TOK_RB || - pn->pn_type == TOK_RC) { - /* - * All invocation operations (construct: TOK_NEW, call: TOK_LP) - * are presumed to be useful, because they may have side effects - * even if their main effect (their return value) is discarded. - * - * TOK_LB binary trees of 3 or more nodes are flattened into lists - * to avoid too much recursion. All such lists must be presumed - * to be useful because each index operation could invoke a getter - * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case, - * does not apply here: arguments[i][j] might invoke a getter). - * - * Array and object initializers (TOK_RB and TOK_RC lists) must be - * considered useful, because they are sugar for constructor calls - * (to Array and Object, respectively). - */ - *answer = JS_TRUE; - } else { - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) - ok &= CheckSideEffects(cx, tc, pn2, answer); - } - break; - - case PN_TERNARY: - ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) && - CheckSideEffects(cx, tc, pn->pn_kid2, answer) && - CheckSideEffects(cx, tc, pn->pn_kid3, answer); - break; - - case PN_BINARY: - if (pn->pn_type == TOK_ASSIGN) { - /* - * Assignment is presumed to be useful, even if the next operation - * is another assignment overwriting this one's ostensible effect, - * because the left operand may be a property with a setter that - * has side effects. - * - * The only exception is assignment of a useless value to a const - * declared in the function currently being compiled. - */ - pn2 = pn->pn_left; - if (pn2->pn_type != TOK_NAME) { - *answer = JS_TRUE; - } else { - if (!BindNameToSlot(cx, tc, pn2, JS_FALSE)) - return JS_FALSE; - if (!CheckSideEffects(cx, tc, pn->pn_right, answer)) - return JS_FALSE; - if (!*answer && - (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY))) { - *answer = JS_TRUE; - } - } - } else { - if (pn->pn_type == TOK_LB) { - pn2 = pn->pn_left; - if (pn2->pn_type == TOK_NAME && - !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { - return JS_FALSE; - } - if (pn2->pn_op != JSOP_ARGUMENTS) { - /* - * Any indexed property reference could call a getter with - * side effects, except for arguments[i] where arguments is - * unambiguous. - */ - *answer = JS_TRUE; - } - } - ok = CheckSideEffects(cx, tc, pn->pn_left, answer) && - CheckSideEffects(cx, tc, pn->pn_right, answer); - } - break; - - case PN_UNARY: - if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC || - pn->pn_type == TOK_THROW || -#if JS_HAS_GENERATORS - pn->pn_type == TOK_YIELD || -#endif - pn->pn_type == TOK_DEFSHARP) { - /* All these operations have effects that we must commit. */ - *answer = JS_TRUE; - } else if (pn->pn_type == TOK_DELETE) { - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - case TOK_DOT: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - case TOK_LB: - /* All these delete addressing modes have effects too. */ - *answer = JS_TRUE; - break; - default: - ok = CheckSideEffects(cx, tc, pn2, answer); - break; - } - } else { - ok = CheckSideEffects(cx, tc, pn->pn_kid, answer); - } - break; - - case PN_NAME: - /* - * Take care to avoid trying to bind a label name (labels, both for - * statements and property values in object initialisers, have pn_op - * defaulted to JSOP_NOP). - */ - if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) { - if (!BindNameToSlot(cx, tc, pn, JS_FALSE)) - return JS_FALSE; - if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) { - /* - * Not an argument or local variable use, so this expression - * could invoke a getter that has side effects. - */ - *answer = JS_TRUE; - } - } - pn2 = pn->pn_expr; - if (pn->pn_type == TOK_DOT) { - if (pn2->pn_type == TOK_NAME && - !BindNameToSlot(cx, tc, pn2, JS_FALSE)) { - return JS_FALSE; - } - if (!(pn2->pn_op == JSOP_ARGUMENTS && - pn->pn_atom == cx->runtime->atomState.lengthAtom)) { - /* - * Any dotted property reference could call a getter, except - * for arguments.length where arguments is unambiguous. - */ - *answer = JS_TRUE; - } - } - ok = CheckSideEffects(cx, tc, pn2, answer); - break; - - case PN_NULLARY: - if (pn->pn_type == TOK_DEBUGGER) - *answer = JS_TRUE; - break; - } - return ok; -} - -/* - * Secret handshake with js_EmitTree's TOK_LP/TOK_NEW case logic, to flag all - * uses of JSOP_GETMETHOD that implicitly qualify the method property's name - * with a function:: prefix. All other JSOP_GETMETHOD and JSOP_SETMETHOD uses - * must be explicit, so we need a distinct source note (SRC_METHODBASE rather - * than SRC_PCBASE) for round-tripping through the beloved decompiler. - */ -#define JSPROP_IMPLICIT_FUNCTION_NAMESPACE 0x100 - -static jssrcnote -SrcNoteForPropOp(JSParseNode *pn, JSOp op) -{ - return ((op == JSOP_GETMETHOD && - !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) || - op == JSOP_SETMETHOD) - ? SRC_METHODBASE - : SRC_PCBASE; -} - -static JSBool -EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - JSParseNode *pn2, *pndot, *pnup, *pndown; - ptrdiff_t top; - - pn2 = pn->pn_expr; - if (op == JSOP_GETPROP && - pn->pn_type == TOK_DOT && - pn2->pn_type == TOK_NAME) { - /* Try to optimize arguments.length into JSOP_ARGCNT. */ - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - if (pn2->pn_op == JSOP_ARGUMENTS && - pn->pn_atom == cx->runtime->atomState.lengthAtom) { - return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0; - } - } - - /* - * If the object operand is also a dotted property reference, reverse the - * list linked via pn_expr temporarily so we can iterate over it from the - * bottom up (reversing again as we go), to avoid excessive recursion. - */ - if (pn2->pn_type == TOK_DOT) { - pndot = pn2; - pnup = NULL; - top = CG_OFFSET(cg); - for (;;) { - /* Reverse pndot->pn_expr to point up, not down. */ - pndot->pn_offset = top; - pndown = pndot->pn_expr; - pndot->pn_expr = pnup; - if (pndown->pn_type != TOK_DOT) - break; - pnup = pndot; - pndot = pndown; - } - - /* pndown is a primary expression, not a dotted property reference. */ - if (!js_EmitTree(cx, cg, pndown)) - return JS_FALSE; - - do { - /* Walk back up the list, emitting annotated name ops. */ - if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pndot, pndot->pn_op), - CG_OFFSET(cg) - pndown->pn_offset) < 0) { - return JS_FALSE; - } - if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg)) - return JS_FALSE; - - /* Reverse the pn_expr link again. */ - pnup = pndot->pn_expr; - pndot->pn_expr = pndown; - pndown = pndot; - } while ((pndot = pnup) != NULL); - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op), - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - if (!pn->pn_atom) { - JS_ASSERT(op == JSOP_IMPORTALL); - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else { - if (!EmitAtomOp(cx, pn, op, cg)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg) -{ - ptrdiff_t top; - JSParseNode *left, *right, *next, ltmp, rtmp; - jsint slot; - - top = CG_OFFSET(cg); - if (pn->pn_arity == PN_LIST) { - /* Left-associative operator chain to avoid too much recursion. */ - JS_ASSERT(pn->pn_op == JSOP_GETELEM || pn->pn_op == JSOP_IMPORTELEM); - JS_ASSERT(pn->pn_count >= 3); - left = pn->pn_head; - right = PN_LAST(pn); - next = left->pn_next; - JS_ASSERT(next != right); - - /* - * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by - * one or more index expression and JSOP_GETELEM op pairs. - */ - if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) - return JS_FALSE; - if (left->pn_op == JSOP_ARGUMENTS && - JSDOUBLE_IS_INT(next->pn_dval, slot) && - (jsuint)slot < JS_BIT(16)) { - left->pn_offset = next->pn_offset = top; - EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); - left = next; - next = left->pn_next; - } - } - - /* - * Check whether we generated JSOP_ARGSUB, just above, and have only - * one more index expression to emit. Given arguments[0][j], we must - * skip the while loop altogether, falling through to emit code for j - * (in the subtree referenced by right), followed by the annotated op, - * at the bottom of this function. - */ - JS_ASSERT(next != right || pn->pn_count == 3); - if (left == pn->pn_head) { - if (!js_EmitTree(cx, cg, left)) - return JS_FALSE; - } - while (next != right) { - if (!js_EmitTree(cx, cg, next)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - next = next->pn_next; - } - } else { - if (pn->pn_arity == PN_NAME) { - /* - * Set left and right so pn appears to be a TOK_LB node, instead - * of a TOK_DOT node. See the TOK_FOR/IN case in js_EmitTree, and - * EmitDestructuringOps nearer below. In the destructuring case, - * the base expression (pn_expr) of the name may be null, which - * means we have to emit a JSOP_BINDNAME. - */ - left = pn->pn_expr; - if (!left) { - left = <mp; - left->pn_type = TOK_OBJECT; - left->pn_op = JSOP_BINDNAME; - left->pn_arity = PN_NULLARY; - left->pn_pos = pn->pn_pos; - left->pn_atom = pn->pn_atom; - } - right = &rtmp; - right->pn_type = TOK_STRING; - JS_ASSERT(ATOM_IS_STRING(pn->pn_atom)); - right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom)) - ? JSOP_QNAMEPART - : JSOP_STRING; - right->pn_arity = PN_NULLARY; - right->pn_pos = pn->pn_pos; - right->pn_atom = pn->pn_atom; - } else { - JS_ASSERT(pn->pn_arity == PN_BINARY); - left = pn->pn_left; - right = pn->pn_right; - } - - /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */ - if (op == JSOP_GETELEM && - left->pn_type == TOK_NAME && - right->pn_type == TOK_NUMBER) { - if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE)) - return JS_FALSE; - if (left->pn_op == JSOP_ARGUMENTS && - JSDOUBLE_IS_INT(right->pn_dval, slot) && - (jsuint)slot < JS_BIT(16)) { - left->pn_offset = right->pn_offset = top; - EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot); - return JS_TRUE; - } - } - - if (!js_EmitTree(cx, cg, left)) - return JS_FALSE; - } - - /* The right side of the descendant operator is implicitly quoted. */ - JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING || - right->pn_op == JSOP_QNAMEPART); - if (!js_EmitTree(cx, cg, right)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - return js_Emit1(cx, cg, op) >= 0; -} - -static JSBool -EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg) -{ - jsint ival; - jsatomid atomIndex; - ptrdiff_t off; - jsbytecode *pc; - JSAtom *atom; - JSAtomListElement *ale; - - if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) { - if (ival == 0) - return js_Emit1(cx, cg, JSOP_ZERO) >= 0; - if (ival == 1) - return js_Emit1(cx, cg, JSOP_ONE) >= 0; - - atomIndex = (jsatomid)ival; - if (atomIndex < JS_BIT(16)) { - EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex); - return JS_TRUE; - } - - if (atomIndex < JS_BIT(24)) { - off = js_EmitN(cx, cg, JSOP_UINT24, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_LITERAL_INDEX(pc, atomIndex); - return JS_TRUE; - } - - atom = js_AtomizeInt(cx, ival, 0); - } else { - atom = js_AtomizeDouble(cx, dval, 0); - } - if (!atom) - return JS_FALSE; - - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg); -} - -static JSBool -EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSStmtInfo *stmtInfo) -{ - JSOp switchOp; - JSBool ok, hasDefault, constPropagated; - ptrdiff_t top, off, defaultOffset; - JSParseNode *pn2, *pn3, *pn4; - uint32 caseCount, tableLength; - JSParseNode **table; - jsdouble d; - jsint i, low, high; - jsval v; - JSAtom *atom; - JSAtomListElement *ale; - intN noteIndex; - size_t switchSize, tableSize; - jsbytecode *pc, *savepc; -#if JS_HAS_BLOCK_SCOPE - JSObject *obj; - jsint count; -#endif - - /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */ - switchOp = JSOP_TABLESWITCH; - ok = JS_TRUE; - hasDefault = constPropagated = JS_FALSE; - defaultOffset = -1; - - /* - * If the switch contains let variables scoped by its body, model the - * resulting block on the stack first, before emitting the discriminant's - * bytecode (in case the discriminant contains a stack-model dependency - * such as a let expression). - */ - pn2 = pn->pn_right; -#if JS_HAS_BLOCK_SCOPE - if (pn2->pn_type == TOK_LEXICALSCOPE) { - atom = pn2->pn_atom; - obj = ATOM_TO_OBJECT(atom); - OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); - - /* - * Push the body's block scope before discriminant code-gen for proper - * static block scope linkage in case the discriminant contains a let - * expression. The block's locals must lie under the discriminant on - * the stack so that case-dispatch bytecodes can find the discriminant - * on top of stack. - */ - js_PushBlockScope(&cg->treeContext, stmtInfo, atom, -1); - stmtInfo->type = STMT_SWITCH; - - count = OBJ_BLOCK_COUNT(cx, obj); - cg->stackDepth += count; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */ - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); - - /* - * Pop the switch's statement info around discriminant code-gen. Note - * how this leaves cg->treeContext.blockChain referencing the switch's - * block scope object, which is necessary for correct block parenting - * in the case where the discriminant contains a let expression. - */ - cg->treeContext.topStmt = stmtInfo->down; - cg->treeContext.topScopeStmt = stmtInfo->downScope; - } -#ifdef __GNUC__ - else { - atom = NULL; - count = -1; - } -#endif -#endif - - /* - * Emit code for the discriminant first (or nearly first, in the case of a - * switch whose body is a block scope). - */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - - /* Switch bytecodes run from here till end of final case. */ - top = CG_OFFSET(cg); -#if !JS_HAS_BLOCK_SCOPE - js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); -#else - if (pn2->pn_type == TOK_LC) { - js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top); - } else { - /* Re-push the switch's statement info record. */ - cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo; - - /* Set the statement info record's idea of top. */ - stmtInfo->update = top; - - /* Advance pn2 to refer to the switch case list. */ - pn2 = pn2->pn_expr; - } -#endif - - caseCount = pn2->pn_count; - tableLength = 0; - table = NULL; - - if (caseCount == 0 || - (caseCount == 1 && - (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) { - caseCount = 0; - low = 0; - high = -1; - } else { -#define INTMAP_LENGTH 256 - jsbitmap intmap_space[INTMAP_LENGTH]; - jsbitmap *intmap = NULL; - int32 intmap_bitlen = 0; - - low = JSVAL_INT_MAX; - high = JSVAL_INT_MIN; - - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) { - hasDefault = JS_TRUE; - caseCount--; /* one of the "cases" was the default */ - continue; - } - - JS_ASSERT(pn3->pn_type == TOK_CASE); - if (switchOp == JSOP_CONDSWITCH) - continue; - - pn4 = pn3->pn_left; - switch (pn4->pn_type) { - case TOK_NUMBER: - d = pn4->pn_dval; - if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { - pn3->pn_val = INT_TO_JSVAL(i); - } else { - atom = js_AtomizeDouble(cx, d, 0); - if (!atom) { - ok = JS_FALSE; - goto release; - } - pn3->pn_val = ATOM_KEY(atom); - } - break; - case TOK_STRING: - pn3->pn_val = ATOM_KEY(pn4->pn_atom); - break; - case TOK_NAME: - if (!pn4->pn_expr) { - ok = js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v); - if (!ok) - goto release; - if (!JSVAL_IS_VOID(v)) { - pn3->pn_val = v; - constPropagated = JS_TRUE; - break; - } - } - /* FALL THROUGH */ - case TOK_PRIMARY: - if (pn4->pn_op == JSOP_TRUE) { - pn3->pn_val = JSVAL_TRUE; - break; - } - if (pn4->pn_op == JSOP_FALSE) { - pn3->pn_val = JSVAL_FALSE; - break; - } - /* FALL THROUGH */ - default: - switchOp = JSOP_CONDSWITCH; - continue; - } - - JS_ASSERT(JSVAL_IS_NUMBER(pn3->pn_val) || - JSVAL_IS_STRING(pn3->pn_val) || - JSVAL_IS_BOOLEAN(pn3->pn_val)); - - if (switchOp != JSOP_TABLESWITCH) - continue; - if (!JSVAL_IS_INT(pn3->pn_val)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - i = JSVAL_TO_INT(pn3->pn_val); - if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - if (i < low) - low = i; - if (high < i) - high = i; - - /* - * Check for duplicates, which require a JSOP_LOOKUPSWITCH. - * We bias i by 65536 if it's negative, and hope that's a rare - * case (because it requires a malloc'd bitmap). - */ - if (i < 0) - i += JS_BIT(16); - if (i >= intmap_bitlen) { - if (!intmap && - i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) { - intmap = intmap_space; - intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2; - } else { - /* Just grab 8K for the worst-case bitmap. */ - intmap_bitlen = JS_BIT(16); - intmap = (jsbitmap *) - JS_malloc(cx, - (JS_BIT(16) >> JS_BITS_PER_WORD_LOG2) - * sizeof(jsbitmap)); - if (!intmap) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2); - } - if (JS_TEST_BIT(intmap, i)) { - switchOp = JSOP_LOOKUPSWITCH; - continue; - } - JS_SET_BIT(intmap, i); - } - - release: - if (intmap && intmap != intmap_space) - JS_free(cx, intmap); - if (!ok) - return JS_FALSE; - - /* - * Compute table length and select lookup instead if overlarge or - * more than half-sparse. - */ - if (switchOp == JSOP_TABLESWITCH) { - tableLength = (uint32)(high - low + 1); - if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount) - switchOp = JSOP_LOOKUPSWITCH; - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* - * Lookup switch supports only atom indexes below 64K limit. - * Conservatively estimate the maximum possible index during - * switch generation and use conditional switch if it exceeds - * the limit. - */ - if (caseCount + cg->atomList.count > JS_BIT(16)) - switchOp = JSOP_CONDSWITCH; - } - } - - /* - * Emit a note with two offsets: first tells total switch code length, - * second tells offset to first JSOP_CASE if condswitch. - */ - noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0); - if (noteIndex < 0) - return JS_FALSE; - - if (switchOp == JSOP_CONDSWITCH) { - /* - * 0 bytes of immediate for unoptimized ECMAv2 switch. - */ - switchSize = 0; - } else if (switchOp == JSOP_TABLESWITCH) { - /* - * 3 offsets (len, low, high) before the table, 1 per entry. - */ - switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength)); - } else { - /* - * JSOP_LOOKUPSWITCH: - * 1 offset (len) and 1 atom index (npairs) before the table, - * 1 atom index and 1 jump offset per entry. - */ - switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN + - (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount); - } - - /* - * Emit switchOp followed by switchSize bytes of jump or lookup table. - * - * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial - * to emit the immediate operand(s) by which bytecode readers such as - * BuildSpanDepTable discover the length of the switch opcode *before* - * calling js_SetJumpOffset (which may call BuildSpanDepTable). It's - * also important to zero all unknown jump offset immediate operands, - * so they can be converted to span dependencies with null targets to - * be computed later (js_EmitN zeros switchSize bytes after switchOp). - */ - if (js_EmitN(cx, cg, switchOp, switchSize) < 0) - return JS_FALSE; - - off = -1; - if (switchOp == JSOP_CONDSWITCH) { - intN caseNoteIndex = -1; - JSBool beforeCases = JS_TRUE; - - /* Emit code for evaluating cases and jumping to case statements. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - pn4 = pn3->pn_left; - if (pn4 && !js_EmitTree(cx, cg, pn4)) - return JS_FALSE; - if (caseNoteIndex >= 0) { - /* off is the previous JSOP_CASE's bytecode offset. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - } - if (!pn4) { - JS_ASSERT(pn3->pn_type == TOK_DEFAULT); - continue; - } - caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (caseNoteIndex < 0) - return JS_FALSE; - off = EmitJump(cx, cg, JSOP_CASE, 0); - if (off < 0) - return JS_FALSE; - pn3->pn_offset = off; - if (beforeCases) { - uintN noteCount, noteCountDelta; - - /* Switch note's second offset is to first JSOP_CASE. */ - noteCount = CG_NOTE_COUNT(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - off - top)) { - return JS_FALSE; - } - noteCountDelta = CG_NOTE_COUNT(cg) - noteCount; - if (noteCountDelta != 0) - caseNoteIndex += noteCountDelta; - beforeCases = JS_FALSE; - } - } - - /* - * If we didn't have an explicit default (which could fall in between - * cases, preventing us from fusing this js_SetSrcNoteOffset with the - * call in the loop above), link the last case to the implicit default - * for the decompiler. - */ - if (!hasDefault && - caseNoteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0, - CG_OFFSET(cg) - off)) { - return JS_FALSE; - } - - /* Emit default even if no explicit default statement. */ - defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0); - if (defaultOffset < 0) - return JS_FALSE; - } else { - pc = CG_CODE(cg, top + JUMP_OFFSET_LEN); - - if (switchOp == JSOP_TABLESWITCH) { - /* Fill in switch bounds, which we know fit in 16-bit offsets. */ - SET_JUMP_OFFSET(pc, low); - pc += JUMP_OFFSET_LEN; - SET_JUMP_OFFSET(pc, high); - pc += JUMP_OFFSET_LEN; - - /* - * Use malloc to avoid arena bloat for programs with many switches. - * We free table if non-null at label out, so all control flow must - * exit this function through goto out or goto bad. - */ - if (tableLength != 0) { - tableSize = (size_t)tableLength * sizeof *table; - table = (JSParseNode **) JS_malloc(cx, tableSize); - if (!table) - return JS_FALSE; - memset(table, 0, tableSize); - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) - continue; - i = JSVAL_TO_INT(pn3->pn_val); - i -= low; - JS_ASSERT((uint32)i < tableLength); - table[i] = pn3; - } - } - } else { - JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH); - - /* Fill in the number of cases. */ - SET_ATOM_INDEX(pc, caseCount); - pc += ATOM_INDEX_LEN; - } - - /* - * After this point, all control flow involving JSOP_TABLESWITCH - * must set ok and goto out to exit this function. To keep things - * simple, all switchOp cases exit that way. - */ - if (constPropagated) { - /* - * Skip switchOp, as we are not setting jump offsets in the two - * for loops below. We'll restore CG_NEXT(cg) from savepc after, - * unless there was an error. - */ - savepc = CG_NEXT(cg); - CG_NEXT(cg) = pc + 1; - if (switchOp == JSOP_TABLESWITCH) { - for (i = 0; i < (jsint)tableLength; i++) { - pn3 = table[i]; - if (pn3 && - (pn4 = pn3->pn_left) != NULL && - pn4->pn_type == TOK_NAME) { - /* Note a propagated constant with the const's name. */ - JS_ASSERT(!pn4->pn_expr); - ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); - if (!ale) - goto bad; - CG_NEXT(cg) = pc; - if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) - ALE_INDEX(ale)) < 0) { - goto bad; - } - } - pc += JUMP_OFFSET_LEN; - } - } else { - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - pn4 = pn3->pn_left; - if (pn4 && pn4->pn_type == TOK_NAME) { - /* Note a propagated constant with the const's name. */ - JS_ASSERT(!pn4->pn_expr); - ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList); - if (!ale) - goto bad; - CG_NEXT(cg) = pc; - if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t) - ALE_INDEX(ale)) < 0) { - goto bad; - } - } - pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; - } - } - CG_NEXT(cg) = savepc; - } - } - - /* Emit code for each case's statements, copying pn_offset up to pn3. */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset); - pn4 = pn3->pn_right; - ok = js_EmitTree(cx, cg, pn4); - if (!ok) - goto out; - pn3->pn_offset = pn4->pn_offset; - if (pn3->pn_type == TOK_DEFAULT) - off = pn3->pn_offset - top; - } - - if (!hasDefault) { - /* If no default case, offset for default is to end of switch. */ - off = CG_OFFSET(cg) - top; - } - - /* We better have set "off" by now. */ - JS_ASSERT(off != -1); - - /* Set the default offset (to end of switch if no default). */ - if (switchOp == JSOP_CONDSWITCH) { - pc = NULL; - JS_ASSERT(defaultOffset != -1); - ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset), - off - (defaultOffset - top)); - if (!ok) - goto out; - } else { - pc = CG_CODE(cg, top); - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - - /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ - off = CG_OFFSET(cg) - top; - ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off); - if (!ok) - goto out; - - if (switchOp == JSOP_TABLESWITCH) { - /* Skip over the already-initialized switch bounds. */ - pc += 2 * JUMP_OFFSET_LEN; - - /* Fill in the jump table, if there is one. */ - for (i = 0; i < (jsint)tableLength; i++) { - pn3 = table[i]; - off = pn3 ? pn3->pn_offset - top : 0; - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - } else if (switchOp == JSOP_LOOKUPSWITCH) { - /* Skip over the already-initialized number of cases. */ - pc += ATOM_INDEX_LEN; - - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - if (pn3->pn_type == TOK_DEFAULT) - continue; - atom = js_AtomizeValue(cx, pn3->pn_val, 0); - if (!atom) - goto bad; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - goto bad; - SET_ATOM_INDEX(pc, ALE_INDEX(ale)); - pc += ATOM_INDEX_LEN; - - off = pn3->pn_offset - top; - ok = js_SetJumpOffset(cx, cg, pc, off); - if (!ok) - goto out; - pc += JUMP_OFFSET_LEN; - } - } - -out: - if (table) - JS_free(cx, table); - if (ok) { - ok = js_PopStatementCG(cx, cg); - -#if JS_HAS_BLOCK_SCOPE - if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) { - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); - cg->stackDepth -= count; - } -#endif - } - return ok; - -bad: - ok = JS_FALSE; - goto out; -} - -JSBool -js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) -{ - if (!js_AllocTryNotes(cx, cg)) - return JS_FALSE; - - if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) { - /* JSOP_GENERATOR must be the first instruction. */ - CG_SWITCH_TO_PROLOG(cg); - JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg)); - if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0) - return JS_FALSE; - CG_SWITCH_TO_MAIN(cg); - } - - return js_EmitTree(cx, cg, body) && - js_Emit1(cx, cg, JSOP_STOP) >= 0; -} - -JSBool -js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, - JSFunction *fun) -{ - JSStackFrame *fp, frame; - JSObject *funobj; - JSBool ok; - - fp = cx->fp; - funobj = fun->object; - JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && - fp->scopeChain != funobj)); - memset(&frame, 0, sizeof frame); - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) - ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO - : JSFRAME_COMPILING; - cx->fp = &frame; - ok = js_EmitFunctionBytecode(cx, cg, body); - cx->fp = fp; - if (!ok) - return JS_FALSE; - - if (!js_NewScriptFromCG(cx, cg, fun)) - return JS_FALSE; - - JS_ASSERT(FUN_INTERPRETED(fun)); - return JS_TRUE; -} - -/* A macro for inlining at the top of js_EmitTree (whence it came). */ -#define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn) \ - JS_BEGIN_MACRO \ - uintN line_ = (pn)->pn_pos.begin.lineno; \ - uintN delta_ = line_ - CG_CURRENT_LINE(cg); \ - if (delta_ != 0) { \ - /* \ - * Encode any change in the current source line number by using \ - * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \ - * whichever consumes less space. \ - * \ - * NB: We handle backward line number deltas (possible with for \ - * loops where the update part is emitted after the body, but its \ - * line number is <= any line number in the body) here by letting \ - * unsigned delta_ wrap to a very large number, which triggers a \ - * SRC_SETLINE. \ - */ \ - CG_CURRENT_LINE(cg) = line_; \ - if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \ - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\ - return JS_FALSE; \ - } else { \ - do { \ - if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0) \ - return JS_FALSE; \ - } while (--delta_ != 0); \ - } \ - } \ - JS_END_MACRO - -/* A function, so that we avoid macro-bloating all the other callsites. */ -static JSBool -UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); - return JS_TRUE; -} - -static JSBool -MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn, jsatomid *result) -{ - jsatomid atomIndex; - JSAtomListElement *ale; - - if (pn->pn_slot >= 0) { - atomIndex = (jsatomid) pn->pn_slot; - } else { - ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - } - - if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_CONST && - (!(cg->treeContext.flags & TCF_IN_FUNCTION) || - (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) { - /* Emit a prolog bytecode to predefine the variable. */ - CG_SWITCH_TO_PROLOG(cg); - if (!UpdateLineNumberNotes(cx, cg, pn)) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(prologOp, atomIndex); - CG_SWITCH_TO_MAIN(cg); - } - - if (result) - *result = atomIndex; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING - -typedef JSBool -(*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn); - -static JSBool -EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JS_ASSERT(pn->pn_type == TOK_NAME); - if (!BindNameToSlot(cx, &cg->treeContext, pn, prologOp == JSOP_NOP)) - return JS_FALSE; - - JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS); - return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL); -} - -static JSBool -EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp, - JSParseNode *pn) -{ - JSParseNode *pn2, *pn3; - DestructuringDeclEmitter emitter; - - if (pn->pn_type == TOK_RB) { - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_COMMA) - continue; - emitter = (pn2->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn2)) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pn3 = pn2->pn_right; - emitter = (pn3->pn_type == TOK_NAME) - ? EmitDestructuringDecl - : EmitDestructuringDecls; - if (!emitter(cx, cg, prologOp, pn3)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -static JSBool -EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool wantpop) -{ - jsuint slot; - - /* Skip any parenthesization. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - - /* - * Now emit the lvalue opcode sequence. If the lvalue is a nested - * destructuring initialiser-form, call ourselves to handle it, then - * pop the matched value. Otherwise emit an lvalue bytecode sequence - * ending with a JSOP_ENUMELEM or equivalent op. - */ - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (!EmitDestructuringOpsHelper(cx, cg, pn)) - return JS_FALSE; - if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (pn->pn_type == TOK_NAME && - !BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) { - return JS_FALSE; - } - - switch (pn->pn_op) { - case JSOP_SETNAME: - /* - * NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless, - * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM. - * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp. - */ - if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETCONST: - if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg)) - return JS_FALSE; - break; - - case JSOP_SETLOCAL: - if (wantpop) { - slot = (jsuint) pn->pn_slot; - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot); - break; - } - /* FALL THROUGH */ - - case JSOP_SETARG: - case JSOP_SETVAR: - case JSOP_SETGVAR: - slot = (jsuint) pn->pn_slot; - EMIT_UINT16_IMM_OP(pn->pn_op, slot); - if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - - default: -#if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT - { - ptrdiff_t top; - - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0) - return JS_FALSE; - break; - } -#endif - case JSOP_ENUMELEM: - JS_ASSERT(0); - } - } - - return JS_TRUE; -} - -/* - * Recursive helper for EmitDestructuringOps. - * - * Given a value to destructure on the stack, walk over an object or array - * initialiser at pn, emitting bytecodes to match property values and store - * them in the lvalues identified by the matched property names. - */ -static JSBool -EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - jsuint index; - JSParseNode *pn2, *pn3; - JSBool doElemOp; - -#ifdef DEBUG - intN stackDepth = cg->stackDepth; - JS_ASSERT(stackDepth != 0); - JS_ASSERT(pn->pn_arity == PN_LIST); - JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC); -#endif - - if (pn->pn_count == 0) { - /* Emit a DUP;POP sequence for the decompiler. */ - return js_Emit1(cx, cg, JSOP_DUP) >= 0 && - js_Emit1(cx, cg, JSOP_POP) >= 0; - } - - index = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Duplicate the value being destructured to use as a reference base. - */ - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - - /* - * Now push the property name currently being matched, which is either - * the array initialiser's current index, or the current property name - * "label" on the left of a colon in the object initialiser. Set pn3 - * to the lvalue node, which is in the value-initializing position. - */ - doElemOp = JS_TRUE; - if (pn->pn_type == TOK_RB) { - if (!EmitNumberOp(cx, index, cg)) - return JS_FALSE; - pn3 = pn2; - } else { - JS_ASSERT(pn->pn_type == TOK_RC); - JS_ASSERT(pn2->pn_type == TOK_COLON); - pn3 = pn2->pn_left; - if (pn3->pn_type == TOK_NUMBER) { - /* - * If we are emitting an object destructuring initialiser, - * annotate the index op with SRC_INITPROP so we know we are - * not decompiling an array initialiser. - */ - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - } else { - JS_ASSERT(pn3->pn_type == TOK_STRING || - pn3->pn_type == TOK_NAME); - if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg)) - return JS_FALSE; - doElemOp = JS_FALSE; - } - pn3 = pn2->pn_right; - } - - if (doElemOp) { - /* - * Ok, get the value of the matching property name. This leaves - * that value on top of the value being destructured, so the stack - * is one deeper than when we started. - */ - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == stackDepth + 1); - } - - /* Nullary comma node makes a hole in the array destructurer. */ - if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) { - JS_ASSERT(pn->pn_type == TOK_RB); - JS_ASSERT(pn2 == pn3); - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE)) - return JS_FALSE; - } - - JS_ASSERT(cg->stackDepth == stackDepth); - ++index; - } - - return JS_TRUE; -} - -static ptrdiff_t -OpToDeclType(JSOp op) -{ - switch (op) { - case JSOP_NOP: - return SRC_DECL_LET; - case JSOP_DEFCONST: - return SRC_DECL_CONST; - case JSOP_DEFVAR: - return SRC_DECL_VAR; - default: - return SRC_DECL_NONE; - } -} - -static JSBool -EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *pn) -{ - /* - * If we're called from a variable declaration, help the decompiler by - * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits. - * If the destructuring initialiser is empty, our helper will emit a - * JSOP_DUP followed by a JSOP_POP for the decompiler. - */ - if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0) - return JS_FALSE; - - /* - * Call our recursive helper to emit the destructuring assignments and - * related stack manipulations. - */ - return EmitDestructuringOpsHelper(cx, cg, pn); -} - -static JSBool -EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *lhs, JSParseNode *rhs) -{ - jsuint depth, limit, slot; - JSParseNode *pn; - - depth = limit = (uintN) cg->stackDepth; - for (pn = rhs->pn_head; pn; pn = pn->pn_next) { - if (limit == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, rhs, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_ARRAY_INIT_TOO_BIG); - return JS_FALSE; - } - - if (pn->pn_type == TOK_COMMA) { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - JS_ASSERT(pn->pn_type != TOK_DEFSHARP); - if (!js_EmitTree(cx, cg, pn)) - return JS_FALSE; - } - ++limit; - } - - if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0) - return JS_FALSE; - - slot = depth; - for (pn = lhs->pn_head; pn; pn = pn->pn_next) { - if (slot < limit) { - EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot); - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } else { - if (!EmitDestructuringLHS(cx, cg, pn, pn->pn_next != NULL)) - return JS_FALSE; - } - ++slot; - } - - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - cg->stackDepth = (uintN) depth; - return JS_TRUE; -} - -/* - * Helper called with pop out param initialized to a JSOP_POP* opcode. If we - * can emit a group assignment sequence, which results in 0 stack depth delta, - * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop. - */ -static JSBool -MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp, - JSParseNode *pn, JSOp *pop) -{ - JSParseNode *lhs, *rhs; - - JS_ASSERT(pn->pn_type == TOK_ASSIGN); - JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV); - lhs = pn->pn_left; - rhs = pn->pn_right; - if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB && - lhs->pn_count <= rhs->pn_count && - (rhs->pn_count == 0 || - rhs->pn_head->pn_type != TOK_DEFSHARP)) { - if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs)) - return JS_FALSE; - *pop = JSOP_NOP; - } - return JS_TRUE; -} - -#endif /* JS_HAS_DESTRUCTURING */ - -static JSBool -EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn, - JSBool inLetHead, ptrdiff_t *headNoteIndex) -{ - JSTreeContext *tc; - JSBool let, forInVar; -#if JS_HAS_BLOCK_SCOPE - JSBool forInLet, popScope; - JSStmtInfo *stmt, *scopeStmt; -#endif - ptrdiff_t off, noteIndex, tmp; - JSParseNode *pn2, *pn3; - JSOp op; - jsatomid atomIndex; - uintN oldflags; - - /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */ - *headNoteIndex = -1; - - /* - * Let blocks and expressions have a parenthesized head in which the new - * scope is not yet open. Initializer evaluation uses the parent node's - * lexical scope. If popScope is true below, then we hide the top lexical - * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that - * it won't find any names in the new let block. - * - * The same goes for let declarations in the head of any kind of for loop. - * Unlike a let declaration 'let x = i' within a block, where x is hoisted - * to the start of the block, a 'for (let x = i...) ...' loop evaluates i - * in the containing scope, and puts x in the loop body's scope. - */ - tc = &cg->treeContext; - let = (pn->pn_op == JSOP_NOP); - forInVar = (pn->pn_extra & PNX_FORINVAR) != 0; -#if JS_HAS_BLOCK_SCOPE - forInLet = let && forInVar; - popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT))); - JS_ASSERT(!popScope || let); -#endif - - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { -#if JS_HAS_DESTRUCTURING - if (pn2->pn_type != TOK_NAME) { - if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) { - /* - * Emit variable binding ops, but not destructuring ops. - * The parser (see Variables, jsparse.c) has ensured that - * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree, - * and that case will emit the destructuring code only after - * emitting an enumerating opcode and a branch that tests - * whether the enumeration ended. - */ - JS_ASSERT(forInVar); - JS_ASSERT(pn->pn_count == 1); - if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn2)) - return JS_FALSE; - break; - } - - /* - * A destructuring initialiser assignment preceded by var is - * always evaluated promptly, even if it is to the left of 'in' - * in a for-in loop. As with 'for (var x = i in o)...', this - * will cause the entire 'var [a, b] = i' to be hoisted out of - * the head of the loop. - */ - JS_ASSERT(pn2->pn_type == TOK_ASSIGN); - if (pn->pn_count == 1 && !forInLet) { - /* - * If this is the only destructuring assignment in the list, - * try to optimize to a group assignment. If we're in a let - * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP - * in pn->pn_op, to suppress a second (and misplaced) 'let'. - */ - JS_ASSERT(noteIndex < 0 && !pn2->pn_next); - op = JSOP_POP; - if (!MaybeEmitGroupAssignment(cx, cg, - inLetHead ? JSOP_POP : pn->pn_op, - pn2, &op)) { - return JS_FALSE; - } - if (op == JSOP_NOP) { - pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT; - break; - } - } - - pn3 = pn2->pn_left; - if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn3)) - return JS_FALSE; - -#if JS_HAS_BLOCK_SCOPE - /* - * If this is a 'for (let [x, y] = i in o) ...' let declaration, - * throw away i if it is a useless expression. - */ - if (forInLet) { - JSBool useful = JS_FALSE; - - JS_ASSERT(pn->pn_count == 1); - if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful)) - return JS_FALSE; - if (!useful) - return JS_TRUE; - } -#endif - - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - -#if JS_HAS_BLOCK_SCOPE - /* - * The expression i in 'for (let [x, y] = i in o) ...', which is - * pn2->pn_right above, appears to have side effects. We've just - * emitted code to evaluate i, but we must not destructure i yet. - * Let the TOK_FOR: code in js_EmitTree do the destructuring to - * emit the right combination of source notes and bytecode for the - * decompiler. - * - * This has the effect of hoisting the evaluation of i out of the - * for-in loop, without hoisting the let variables, which must of - * course be scoped by the loop. Set PNX_POPVAR to cause JSOP_POP - * to be emitted, just before returning from this function. - */ - if (forInVar) { - pn->pn_extra |= PNX_POPVAR; - if (forInLet) - break; - } -#endif - - /* - * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT - * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that - * we will emit at the bottom of this function. - */ - if (!EmitDestructuringOps(cx, cg, - inLetHead ? JSOP_POP : pn->pn_op, - pn3)) { - return JS_FALSE; - } - goto emit_note_pop; - } -#else - JS_ASSERT(pn2->pn_type == TOK_NAME); -#endif - - if (!BindNameToSlot(cx, &cg->treeContext, pn2, let)) - return JS_FALSE; - JS_ASSERT(pn2->pn_slot >= 0 || !let); - - op = pn2->pn_op; - if (op == JSOP_ARGUMENTS) { - /* JSOP_ARGUMENTS => no initializer */ - JS_ASSERT(!pn2->pn_expr && !let); - pn3 = NULL; -#ifdef __GNUC__ - atomIndex = 0; /* quell GCC overwarning */ -#endif - } else { - if (!MaybeEmitVarDecl(cx, cg, pn->pn_op, pn2, &atomIndex)) - return JS_FALSE; - - pn3 = pn2->pn_expr; - if (pn3) { -#if JS_HAS_BLOCK_SCOPE - /* - * If this is a 'for (let x = i in o) ...' let declaration, - * throw away i if it is a useless expression. - */ - if (forInLet) { - JSBool useful = JS_FALSE; - - JS_ASSERT(pn->pn_count == 1); - if (!CheckSideEffects(cx, tc, pn3, &useful)) - return JS_FALSE; - if (!useful) - return JS_TRUE; - } -#endif - - if (op == JSOP_SETNAME) { - JS_ASSERT(!let); - EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); - } - if (pn->pn_op == JSOP_DEFCONST && - !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom, - pn3)) { - return JS_FALSE; - } - -#if JS_HAS_BLOCK_SCOPE - /* Evaluate expr in the outer lexical scope if requested. */ - if (popScope) { - stmt = tc->topStmt; - scopeStmt = tc->topScopeStmt; - - tc->topStmt = stmt->down; - tc->topScopeStmt = scopeStmt->downScope; - } -#ifdef __GNUC__ - else { - stmt = scopeStmt = NULL; /* quell GCC overwarning */ - } -#endif -#endif - - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - -#if JS_HAS_BLOCK_SCOPE - if (popScope) { - tc->topStmt = stmt; - tc->topScopeStmt = scopeStmt; - } -#endif - } - } - - /* - * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the - * TOK_VAR case, but only the initialized case (a strange one that - * falls out of ECMA-262's grammar) wants to run past this point. - * Both cases must conditionally emit a JSOP_DEFVAR, above. Note - * that the parser error-checks to ensure that pn->pn_count is 1. - * - * 'for (let x = i in o) ...' must evaluate i before the loop, and - * subject it to useless expression elimination. The variable list - * in pn is a single let declaration if pn_op == JSOP_NOP. We test - * the let local in order to break early in this case, as well as in - * the 'for (var x in o)' case. - * - * XXX Narcissus keeps track of variable declarations in the node - * for the script being compiled, so there's no need to share any - * conditional prolog code generation there. We could do likewise, - * but it's a big change, requiring extra allocation, so probably - * not worth the trouble for SpiderMonkey. - */ - JS_ASSERT(pn3 == pn2->pn_expr); - if (forInVar && (!pn3 || let)) { - JS_ASSERT(pn->pn_count == 1); - break; - } - - if (pn2 == pn->pn_head && - !inLetHead && - js_NewSrcNote2(cx, cg, SRC_DECL, - (pn->pn_op == JSOP_DEFCONST) - ? SRC_DECL_CONST - : (pn->pn_op == JSOP_DEFVAR) - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - if (op == JSOP_ARGUMENTS) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else if (pn2->pn_slot >= 0) { - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - EMIT_ATOM_INDEX_OP(op, atomIndex); - } - -#if JS_HAS_DESTRUCTURING - emit_note_pop: -#endif - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!pn2->pn_next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - - /* If this is a let head, emit and return a srcnote on the pop. */ - if (inLetHead) { - *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL); - if (*headNoteIndex < 0) - return JS_FALSE; - if (!(pn->pn_extra & PNX_POPVAR)) - return js_Emit1(cx, cg, JSOP_NOP) >= 0; - } - - return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0; -} - -#if defined DEBUG_brendan || defined DEBUG_mrbkap -static JSBool -GettableNoteForNextOp(JSCodeGenerator *cg) -{ - ptrdiff_t offset, target; - jssrcnote *sn, *end; - - offset = 0; - target = CG_OFFSET(cg); - for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end; - sn = SN_NEXT(sn)) { - if (offset == target && SN_IS_GETTABLE(sn)) - return JS_TRUE; - offset += SN_DELTA(sn); - } - return JS_FALSE; -} -#endif - -JSBool -js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) -{ - JSBool ok, useful, wantval; - JSStmtInfo *stmt, stmtInfo; - ptrdiff_t top, off, tmp, beq, jmp; - JSParseNode *pn2, *pn3; - JSAtom *atom; - JSAtomListElement *ale; - jsatomid atomIndex; - ptrdiff_t noteIndex; - JSSrcNoteType noteType; - jsbytecode *pc; - JSOp op; - JSTokenType type; - uint32 argc; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - ok = JS_TRUE; - cg->emitLevel++; - pn->pn_offset = top = CG_OFFSET(cg); - - /* Emit notes to tell the current bytecode's source line number. */ - UPDATE_LINE_NUMBER_NOTES(cx, cg, pn); - - switch (pn->pn_type) { - case TOK_FUNCTION: - { - void *cg2mark; - JSCodeGenerator *cg2; - JSFunction *fun; - -#if JS_HAS_XML_SUPPORT - if (pn->pn_arity == PN_NULLARY) { - if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0) - return JS_FALSE; - break; - } -#endif - - /* Generate code for the function's body. */ - cg2mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, &cx->tempPool); - if (!cg2) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool, - cg->filename, pn->pn_pos.begin.lineno, - cg->principals)) { - return JS_FALSE; - } - cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); - cg2->treeContext.tryCount = pn->pn_tryCount; - cg2->parent = cg; - fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom)); - if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun)) - return JS_FALSE; - - /* - * We need an activation object if an inner peeks out, or if such - * inner-peeking caused one of our inners to become heavyweight. - */ - if (cg2->treeContext.flags & - (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) { - cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT; - } - js_FinishCodeGenerator(cx, cg2); - JS_ARENA_RELEASE(&cx->tempPool, cg2mark); - - /* Make the function object a literal in the outer script's pool. */ - ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - - /* Emit a bytecode pointing to the closure object in its immediate. */ - if (pn->pn_op != JSOP_NOP) { - EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex); - break; - } - - /* Top-level named functions need a nop for decompilation. */ - noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* - * Top-levels also need a prolog op to predefine their names in the - * variable object, or if local, to fill their stack slots. - */ - CG_SWITCH_TO_PROLOG(cg); - - if (cg->treeContext.flags & TCF_IN_FUNCTION) { - JSObject *obj, *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - uintN slot; - - obj = OBJ_GET_PARENT(cx, fun->object); - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom), - &pobj, &prop)) { - return JS_FALSE; - } - - JS_ASSERT(prop && pobj == obj); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(sprop->getter == js_GetLocalVariable); - slot = sprop->shortid; - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* - * If this local function is declared in a body block induced by - * let declarations, reparent fun->object to the compiler-created - * body block object so that JSOP_DEFLOCALFUN can clone that block - * into the runtime scope chain. - */ - stmt = cg->treeContext.topStmt; - if (stmt && stmt->type == STMT_BLOCK && - stmt->down && stmt->down->type == STMT_BLOCK && - (stmt->down->flags & SIF_SCOPE)) { - obj = ATOM_TO_OBJECT(stmt->down->atom); - JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass); - OBJ_SET_PARENT(cx, fun->object, obj); - } - - if (atomIndex >= JS_BIT(16)) { - /* - * Lots of literals in the outer function, so we have to emit - * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot]. - */ - off = js_EmitN(cx, cg, JSOP_LITOPX, 3); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_LITERAL_INDEX(pc, atomIndex); - EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot); - } else { - /* Emit [JSOP_DEFLOCALFUN, var slot, atomIndex]. */ - off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN, - VARNO_LEN + ATOM_INDEX_LEN); - if (off < 0) - return JS_FALSE; - pc = CG_CODE(cg, off); - SET_VARNO(pc, slot); - pc += VARNO_LEN; - SET_ATOM_INDEX(pc, atomIndex); - } - } else { - JS_ASSERT(!cg->treeContext.topStmt); - EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex); - } - - CG_SWITCH_TO_MAIN(cg); - break; - } - -#if JS_HAS_EXPORT_IMPORT - case TOK_EXPORT: - pn2 = pn->pn_head; - if (pn2->pn_type == TOK_STAR) { - /* - * 'export *' must have no other elements in the list (what would - * be the point?). - */ - if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0) - return JS_FALSE; - } else { - /* - * If not 'export *', the list consists of NAME nodes identifying - * properties of the variables object to flag as exported. - */ - do { - ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale)); - } while ((pn2 = pn2->pn_next) != NULL); - } - break; - - case TOK_IMPORT: - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Each subtree on an import list is rooted by a DOT or LB node. - * A DOT may have a null pn_atom member, in which case pn_op must - * be JSOP_IMPORTALL -- see EmitPropOp above. - */ - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - break; -#endif /* JS_HAS_EXPORT_IMPORT */ - - case TOK_IF: - /* Initialize so we can detect else-if chains and avoid recursion. */ - stmtInfo.type = STMT_IF; - beq = jmp = -1; - noteIndex = -1; - - if_again: - /* Emit code for the condition before pushing stmtInfo. */ - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - top = CG_OFFSET(cg); - if (stmtInfo.type == STMT_IF) { - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top); - } else { - /* - * We came here from the goto further below that detects else-if - * chains, so we must mutate stmtInfo back into a STMT_IF record. - * Also (see below for why) we need a note offset for SRC_IF_ELSE - * to help the decompiler. Actually, we need two offsets, one for - * decompiling any else clause and the second for decompiling an - * else-if chain without bracing, overindenting, or incorrectly - * scoping let declarations. - */ - JS_ASSERT(stmtInfo.type == STMT_ELSE); - stmtInfo.type = STMT_IF; - stmtInfo.update = top; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp)) - return JS_FALSE; - } - - /* Emit an annotated branch-if-false around the then part. */ - pn3 = pn->pn_kid3; - noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - - /* Emit code for the then and optional else parts. */ - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (pn3) { - /* Modify stmtInfo so we know we're in the else part. */ - stmtInfo.type = STMT_ELSE; - - /* - * Emit a JSOP_BACKPATCH op to jump from the end of our then part - * around the else part. The js_PopStatementCG call at the bottom - * of this switch case will fix up the backpatch chain linked from - * stmtInfo.breaks. - */ - jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL); - if (jmp < 0) - return JS_FALSE; - - /* Ensure the branch-if-false comes here, then emit the else. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (pn3->pn_type == TOK_IF) { - pn = pn3; - goto if_again; - } - - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* - * Annotate SRC_IF_ELSE with the offset from branch to jump, for - * the decompiler's benefit. We can't just "back up" from the pc - * of the else clause, because we don't know whether an extended - * jump was required to leap from the end of the then clause over - * the else clause. - */ - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - } else { - /* No else part, fixup the branch-if-false to come here. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - } - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_SWITCH: - /* Out of line to avoid bloating js_EmitTree's stack frame size. */ - ok = EmitSwitch(cx, cg, pn, &stmtInfo); - break; - - case TOK_WHILE: - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top); - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_DO: - /* Emit an annotated nop so we know to decompile a 'do' keyword. */ - if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Compile the loop body. */ - top = CG_OFFSET(cg); - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top); - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - - /* Set loop and enclosing label update offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL); - - /* Compile the loop condition, now that continues know where to go. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* - * No source note needed, because JSOP_IFNE is used only for do-while. - * If we ever use JSOP_IFNE for other purposes, we can still avoid yet - * another note here, by storing (jmp - top) in the SRC_WHILE note's - * offset, and fetching that delta in order to decompile recursively. - */ - if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_FOR: - beq = 0; /* suppress gcc warnings */ - pn2 = pn->pn_left; - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top); - - if (pn2->pn_type == TOK_IN) { - JSBool emitIFEQ; - - /* Set stmtInfo type for later testing. */ - stmtInfo.type = STMT_FOR_IN_LOOP; - noteIndex = -1; - - /* - * If the left part is 'var x', emit code to define x if necessary - * using a prolog opcode, but do not emit a pop. If the left part - * is 'var x = i', emit prolog code to define x if necessary; then - * emit code to evaluate i, assign the result to x, and pop the - * result off the stack. - * - * All the logic to do this is implemented in the outer switch's - * TOK_VAR case, conditioned on pn_extra flags set by the parser. - * - * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3) - * called here will generate the proper note for the assignment - * op that sets x = i, hoisting the initialized var declaration - * out of the loop: 'var x = i; for (x in o) ...'. - * - * In the 'for (var x in o) ...' case, nothing but the prolog op - * (if needed) should be generated here, we must emit the note - * just before the JSOP_FOR* opcode in the switch on pn3->pn_type - * a bit below, so nothing is hoisted: 'for (var x in o) ...'. - * - * A 'for (let x = i in o)' loop must not be hoisted, since in - * this form the let variable is scoped by the loop body (but not - * the head). The initializer expression i must be evaluated for - * any side effects. So we hoist only i in the let case. - */ - pn3 = pn2->pn_left; - type = pn3->pn_type; - cg->treeContext.flags |= TCF_IN_FOR_INIT; - if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - - /* Emit a push to allocate the iterator. */ - if (js_Emit1(cx, cg, JSOP_STARTITER) < 0) - return JS_FALSE; - - /* Compile the object expression to the right of 'in'. */ - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - - /* - * Emit a bytecode to convert top of stack value to the iterator - * object depending on the loop variant (for-in, for-each-in, or - * destructuring for-in). - */ -#if JS_HAS_DESTRUCTURING - JS_ASSERT(pn->pn_op == JSOP_FORIN || - pn->pn_op == JSOP_FOREACHKEYVAL || - pn->pn_op == JSOP_FOREACH); -#else - JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH); -#endif - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - - /* - * Compile a JSOP_FOR* bytecode based on the left hand side. - * - * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...| - * or similar, to signify assignment, rather than declaration, to - * the decompiler. EmitDestructuringOps takes a prolog bytecode - * parameter and emits the appropriate source note, defaulting to - * assignment, so JSOP_SETNAME is not critical here; many similar - * ops could be used -- just not JSOP_NOP (which means 'let'). - */ - emitIFEQ = JS_TRUE; - op = JSOP_SETNAME; - switch (type) { -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: -#endif - case TOK_VAR: - JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1); - pn3 = pn3->pn_head; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN) { - pn3 = pn3->pn_left; - JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC); - } - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - op = pn2->pn_left->pn_op; - goto destructuring_for; - } -#else - JS_ASSERT(pn3->pn_type == TOK_NAME); -#endif - /* - * Always annotate JSOP_FORLOCAL if given input of the form - * 'for (let x in * o)' -- the decompiler must not hoist the - * 'let x' out of the loop head, or x will be bound in the - * wrong scope. Likewise, but in this case only for the sake - * of higher decompilation fidelity only, do not hoist 'var x' - * when given 'for (var x in o)'. But 'for (var x = i in o)' - * requires hoisting in order to preserve the initializer i. - * The decompiler can only handle so much! - */ - if (( -#if JS_HAS_BLOCK_SCOPE - type == TOK_LET || -#endif - !pn3->pn_expr) && - js_NewSrcNote2(cx, cg, SRC_DECL, - type == TOK_VAR - ? SRC_DECL_VAR - : SRC_DECL_LET) < 0) { - return JS_FALSE; - } - /* FALL THROUGH */ - case TOK_NAME: - if (pn3->pn_slot >= 0) { - op = pn3->pn_op; - switch (op) { - case JSOP_GETARG: /* FALL THROUGH */ - case JSOP_SETARG: op = JSOP_FORARG; break; - case JSOP_GETVAR: /* FALL THROUGH */ - case JSOP_SETVAR: op = JSOP_FORVAR; break; - case JSOP_GETGVAR: /* FALL THROUGH */ - case JSOP_SETGVAR: op = JSOP_FORNAME; break; - case JSOP_GETLOCAL: /* FALL THROUGH */ - case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break; - default: JS_ASSERT(0); - } - } else { - pn3->pn_op = JSOP_FORNAME; - if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE)) - return JS_FALSE; - op = pn3->pn_op; - } - if (pn3->pn_slot >= 0) { - if (pn3->pn_attrs & JSPROP_READONLY) { - JS_ASSERT(op == JSOP_FORVAR); - op = JSOP_GETVAR; - } - atomIndex = (jsatomid) pn3->pn_slot; - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - if (!EmitAtomOp(cx, pn3, op, cg)) - return JS_FALSE; - } - break; - - case TOK_DOT: - useful = JS_FALSE; - if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr, - &useful)) { - return JS_FALSE; - } - if (!useful) { - if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg)) - return JS_FALSE; - break; - } - /* FALL THROUGH */ - -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - destructuring_for: -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - case TOK_LB: - /* - * We separate the first/next bytecode from the enumerator - * variable binding to avoid any side-effects in the index - * expression (e.g., for (x[i++] in {}) should not bind x[i] - * or increment i at all). - */ - emitIFEQ = JS_FALSE; - if (!js_Emit1(cx, cg, JSOP_FORELEM)) - return JS_FALSE; - - /* - * Emit a SRC_WHILE note with offset telling the distance to - * the loop-closing jump (we can't reckon from the branch at - * the top of the loop, because the loop-closing jump might - * need to be an extended jump, independent of whether the - * branch is short or long). - */ - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) { - if (!EmitDestructuringOps(cx, cg, op, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; - } -#endif -#if JS_HAS_LVALUE_RETURN - if (pn3->pn_type == TOK_LP) { - JS_ASSERT(pn3->pn_op == JSOP_SETCALL); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) - return JS_FALSE; - break; - } -#endif -#if JS_HAS_XML_SUPPORT - if (pn3->pn_type == TOK_UNARYOP) { - JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (!js_Emit1(cx, cg, JSOP_ENUMELEM)) - return JS_FALSE; - break; - } -#endif - - /* Now that we're safely past the IFEQ, commit side effects. */ - if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg)) - return JS_FALSE; - break; - - default: - JS_ASSERT(0); - } - - if (emitIFEQ) { - /* Annotate so the decompiler can find the loop-closing jump. */ - noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE); - if (noteIndex < 0) - return JS_FALSE; - - /* Pop and test the loop condition generated by JSOP_FOR*. */ - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - } - } else { - op = JSOP_POP; - if (!pn2->pn_kid1) { - /* No initializer: emit an annotated nop for the decompiler. */ - op = JSOP_NOP; - } else { - cg->treeContext.flags |= TCF_IN_FOR_INIT; -#if JS_HAS_DESTRUCTURING - pn3 = pn2->pn_kid1; - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { - return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) { - /* - * Check whether a destructuring-initialized var decl - * was optimized to a group assignment. If so, we do - * not need to emit a pop below, so switch to a nop, - * just for the decompiler. - */ - JS_ASSERT(pn3->pn_arity == PN_LIST); - if (pn3->pn_extra & PNX_GROUPINIT) - op = JSOP_NOP; - } - } - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - } - noteIndex = js_NewSrcNote(cx, cg, SRC_FOR); - if (noteIndex < 0 || - js_Emit1(cx, cg, op) < 0) { - return JS_FALSE; - } - - top = CG_OFFSET(cg); - SET_STATEMENT_TOP(&stmtInfo, top); - if (!pn2->pn_kid2) { - /* No loop condition: flag this fact in the source notes. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0)) - return JS_FALSE; - } else { - if (!js_EmitTree(cx, cg, pn2->pn_kid2)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0) - return JS_FALSE; - } - - /* Set pn3 (used below) here to avoid spurious gcc warnings. */ - pn3 = pn2->pn_kid3; - } - - /* Emit code for the loop body. */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - if (pn2->pn_type != TOK_IN) { - /* Set the second note offset so we can find the update part. */ - JS_ASSERT(noteIndex != -1); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - - if (pn3) { - /* Set loop and enclosing "update" offsets, for continue. */ - stmt = &stmtInfo; - do { - stmt->update = CG_OFFSET(cg); - } while ((stmt = stmt->down) != NULL && - stmt->type == STMT_LABEL); - - op = JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (pn3->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) { - return JS_FALSE; - } -#endif - if (op == JSOP_POP) { - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - - /* Restore the absolute line number for source note readers. */ - off = (ptrdiff_t) pn->pn_pos.end.lineno; - if (CG_CURRENT_LINE(cg) != (uintN) off) { - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0) - return JS_FALSE; - CG_CURRENT_LINE(cg) = (uintN) off; - } - } - - /* The third note offset helps us find the loop-closing jump. */ - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - } - - /* Emit the loop-closing jump and fixup all jump offsets. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg)); - if (jmp < 0) - return JS_FALSE; - if (beq > 0) - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - if (pn2->pn_type == TOK_IN) { - /* Set the SRC_WHILE note offset so we can find the closing jump. */ - JS_ASSERT(noteIndex != -1); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq)) - return JS_FALSE; - } - - /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */ - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - if (pn2->pn_type == TOK_IN) { - if (js_Emit1(cx, cg, JSOP_ENDITER) < 0) - return JS_FALSE; - } - break; - - case TOK_BREAK: - stmt = cg->treeContext.topStmt; - atom = pn->pn_atom; - if (atom) { - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->atom != atom) - stmt = stmt->down; - noteType = SRC_BREAK2LABEL; - } else { - ale = NULL; - while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH) - stmt = stmt->down; - noteType = SRC_NULL; - } - - if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0) - return JS_FALSE; - break; - - case TOK_CONTINUE: - stmt = cg->treeContext.topStmt; - atom = pn->pn_atom; - if (atom) { - /* Find the loop statement enclosed by the matching label. */ - JSStmtInfo *loop = NULL; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - while (stmt->type != STMT_LABEL || stmt->atom != atom) { - if (STMT_IS_LOOP(stmt)) - loop = stmt; - stmt = stmt->down; - } - stmt = loop; - noteType = SRC_CONT2LABEL; - } else { - ale = NULL; - while (!STMT_IS_LOOP(stmt)) - stmt = stmt->down; - noteType = SRC_CONTINUE; - } - - if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0) - return JS_FALSE; - break; - - case TOK_WITH: - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg)); - if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) - return JS_FALSE; - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_TRY: - { - ptrdiff_t start, end, catchJump, catchStart, finallyCatch; - intN depth; - JSParseNode *lastCatch; - - catchJump = catchStart = finallyCatch = -1; - - /* - * Push stmtInfo to track jumps-over-catches and gosubs-to-finally - * for later fixup. - * - * When a finally block is 'active' (STMT_FINALLY on the treeContext), - * non-local jumps (including jumps-over-catches) result in a GOSUB - * being written into the bytecode stream and fixed-up later (c.f. - * EmitBackPatchOp and BackPatch). - */ - js_PushStatement(&cg->treeContext, &stmtInfo, - pn->pn_kid3 ? STMT_FINALLY : STMT_TRY, - CG_OFFSET(cg)); - - /* - * About JSOP_SETSP: an exception can be thrown while the stack is in - * an unbalanced state, and this imbalance causes problems with things - * like function invocation later on. - * - * To fix this, we compute the 'balanced' stack depth upon try entry, - * and then restore the stack to this depth when we hit the first catch - * or finally block. We can't just zero the stack, because things like - * for/in and with that are active upon entry to the block keep state - * variables on the stack. - */ - depth = cg->stackDepth; - - /* Mark try location for decompilation, then emit try block. */ - if (js_Emit1(cx, cg, JSOP_TRY) < 0) - return JS_FALSE; - start = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - - /* GOSUB to finally, if present. */ - if (pn->pn_kid3) { - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - - /* JSOP_RETSUB pops the return pc-index, balancing the stack. */ - cg->stackDepth = depth; - } - - /* Emit (hidden) jump over catch and/or finally. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); - if (jmp < 0) - return JS_FALSE; - - end = CG_OFFSET(cg); - - /* If this try has a catch block, emit it. */ - pn2 = pn->pn_kid2; - lastCatch = NULL; - if (pn2) { - jsint count = 0; /* previous catch block's population */ - - catchStart = end; - - /* - * The emitted code for a catch block looks like: - * - * [throwing] only if 2nd+ catch block - * [leaveblock] only if 2nd+ catch block - * enterblock with SRC_CATCH - * exception - * [dup] only if catchguard - * setlocalpop or destructuring code - * [< catchguard code >] if there's a catchguard - * [ifeq ] " " - * [pop] only if catchguard - * < catch block contents > - * leaveblock - * goto non-local; finally applies - * - * If there's no catch block without a catchguard, the last - * points to rethrow code. This - * code will [gosub] to the finally code if appropriate, and is - * also used for the catch-all trynote for capturing exceptions - * thrown from catch{} blocks. - */ - for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { - ptrdiff_t guardJump, catchNote; - - guardJump = GUARDJUMP(stmtInfo); - if (guardJump == -1) { - /* Set stack to original depth (see SETSP comment above). */ - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - cg->stackDepth = depth; - } else { - /* Fix up and clean up previous catch block. */ - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump); - - /* - * Account for the pushed exception object that we still - * have after the jumping from the previous guard. - */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - - /* - * Move exception back to cx->exception to prepare for - * the next catch. We hide [throwing] from the decompiler - * since it compensates for the hidden JSOP_DUP at the - * start of the previous guarded catch. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROWING) < 0) { - return JS_FALSE; - } - - /* - * Emit an unbalanced [leaveblock] for the previous catch, - * whose block object count is saved below. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - JS_ASSERT(count >= 0); - EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count); - } - - /* - * Annotate the JSOP_ENTERBLOCK that's about to be generated - * by the call to js_EmitTree immediately below. Save this - * source note's index in stmtInfo for use by the TOK_CATCH: - * case, where the length of the catch guard is set as the - * note's offset. - */ - catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0); - if (catchNote < 0) - return JS_FALSE; - CATCHNOTE(stmtInfo) = catchNote; - - /* - * Emit the lexical scope and catch body. Save the catch's - * block object population via count, for use when targeting - * guardJump at the next catch (the guard mismatch case). - */ - JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE); - count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom)); - if (!js_EmitTree(cx, cg, pn3)) - return JS_FALSE; - - /* gosub , if required */ - if (pn->pn_kid3) { - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - JS_ASSERT(cg->stackDepth == depth); - } - - /* - * Jump over the remaining catch blocks. This will get fixed - * up to jump to after catch/finally. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0) - return JS_FALSE; - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump); - if (jmp < 0) - return JS_FALSE; - - /* - * Save a pointer to the last catch node to handle try-finally - * and try-catch(guard)-finally special cases. - */ - lastCatch = pn3->pn_expr; - } - } - - /* - * Last catch guard jumps to the rethrow code sequence if none of the - * guards match. Target guardJump at the beginning of the rethrow - * sequence, just in case a guard expression throws and leaves the - * stack unbalanced. - */ - if (lastCatch && lastCatch->pn_kid2) { - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo)); - - /* Sync the stack to take into account pushed exception. */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth = depth + 1; - - /* - * Rethrow the exception, delegating executing of finally if any - * to the exception handler. - */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_THROW) < 0) { - return JS_FALSE; - } - } - - JS_ASSERT(cg->stackDepth == depth); - - /* Emit finally handler if any. */ - if (pn->pn_kid3) { - /* - * We emit [setsp][gosub] to call try-finally when an exception is - * thrown from try or try-catch blocks. The [gosub] and [retsub] - * opcodes will take care of stacking and rethrowing any exception - * pending across the finally. - */ - finallyCatch = CG_OFFSET(cg); - EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth); - - jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, - &GOSUBS(stmtInfo)); - if (jmp < 0) - return JS_FALSE; - - JS_ASSERT(cg->stackDepth == depth); - JS_ASSERT((uintN)depth <= cg->maxStackDepth); - - /* - * Fix up the gosubs that might have been emitted before non-local - * jumps to the finally code. - */ - if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB)) - return JS_FALSE; - - /* - * The stack budget must be balanced at this point. All [gosub] - * calls emitted before this point will push two stack slots, one - * for the pending exception (or JSVAL_HOLE if there is no pending - * exception) and one for the [retsub] pc-index. - */ - JS_ASSERT(cg->stackDepth == depth); - cg->stackDepth += 2; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* Now indicate that we're emitting a subroutine body. */ - stmtInfo.type = STMT_SUBROUTINE; - if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 || - !js_EmitTree(cx, cg, pn->pn_kid3) || - js_Emit1(cx, cg, JSOP_RETSUB) < 0) { - return JS_FALSE; - } - - /* Restore stack depth budget to its balanced state. */ - JS_ASSERT(cg->stackDepth == depth + 2); - cg->stackDepth = depth; - } - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Fix up the end-of-try/catch jumps to come here. */ - if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO)) - return JS_FALSE; - - /* - * Add the try note last, to let post-order give us the right ordering - * (first to last for a given nesting level, inner to outer by level). - */ - if (pn->pn_kid2) { - JS_ASSERT(end != -1 && catchStart != -1); - if (!js_NewTryNote(cx, cg, start, end, catchStart)) - return JS_FALSE; - } - - /* - * If we've got a finally, mark try+catch region with additional - * trynote to catch exceptions (re)thrown from a catch block or - * for the try{}finally{} case. - */ - if (pn->pn_kid3) { - JS_ASSERT(finallyCatch != -1); - if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch)) - return JS_FALSE; - } - break; - } - - case TOK_CATCH: - { - ptrdiff_t catchStart, guardJump; - - /* - * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset, - * and save the block object atom. - */ - stmt = cg->treeContext.topStmt; - JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE)); - stmt->type = STMT_CATCH; - catchStart = stmt->update; - atom = stmt->atom; - - /* Go up one statement info record to the TRY or FINALLY record. */ - stmt = stmt->down; - JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY); - - /* Pick up the pending exception and bind it to the catch variable. */ - if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) - return JS_FALSE; - - /* - * Dup the exception object if there is a guard for rethrowing to use - * it later when rethrowing or in other catches. - */ - if (pn->pn_kid2) { - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_DUP) < 0) { - return JS_FALSE; - } - } - - pn2 = pn->pn_kid1; - switch (pn2->pn_type) { -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - break; -#endif - - case TOK_NAME: - /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */ - pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom)); - EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot); - break; - - default: - JS_ASSERT(0); - } - - /* Emit the guard expression, if there is one. */ - if (pn->pn_kid2) { - if (!js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0, - CG_OFFSET(cg) - catchStart)) { - return JS_FALSE; - } - /* ifeq */ - guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (guardJump < 0) - return JS_FALSE; - GUARDJUMP(*stmt) = guardJump; - - /* Pop duplicated exception object as we no longer need it. */ - if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 || - js_Emit1(cx, cg, JSOP_POP) < 0) { - return JS_FALSE; - } - } - - /* Emit the catch body. */ - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - - /* - * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via - * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop. - */ - off = cg->stackDepth; - if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0) - return JS_FALSE; - break; - } - - case TOK_VAR: - if (!EmitVariables(cx, cg, pn, JS_FALSE, ¬eIndex)) - return JS_FALSE; - break; - - case TOK_RETURN: - /* Push a return value */ - pn2 = pn->pn_kid; - if (pn2) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - - /* - * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a - * JSOP_SETRVAL if there are open try blocks having finally clauses. - * We can't simply transfer control flow to our caller in that case, - * because we must gosub to those clauses from inner to outer, with - * the correct stack pointer (i.e., after popping any with, for/in, - * etc., slots nested inside the finally's try). - */ - op = JSOP_RETURN; - if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - -#if JS_HAS_GENERATORS - case TOK_YIELD: - if (pn->pn_kid) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - } else { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_YIELD) < 0) - return JS_FALSE; - break; -#endif - - case TOK_LC: -#if JS_HAS_XML_SUPPORT - if (pn->pn_arity == PN_UNARY) { - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - break; - } -#endif - - JS_ASSERT(pn->pn_arity == PN_LIST); - - noteIndex = -1; - tmp = CG_OFFSET(cg); - if (pn->pn_extra & PNX_NEEDBRACES) { - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0) - return JS_FALSE; - } - - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_BODY: - JS_ASSERT(pn->pn_arity == PN_LIST); - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - ok = js_PopStatementCG(cx, cg); - break; - - case TOK_SEMI: - pn2 = pn->pn_kid; - if (pn2) { - /* - * Top-level or called-from-a-native JS_Execute/EvaluateScript, - * debugger, and eval frames may need the value of the ultimate - * expression statement as the script's result, despite the fact - * that it appears useless to the compiler. - */ - useful = wantval = !cx->fp->fun || - !FUN_INTERPRETED(cx->fp->fun) || - (cx->fp->flags & JSFRAME_SPECIAL); - if (!useful) { - if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) - return JS_FALSE; - } - - /* - * Don't eliminate apparently useless expressions if they are - * labeled expression statements. The tc->topStmt->update test - * catches the case where we are nesting in js_EmitTree for a - * labeled compound statement. - */ - if (!useful && - (!cg->treeContext.topStmt || - cg->treeContext.topStmt->type != STMT_LABEL || - cg->treeContext.topStmt->update < CG_OFFSET(cg))) { - CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno; - if (!js_ReportCompileErrorNumber(cx, cg, - JSREPORT_CG | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_USELESS_EXPR)) { - return JS_FALSE; - } - } else { - op = wantval ? JSOP_POPV : JSOP_POP; -#if JS_HAS_DESTRUCTURING - if (!wantval && - pn2->pn_type == TOK_ASSIGN && - !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) { - return JS_FALSE; - } -#endif - if (op != JSOP_NOP) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } - } - break; - - case TOK_COLON: - /* Emit an annotated nop so we know to decompile a label. */ - atom = pn->pn_atom; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - pn2 = pn->pn_expr; - noteType = (pn2->pn_type == TOK_LC || - (pn2->pn_type == TOK_LEXICALSCOPE && - pn2->pn_expr->pn_type == TOK_LC)) - ? SRC_LABELBRACE - : SRC_LABEL; - noteIndex = js_NewSrcNote2(cx, cg, noteType, - (ptrdiff_t) ALE_INDEX(ale)); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - - /* Emit code for the labeled statement. */ - js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL, - CG_OFFSET(cg)); - stmtInfo.atom = atom; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (!js_PopStatementCG(cx, cg)) - return JS_FALSE; - - /* If the statement was compound, emit a note for the end brace. */ - if (noteType == SRC_LABELBRACE) { - if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 || - js_Emit1(cx, cg, JSOP_NOP) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_COMMA: - /* - * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands. - * These notes help the decompiler bracket the bytecodes generated - * from each sub-expression that follows a comma. - */ - off = noteIndex = -1; - for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - tmp = CG_OFFSET(cg); - if (noteIndex >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - if (!pn2->pn_next) - break; - off = tmp; - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || - js_Emit1(cx, cg, JSOP_POP) < 0) { - return JS_FALSE; - } - } - break; - - case TOK_ASSIGN: - /* - * Check left operand type and generate specialized code for it. - * Specialize to avoid ECMA "reference type" values on the operand - * stack, which impose pervasive runtime "GetValue" costs. - */ - pn2 = pn->pn_left; - JS_ASSERT(pn2->pn_type != TOK_RP); - atomIndex = (jsatomid) -1; - switch (pn2->pn_type) { - case TOK_NAME: - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - if (pn2->pn_slot >= 0) { - atomIndex = (jsatomid) pn2->pn_slot; - } else { - ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex); - } - break; - case TOK_DOT: - if (!js_EmitTree(cx, cg, pn2->pn_expr)) - return JS_FALSE; - ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - atomIndex = ALE_INDEX(ale); - break; - case TOK_LB: - JS_ASSERT(pn2->pn_arity == PN_BINARY); - if (!js_EmitTree(cx, cg, pn2->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - break; -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); - if (!js_EmitTree(cx, cg, pn2->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - - op = pn->pn_op; -#if JS_HAS_GETTER_SETTER - if (op == JSOP_GETTER || op == JSOP_SETTER) { - /* We'll emit these prefix bytecodes after emitting the r.h.s. */ - if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - } else -#endif - /* If += or similar, dup the left operand and get its value. */ - if (op != JSOP_NOP) { - switch (pn2->pn_type) { - case TOK_NAME: - if (pn2->pn_op != JSOP_SETNAME) { - EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR) - ? JSOP_GETGVAR - : (pn2->pn_op == JSOP_SETARG) - ? JSOP_GETARG - : (pn2->pn_op == JSOP_SETLOCAL) - ? JSOP_GETLOCAL - : JSOP_GETVAR, - atomIndex); - break; - } - /* FALL THROUGH */ - case TOK_DOT: - if (js_Emit1(cx, cg, JSOP_DUP) < 0) - return JS_FALSE; - EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME) - ? JSOP_GETXPROP - : JSOP_GETPROP, - atomIndex); - break; - case TOK_LB: -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: -#endif - if (js_Emit1(cx, cg, JSOP_DUP2) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_GETELEM) < 0) - return JS_FALSE; - break; - default:; - } - } - - /* Now emit the right operand (it may affect the namespace). */ - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - - /* If += etc., emit the binary operator with a decompiler note. */ - if (op != JSOP_NOP) { - /* - * Take care to avoid SRC_ASSIGNOP if the left-hand side is a - * const declared in a function (i.e., with non-negative pn_slot - * and JSPROP_READONLY in pn_attrs), as in this case (just a bit - * further below) we will avoid emitting the assignment op. - */ - if (pn2->pn_type != TOK_NAME || - pn2->pn_slot < 0 || - !(pn2->pn_attrs & JSPROP_READONLY)) { - if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - - /* Left parts such as a.b.c and a[b].c need a decompiler note. */ - if (pn2->pn_type != TOK_NAME && -#if JS_HAS_DESTRUCTURING - pn2->pn_type != TOK_RB && - pn2->pn_type != TOK_RC && -#endif - js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op), - CG_OFFSET(cg) - top) < 0) { - return JS_FALSE; - } - - /* Finally, emit the specialized assignment bytecode. */ - switch (pn2->pn_type) { - case TOK_NAME: - if (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY)) { - if (pn2->pn_slot >= 0) { - EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex); - } else { - case TOK_DOT: - EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex); - } - } - break; - case TOK_LB: -#if JS_HAS_LVALUE_RETURN - case TOK_LP: -#endif - if (js_Emit1(cx, cg, JSOP_SETELEM) < 0) - return JS_FALSE; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2)) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - break; - - case TOK_HOOK: - /* Emit the condition, then branch if false to the else part. */ - if (!js_EmitTree(cx, cg, pn->pn_kid1)) - return JS_FALSE; - noteIndex = js_NewSrcNote(cx, cg, SRC_COND); - if (noteIndex < 0) - return JS_FALSE; - beq = EmitJump(cx, cg, JSOP_IFEQ, 0); - if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2)) - return JS_FALSE; - - /* Jump around else, fixup the branch, emit else, fixup jump. */ - jmp = EmitJump(cx, cg, JSOP_GOTO, 0); - if (jmp < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq); - - /* - * Because each branch pushes a single value, but our stack budgeting - * analysis ignores branches, we now have to adjust cg->stackDepth to - * ignore the value pushed by the first branch. Execution will follow - * only one path, so we must decrement cg->stackDepth. - * - * Failing to do this will foil code, such as the try/catch/finally - * exception handling code generator, that samples cg->stackDepth for - * use at runtime (JSOP_SETSP), or in let expression and block code - * generation, which must use the stack depth to compute local stack - * indexes correctly. - */ - JS_ASSERT(cg->stackDepth > 0); - cg->stackDepth--; - if (!js_EmitTree(cx, cg, pn->pn_kid3)) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq)) - return JS_FALSE; - break; - - case TOK_OR: - case TOK_AND: - /* - * JSOP_OR converts the operand on the stack to boolean, and if true, - * leaves the original operand value on the stack and jumps; otherwise - * it pops and falls into the next bytecode, which evaluates the right - * operand. The jump goes around the right operand evaluation. - * - * JSOP_AND converts the operand on the stack to boolean, and if false, - * leaves the original operand value on the stack and jumps; otherwise - * it pops and falls into the right operand's bytecode. - * - * Avoid tail recursion for long ||...|| expressions and long &&...&& - * expressions or long mixtures of ||'s and &&'s that can easily blow - * the stack, by forward-linking and then backpatching all the JSOP_OR - * and JSOP_AND bytecodes' immediate jump-offset operands. - */ - pn3 = pn; - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (top < 0) - return JS_FALSE; - jmp = top; - pn2 = pn->pn_right; - while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) { - pn = pn2; - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0); - if (off < 0) - return JS_FALSE; - if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp)) - return JS_FALSE; - jmp = off; - pn2 = pn->pn_right; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - off = CG_OFFSET(cg); - do { - pc = CG_CODE(cg, top); - tmp = GetJumpOffset(cg, pc); - CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top); - *pc = pn3->pn_op; - top += tmp; - } while ((pn3 = pn3->pn_right) != pn2); - break; - - case TOK_BITOR: - case TOK_BITXOR: - case TOK_BITAND: - case TOK_EQOP: - case TOK_RELOP: - case TOK_IN: - case TOK_INSTANCEOF: - case TOK_SHOP: - case TOK_PLUS: - case TOK_MINUS: - case TOK_STAR: - case TOK_DIVOP: - if (pn->pn_arity == PN_LIST) { - /* Left-associative operator chain: avoid too much recursion. */ - pn2 = pn->pn_head; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - op = pn->pn_op; - while ((pn2 = pn2->pn_next) != NULL) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } - } else { -#if JS_HAS_XML_SUPPORT - uintN oldflags; - - case TOK_DBLCOLON: - if (pn->pn_arity == PN_NAME) { - if (!js_EmitTree(cx, cg, pn->pn_expr)) - return JS_FALSE; - if (!EmitAtomOp(cx, pn, pn->pn_op, cg)) - return JS_FALSE; - break; - } - - /* - * Binary :: has a right operand that brackets arbitrary code, - * possibly including a let (a = b) ... expression. We must clear - * TCF_IN_FOR_INIT to avoid mis-compiling such beasts. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; -#endif - - /* Binary operators that evaluate both operands unconditionally. */ - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; -#if JS_HAS_XML_SUPPORT - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; -#endif - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - } - break; - - case TOK_THROW: -#if JS_HAS_XML_SUPPORT - case TOK_AT: - case TOK_DEFAULT: - JS_ASSERT(pn->pn_arity == PN_UNARY); - /* FALL THROUGH */ -#endif - case TOK_UNARYOP: - { - uintN oldflags; - - /* Unary op, including unary +/-. */ - pn2 = pn->pn_kid; - op = pn->pn_op; - if (op == JSOP_TYPEOF) { - for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid) - continue; - if (pn3->pn_type != TOK_NAME) - op = JSOP_TYPEOFEXPR; - } - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; -#if JS_HAS_XML_SUPPORT - if (op == JSOP_XMLNAME && - js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } -#endif - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - } - - case TOK_INC: - case TOK_DEC: - { - intN depth; - - /* Emit lvalue-specialized code for ++/-- operators. */ - pn2 = pn->pn_kid; - JS_ASSERT(pn2->pn_type != TOK_RP); - op = pn->pn_op; - depth = cg->stackDepth; - switch (pn2->pn_type) { - case TOK_NAME: - pn2->pn_op = op; - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - op = pn2->pn_op; - if (pn2->pn_slot >= 0) { - if (pn2->pn_attrs & JSPROP_READONLY) { - /* Incrementing a declared const: just get its value. */ - op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST) - ? JSOP_GETGVAR - : JSOP_GETVAR; - } - atomIndex = (jsatomid) pn2->pn_slot; - EMIT_UINT16_IMM_OP(op, atomIndex); - } else { - if (!EmitAtomOp(cx, pn2, op, cg)) - return JS_FALSE; - } - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, op, cg)) - return JS_FALSE; - ++depth; - break; - case TOK_LB: - if (!EmitElemOp(cx, pn2, op, cg)) - return JS_FALSE; - depth += 2; - break; -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - depth = cg->stackDepth; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, - CG_OFFSET(cg) - pn2->pn_offset) < 0) { - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME); - if (!js_EmitTree(cx, cg, pn2->pn_kid)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0) - return JS_FALSE; - depth = cg->stackDepth; - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; -#endif - default: - JS_ASSERT(0); - } - - /* - * Allocate another stack slot for GC protection in case the initial - * value being post-incremented or -decremented is not a number, but - * converts to a jsdouble. In the TOK_NAME cases, op has 0 operand - * uses and 1 definition, so we don't need an extra stack slot -- we - * can use the one allocated for the def. - */ - if (pn2->pn_type != TOK_NAME && - (js_CodeSpec[op].format & JOF_POST) && - (uintN)depth == cg->maxStackDepth) { - ++cg->maxStackDepth; - } - break; - } - - case TOK_DELETE: - /* - * Under ECMA 3, deleting a non-reference returns true -- but alas we - * must evaluate the operand if it appears it might have side effects. - */ - pn2 = pn->pn_kid; - switch (pn2->pn_type) { - case TOK_NAME: - pn2->pn_op = JSOP_DELNAME; - if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE)) - return JS_FALSE; - op = pn2->pn_op; - if (op == JSOP_FALSE) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } else { - if (!EmitAtomOp(cx, pn2, op, cg)) - return JS_FALSE; - } - break; - case TOK_DOT: - if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg)) - return JS_FALSE; - break; -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: - if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg)) - return JS_FALSE; - break; -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - if (pn2->pn_op != JSOP_SETCALL) { - JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); - pn2->pn_op = JSOP_SETCALL; - } - top = CG_OFFSET(cg); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_DELELEM) < 0) - return JS_FALSE; - break; -#endif - case TOK_LB: - if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg)) - return JS_FALSE; - break; - default: - /* - * If useless, just emit JSOP_TRUE; otherwise convert delete foo() - * to foo(), true (a comma expression, requiring SRC_PCDELTA). - */ - useful = JS_FALSE; - if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) - return JS_FALSE; - if (!useful) { - off = noteIndex = -1; - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - off = CG_OFFSET(cg); - noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0); - if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, JSOP_TRUE) < 0) - return JS_FALSE; - if (noteIndex >= 0) { - tmp = CG_OFFSET(cg); - if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off)) - return JS_FALSE; - } - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_FILTER: - if (!js_EmitTree(cx, cg, pn->pn_left)) - return JS_FALSE; - jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0); - if (jmp < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn->pn_right)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0) - return JS_FALSE; - CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp); - break; -#endif - - case TOK_DOT: - /* - * Pop a stack operand, convert it to object, get a property named by - * this bytecode's immediate-indexed atom operand, and push its value - * (not a reference to it). This bytecode sets the virtual machine's - * "obj" register to the left operand's ToObject conversion result, - * for use by JSOP_PUSHOBJ. - */ - ok = EmitPropOp(cx, pn, pn->pn_op, cg); - break; - - case TOK_LB: -#if JS_HAS_XML_SUPPORT - case TOK_DBLDOT: -#endif - /* - * Pop two operands, convert the left one to object and the right one - * to property name (atom or tagged int), get the named property, and - * push its value. Set the "obj" register to the result of ToObject - * on the left operand. - */ - ok = EmitElemOp(cx, pn, pn->pn_op, cg); - break; - - case TOK_NEW: - case TOK_LP: - { - uintN oldflags; - - /* - * Emit function call or operator new (constructor call) code. - * First, emit code for the left operand to evaluate the callable or - * constructable object expression. - * - * For E4X, if this expression is a dotted member reference, select - * JSOP_GETMETHOD instead of JSOP_GETPROP. ECMA-357 separates XML - * method lookup from the normal property id lookup done for native - * objects. - */ - pn2 = pn->pn_head; -#if JS_HAS_XML_SUPPORT - if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) { - JS_ASSERT(pn2->pn_op == JSOP_GETPROP); - pn2->pn_op = JSOP_GETMETHOD; - pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE; - } -#endif - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - /* - * Push the virtual machine's "obj" register, which was set by a - * name, property, or element get (or set) bytecode. - */ - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - - /* Remember start of callable-object bytecode for decompilation hint. */ - off = top; - - /* - * Emit code for each argument in order, then emit the JSOP_*CALL or - * JSOP_NEW bytecode with a two-byte immediate telling how many args - * were pushed on the operand stack. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0) - return JS_FALSE; - - argc = pn->pn_count - 1; - if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0) - return JS_FALSE; - break; - } - - case TOK_LEXICALSCOPE: - { - JSObject *obj; - jsint count; - - atom = pn->pn_atom; - obj = ATOM_TO_OBJECT(atom); - js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg)); - - OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth); - count = OBJ_BLOCK_COUNT(cx, obj); - cg->stackDepth += count; - if ((uintN)cg->stackDepth > cg->maxStackDepth) - cg->maxStackDepth = cg->stackDepth; - - /* - * If this lexical scope is not for a catch block, let block or let - * expression, or any kind of for loop (where the scope starts in the - * head after the first part if for (;;), else in the body if for-in); - * and if our container is top-level but not a function body, or else - * a block statement; then emit a SRC_BRACE note. All other container - * statements get braces by default from the decompiler. - */ - noteIndex = -1; - type = pn->pn_expr->pn_type; - if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR && - (!(stmt = stmtInfo.down) - ? !(cg->treeContext.flags & TCF_IN_FUNCTION) - : stmt->type == STMT_BLOCK)) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap - /* There must be no source note already output for the next op. */ - JS_ASSERT(CG_NOTE_COUNT(cg) == 0 || - CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) || - !GettableNoteForNextOp(cg)); -#endif - noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0); - if (noteIndex < 0) - return JS_FALSE; - } - - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - JS_ASSERT(CG_OFFSET(cg) == top); - EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale)); - - if (!js_EmitTree(cx, cg, pn->pn_expr)) - return JS_FALSE; - - op = pn->pn_op; - if (op == JSOP_LEAVEBLOCKEXPR) { - if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0) - return JS_FALSE; - } else { - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - top)) { - return JS_FALSE; - } - } - - /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */ - EMIT_UINT16_IMM_OP(op, count); - cg->stackDepth -= count; - - ok = js_PopStatementCG(cx, cg); - break; - } - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - /* Let statements have their variable declarations on the left. */ - if (pn->pn_arity == PN_BINARY) { - pn2 = pn->pn_right; - pn = pn->pn_left; - } else { - pn2 = NULL; - } - - /* Non-null pn2 means that pn is the variable list from a let head. */ - JS_ASSERT(pn->pn_arity == PN_LIST); - if (!EmitVariables(cx, cg, pn, pn2 != NULL, ¬eIndex)) - return JS_FALSE; - - /* Thus non-null pn2 is the body of the let block or expression. */ - tmp = CG_OFFSET(cg); - if (pn2 && !js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - - if (noteIndex >= 0 && - !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, - CG_OFFSET(cg) - tmp)) { - return JS_FALSE; - } - break; -#endif /* JS_HAS_BLOCK_SCOPE */ - -#if JS_HAS_GENERATORS - case TOK_ARRAYPUSH: - /* - * The array object's stack index is in cg->arrayCompSlot. See below - * under the array initialiser code generator for array comprehension - * special casing. - */ - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot); - break; -#endif - - case TOK_RB: -#if JS_HAS_GENERATORS - case TOK_ARRAYCOMP: -#endif - /* - * Emit code for [a, b, c] of the form: - * t = new Array; t[0] = a; t[1] = b; t[2] = c; t; - * but use a stack slot for t and avoid dup'ing and popping it via - * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. - */ - ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) - return JS_FALSE; - - pn2 = pn->pn_head; -#if JS_HAS_SHARP_VARS - if (pn2 && pn2->pn_type == TOK_DEFSHARP) { - EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); - pn2 = pn2->pn_next; - } -#endif - -#if JS_HAS_GENERATORS - if (pn->pn_type == TOK_ARRAYCOMP) { - uintN saveSlot; - - /* - * Pass the new array's stack index to the TOK_ARRAYPUSH case by - * storing it in pn->pn_extra, then simply traverse the TOK_FOR - * node and its kids under pn2 to generate this comprehension. - */ - JS_ASSERT(cg->stackDepth > 0); - saveSlot = cg->arrayCompSlot; - cg->arrayCompSlot = (uint32) (cg->stackDepth - 1); - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - cg->arrayCompSlot = saveSlot; - - /* Emit the usual op needed for decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - } -#endif /* JS_HAS_GENERATORS */ - - for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) { - if (!EmitNumberOp(cx, atomIndex, cg)) - return JS_FALSE; - - /* FIXME 260106: holes in a sparse initializer are void-filled. */ - if (pn2->pn_type == TOK_COMMA) { - if (js_Emit1(cx, cg, JSOP_PUSH) < 0) - return JS_FALSE; - } else { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - } - - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } - - if (pn->pn_extra & PNX_ENDCOMMA) { - /* Emit a source note so we know to decompile an extra comma. */ - if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0) - return JS_FALSE; - } - - /* Emit an op for sharp array cleanup and decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - - case TOK_RC: - /* - * Emit code for {p:a, '%q':b, 2:c} of the form: - * t = new Object; t.p = a; t['%q'] = b; t[2] = c; t; - * but use a stack slot for t and avoid dup'ing and popping it via - * the JSOP_NEWINIT and JSOP_INITELEM bytecodes. - */ - ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale)); - - if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0) - return JS_FALSE; - - pn2 = pn->pn_head; -#if JS_HAS_SHARP_VARS - if (pn2 && pn2->pn_type == TOK_DEFSHARP) { - EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num); - pn2 = pn2->pn_next; - } -#endif - - for (; pn2; pn2 = pn2->pn_next) { - /* Emit an index for t[2], else map an atom for t.p or t['%q']. */ - pn3 = pn2->pn_left; - switch (pn3->pn_type) { - case TOK_NUMBER: - if (!EmitNumberOp(cx, pn3->pn_dval, cg)) - return JS_FALSE; - break; - case TOK_NAME: - case TOK_STRING: - ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList); - if (!ale) - return JS_FALSE; - break; - default: - JS_ASSERT(0); - } - - /* Emit code for the property initializer. */ - if (!js_EmitTree(cx, cg, pn2->pn_right)) - return JS_FALSE; - -#if JS_HAS_GETTER_SETTER - op = pn2->pn_op; - if (op == JSOP_GETTER || op == JSOP_SETTER) { - if (pn3->pn_type != TOK_NUMBER && - ALE_INDEX(ale) >= JS_BIT(16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - } -#endif - /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */ - if (pn3->pn_type == TOK_NUMBER) { - if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_INITELEM) < 0) - return JS_FALSE; - } else { - EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale)); - } - } - - /* Emit an op for sharpArray cleanup and decompilation. */ - if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0) - return JS_FALSE; - break; - -#if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num); - break; - - case TOK_USESHARP: - EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num); - break; -#endif /* JS_HAS_SHARP_VARS */ - - case TOK_RP: - { - uintN oldflags; - - /* - * The node for (e) has e as its kid, enabling users who want to nest - * assignment expressions in conditions to avoid the error correction - * done by Condition (from x = y to x == y) by double-parenthesizing. - */ - oldflags = cg->treeContext.flags; - cg->treeContext.flags &= ~TCF_IN_FOR_INIT; - if (!js_EmitTree(cx, cg, pn->pn_kid)) - return JS_FALSE; - cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT; - if (js_Emit1(cx, cg, JSOP_GROUP) < 0) - return JS_FALSE; - break; - } - - case TOK_NAME: - if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) - return JS_FALSE; - op = pn->pn_op; - if (op == JSOP_ARGUMENTS) { - if (js_Emit1(cx, cg, op) < 0) - return JS_FALSE; - break; - } - if (pn->pn_slot >= 0) { - atomIndex = (jsatomid) pn->pn_slot; - EMIT_UINT16_IMM_OP(op, atomIndex); - break; - } - /* FALL THROUGH */ - -#if JS_HAS_XML_SUPPORT - case TOK_XMLATTR: - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: -#endif - case TOK_STRING: - case TOK_OBJECT: - /* - * The scanner and parser associate JSOP_NAME with TOK_NAME, although - * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME, - * JSOP_FORNAME, etc.). Among JSOP_*NAME* variants, only JSOP_NAME - * may generate the first operand of a call or new expression, so only - * it sets the "obj" virtual machine register to the object along the - * scope chain in which the name was found. - * - * Token types for STRING and OBJECT have corresponding bytecode ops - * in pn_op and emit the same format as NAME, so they share this code. - */ - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - break; - - case TOK_NUMBER: - ok = EmitNumberOp(cx, pn->pn_dval, cg); - break; - -#if JS_HAS_XML_SUPPORT - case TOK_ANYNAME: -#endif - case TOK_PRIMARY: - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - break; - -#if JS_HAS_DEBUGGER_KEYWORD - case TOK_DEBUGGER: - if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0) - return JS_FALSE; - break; -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - case TOK_XMLELEM: - case TOK_XMLLIST: - if (pn->pn_op == JSOP_XMLOBJECT) { - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - break; - } - - JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); - switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) { - case TOK_XMLETAGO: - JS_ASSERT(0); - /* FALL THROUGH */ - case TOK_XMLPTAGC: - case TOK_XMLSTAGO: - break; - default: - if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) - return JS_FALSE; - } - - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - } - - if (pn->pn_extra & PNX_XMLROOT) { - if (pn->pn_count == 0) { - JS_ASSERT(pn->pn_type == TOK_XMLLIST); - atom = cx->runtime->atomState.emptyAtom; - ale = js_IndexAtom(cx, atom, &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - } - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - } -#ifdef DEBUG - else - JS_ASSERT(pn->pn_count != 0); -#endif - break; - - case TOK_XMLPTAGC: - if (pn->pn_op == JSOP_XMLOBJECT) { - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - break; - } - /* FALL THROUGH */ - - case TOK_XMLSTAGO: - case TOK_XMLETAGO: - { - uint32 i; - - if (js_Emit1(cx, cg, JSOP_STARTXML) < 0) - return JS_FALSE; - - ale = js_IndexAtom(cx, - (pn->pn_type == TOK_XMLETAGO) - ? cx->runtime->atomState.etagoAtom - : cx->runtime->atomState.stagoAtom, - &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - - JS_ASSERT(pn->pn_count != 0); - pn2 = pn->pn_head; - if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) - return JS_FALSE; - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - - for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) { - if (pn2->pn_type == TOK_LC && - js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) { - return JS_FALSE; - } - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if ((i & 1) && pn2->pn_type == TOK_LC) { - if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0) - return JS_FALSE; - } - if (js_Emit1(cx, cg, - (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) { - return JS_FALSE; - } - } - - ale = js_IndexAtom(cx, - (pn->pn_type == TOK_XMLPTAGC) - ? cx->runtime->atomState.ptagcAtom - : cx->runtime->atomState.tagcAtom, - &cg->atomList); - if (!ale) - return JS_FALSE; - EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale)); - if (js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - - if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - break; - } - - case TOK_XMLNAME: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_count != 0); - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_EmitTree(cx, cg, pn2)) - return JS_FALSE; - if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0) - return JS_FALSE; - } - } else { - JS_ASSERT(pn->pn_arity == PN_NULLARY); - ok = EmitAtomOp(cx, pn, pn->pn_op, cg); - } - break; - - case TOK_XMLPI: - ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList); - if (!ale) - return JS_FALSE; - if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg)) - return JS_FALSE; - if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg)) - return JS_FALSE; - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default: - JS_ASSERT(0); - } - - if (ok && --cg->emitLevel == 0 && cg->spanDeps) - ok = OptimizeSpanDeps(cx, cg); - - return ok; -} - -/* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */ -JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = { - {"null", 0, 0, 0}, - {"if", 0, 0, 0}, - {"if-else", 2, 0, 1}, - {"while", 1, 0, 1}, - {"for", 3, 1, 1}, - {"continue", 0, 0, 0}, - {"decl", 1, 1, 1}, - {"pcdelta", 1, 0, 1}, - {"assignop", 0, 0, 0}, - {"cond", 1, 0, 1}, - {"brace", 1, 0, 1}, - {"hidden", 0, 0, 0}, - {"pcbase", 1, 0, -1}, - {"label", 1, 0, 0}, - {"labelbrace", 1, 0, 0}, - {"endbrace", 0, 0, 0}, - {"break2label", 1, 0, 0}, - {"cont2label", 1, 0, 0}, - {"switch", 2, 0, 1}, - {"funcdef", 1, 0, 0}, - {"catch", 1, 0, 1}, - {"extended", -1, 0, 0}, - {"newline", 0, 0, 0}, - {"setline", 1, 0, 0}, - {"xdelta", 0, 0, 0}, -}; - -static intN -AllocSrcNote(JSContext *cx, JSCodeGenerator *cg) -{ - intN index; - JSArenaPool *pool; - size_t size; - - index = CG_NOTE_COUNT(cg); - if (((uintN)index & CG_NOTE_MASK(cg)) == 0) { - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - if (!CG_NOTES(cg)) { - /* Allocate the first note array lazily; leave noteMask alone. */ - JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size); - } else { - /* Grow by doubling note array size; update noteMask on success. */ - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (CG_NOTES(cg)) - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; - } - if (!CG_NOTES(cg)) { - JS_ReportOutOfMemory(cx); - return -1; - } - } - - CG_NOTE_COUNT(cg) = index + 1; - return index; -} - -intN -js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type) -{ - intN index, n; - jssrcnote *sn; - ptrdiff_t offset, delta, xdelta; - - /* - * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then - * incrementing CG_NOTE_COUNT(cg). - */ - index = AllocSrcNote(cx, cg); - if (index < 0) - return -1; - sn = &CG_NOTES(cg)[index]; - - /* - * Compute delta from the last annotated bytecode's offset. If it's too - * big to fit in sn, allocate one or more xdelta notes and reset sn. - */ - offset = CG_OFFSET(cg); - delta = offset - CG_LAST_NOTE_OFFSET(cg); - CG_LAST_NOTE_OFFSET(cg) = offset; - if (delta >= SN_DELTA_LIMIT) { - do { - xdelta = JS_MIN(delta, SN_XDELTA_MASK); - SN_MAKE_XDELTA(sn, xdelta); - delta -= xdelta; - index = AllocSrcNote(cx, cg); - if (index < 0) - return -1; - sn = &CG_NOTES(cg)[index]; - } while (delta >= SN_DELTA_LIMIT); - } - - /* - * Initialize type and delta, then allocate the minimum number of notes - * needed for type's arity. Usually, we won't need more, but if an offset - * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg). - */ - SN_MAKE_NOTE(sn, type, delta); - for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) { - if (js_NewSrcNote(cx, cg, SRC_NULL) < 0) - return -1; - } - return index; -} - -intN -js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset) -{ - intN index; - - index = js_NewSrcNote(cx, cg, type); - if (index >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset)) - return -1; - } - return index; -} - -intN -js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset1, ptrdiff_t offset2) -{ - intN index; - - index = js_NewSrcNote(cx, cg, type); - if (index >= 0) { - if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1)) - return -1; - if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2)) - return -1; - } - return index; -} - -static JSBool -GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg) -{ - JSArenaPool *pool; - size_t size; - - /* Grow by doubling note array size; update noteMask on success. */ - pool = cg->notePool; - size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1); - JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size); - if (!CG_NOTES(cg)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1; - return JS_TRUE; -} - -jssrcnote * -js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, - ptrdiff_t delta) -{ - ptrdiff_t base, limit, newdelta, diff; - intN index; - - /* - * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to - * main script note deltas, and only by a small positive amount. - */ - JS_ASSERT(cg->current == &cg->main); - JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT); - - base = SN_DELTA(sn); - limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT; - newdelta = base + delta; - if (newdelta < limit) { - SN_SET_DELTA(sn, newdelta); - } else { - index = sn - cg->main.notes; - if ((cg->main.noteCount & cg->main.noteMask) == 0) { - if (!GrowSrcNotes(cx, cg)) - return NULL; - sn = cg->main.notes + index; - } - diff = cg->main.noteCount - index; - cg->main.noteCount++; - memmove(sn + 1, sn, SRCNOTE_SIZE(diff)); - SN_MAKE_XDELTA(sn, delta); - sn++; - } - return sn; -} - -JS_FRIEND_API(uintN) -js_SrcNoteLength(jssrcnote *sn) -{ - uintN arity; - jssrcnote *base; - - arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity; - for (base = sn++; arity; sn++, arity--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - return sn - base; -} - -JS_FRIEND_API(ptrdiff_t) -js_GetSrcNoteOffset(jssrcnote *sn, uintN which) -{ - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - if (*sn & SN_3BYTE_OFFSET_FLAG) { - return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16) - | (sn[1] << 8) - | sn[2]); - } - return (ptrdiff_t)*sn; -} - -JSBool -js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, - uintN which, ptrdiff_t offset) -{ - jssrcnote *sn; - ptrdiff_t diff; - - if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) { - ReportStatementTooLarge(cx, cg); - return JS_FALSE; - } - - /* Find the offset numbered which (i.e., skip exactly which offsets). */ - sn = &CG_NOTES(cg)[index]; - JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA); - JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity); - for (sn++; which; sn++, which--) { - if (*sn & SN_3BYTE_OFFSET_FLAG) - sn += 2; - } - - /* See if the new offset requires three bytes. */ - if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) { - /* Maybe this offset was already set to a three-byte value. */ - if (!(*sn & SN_3BYTE_OFFSET_FLAG)) { - /* Losing, need to insert another two bytes for this offset. */ - index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote); - - /* - * Simultaneously test to see if the source note array must grow to - * accomodate either the first or second byte of additional storage - * required by this 3-byte offset. - */ - if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) { - if (!GrowSrcNotes(cx, cg)) - return JS_FALSE; - sn = CG_NOTES(cg) + index; - } - CG_NOTE_COUNT(cg) += 2; - - diff = CG_NOTE_COUNT(cg) - (index + 3); - JS_ASSERT(diff >= 0); - if (diff > 0) - memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff)); - } - *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16)); - *sn++ = (jssrcnote)(offset >> 8); - } - *sn = (jssrcnote)offset; - return JS_TRUE; -} - -#ifdef DEBUG_notme -#define DEBUG_srcnotesize -#endif - -#ifdef DEBUG_srcnotesize -#define NBINS 10 -static uint32 hist[NBINS]; - -void DumpSrcNoteSizeHist() -{ - static FILE *fp; - int i, n; - - if (!fp) { - fp = fopen("/tmp/srcnotes.hist", "w"); - if (!fp) - return; - setvbuf(fp, NULL, _IONBF, 0); - } - fprintf(fp, "SrcNote size histogram:\n"); - for (i = 0; i < NBINS; i++) { - fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]); - for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n) - fputc('*', fp); - fputc('\n', fp); - } - fputc('\n', fp); -} -#endif - -/* - * Fill in the storage at notes with prolog and main srcnotes; the space at - * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h. - * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's - * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES! - */ -JSBool -js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes) -{ - uintN prologCount, mainCount, totalCount; - ptrdiff_t offset, delta; - jssrcnote *sn; - - JS_ASSERT(cg->current == &cg->main); - - prologCount = cg->prolog.noteCount; - if (prologCount && cg->prolog.currentLine != cg->firstLine) { - CG_SWITCH_TO_PROLOG(cg); - if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0) - return JS_FALSE; - prologCount = cg->prolog.noteCount; - CG_SWITCH_TO_MAIN(cg); - } else { - /* - * Either no prolog srcnotes, or no line number change over prolog. - * We don't need a SRC_SETLINE, but we may need to adjust the offset - * of the first main note, by adding to its delta and possibly even - * prepending SRC_XDELTA notes to it to account for prolog bytecodes - * that came at and after the last annotated bytecode. - */ - offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset; - JS_ASSERT(offset >= 0); - if (offset > 0 && cg->main.noteCount != 0) { - /* NB: Use as much of the first main note's delta as we can. */ - sn = cg->main.notes; - delta = SN_IS_XDELTA(sn) - ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK) - : SN_DELTA_MASK - (*sn & SN_DELTA_MASK); - if (offset < delta) - delta = offset; - for (;;) { - if (!js_AddToSrcNoteDelta(cx, cg, sn, delta)) - return JS_FALSE; - offset -= delta; - if (offset == 0) - break; - delta = JS_MIN(offset, SN_XDELTA_MASK); - sn = cg->main.notes; - } - } - } - - mainCount = cg->main.noteCount; - totalCount = prologCount + mainCount; - if (prologCount) - memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount)); - memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount)); - SN_MAKE_TERMINATOR(¬es[totalCount]); - -#ifdef DEBUG_notme - { int bin = JS_CeilingLog2(totalCount); - if (bin >= NBINS) - bin = NBINS - 1; - ++hist[bin]; - } -#endif - return JS_TRUE; -} - -JSBool -js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg) -{ - size_t size, incr; - ptrdiff_t delta; - - size = TRYNOTE_SIZE(cg->treeContext.tryCount); - if (size <= cg->tryNoteSpace) - return JS_TRUE; - - /* - * Allocate trynotes from cx->tempPool. - * XXX Too much growing and we bloat, as other tempPool allocators block - * in-place growth, and we never recycle old free space in an arena. - * YYY But once we consume an entire arena, we'll realloc it, letting the - * malloc heap recycle old space, while still freeing _en masse_ via the - * arena pool. - */ - if (!cg->tryBase) { - size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK)); - JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size); - if (!cg->tryBase) - return JS_FALSE; - cg->tryNoteSpace = size; - cg->tryNext = cg->tryBase; - } else { - delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char); - incr = size - cg->tryNoteSpace; - incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK)); - size = cg->tryNoteSpace; - JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr); - if (!cg->tryBase) - return JS_FALSE; - cg->tryNoteSpace = size + incr; - cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta); - } - return JS_TRUE; -} - -JSTryNote * -js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, - ptrdiff_t end, ptrdiff_t catchStart) -{ - JSTryNote *tn; - - JS_ASSERT(cg->tryBase <= cg->tryNext); - JS_ASSERT(catchStart >= 0); - tn = cg->tryNext++; - tn->start = start; - tn->length = end - start; - tn->catchStart = catchStart; - return tn; -} - -void -js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes) -{ - uintN count; - - count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote); - if (!count) - return; - - memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count)); - notes[count].start = 0; - notes[count].length = CG_OFFSET(cg); - notes[count].catchStart = 0; -} diff --git a/spidermonkey/libjs/jsemit.h b/spidermonkey/libjs/jsemit.h deleted file mode 100644 index 90709c2..0000000 --- a/spidermonkey/libjs/jsemit.h +++ /dev/null @@ -1,743 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsemit_h___ -#define jsemit_h___ -/* - * JS bytecode generation. - */ - -#include "jsstddef.h" -#include "jstypes.h" -#include "jsatom.h" -#include "jsopcode.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * NB: If you add enumerators for scope statements, add them between STMT_WITH - * and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add - * non-looping statement enumerators, add them before STMT_DO_LOOP or you will - * break the STMT_TYPE_IS_LOOP macro. - * - * Also remember to keep the statementName array in jsemit.c in sync. - */ -typedef enum JSStmtType { - STMT_LABEL, /* labeled statement: L: s */ - STMT_IF, /* if (then) statement */ - STMT_ELSE, /* else clause of if statement */ - STMT_BODY, /* synthetic body of function with - destructuring formal parameters */ - STMT_BLOCK, /* compound statement: { s1[;... sN] } */ - STMT_SWITCH, /* switch statement */ - STMT_WITH, /* with statement */ - STMT_CATCH, /* catch block */ - STMT_TRY, /* try block */ - STMT_FINALLY, /* finally block */ - STMT_SUBROUTINE, /* gosub-target subroutine body */ - STMT_DO_LOOP, /* do/while loop statement */ - STMT_FOR_LOOP, /* for loop statement */ - STMT_FOR_IN_LOOP, /* for/in loop statement */ - STMT_WHILE_LOOP /* while loop statement */ -} JSStmtType; - -#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) - -/* - * A comment on the encoding of the JSStmtType enum and type-testing macros: - * - * STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may - * become, a lexical scope. It therefore includes block and switch (the two - * low-numbered "maybe" scope types) and excludes with (with has dynamic scope - * pending the "reformed with" in ES4/JS2). It includes all try-catch-finally - * types, which are high-numbered maybe-scope types. - * - * STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly - * links to other scoping statement info records. It excludes the two early - * "maybe" types, block and switch, as well as the try and both finally types, - * since try and the other trailing maybe-scope types don't need block scope - * unless they contain let declarations. - * - * We treat with as a static scope because it prevents lexical binding from - * continuing further up the static scope chain. With the "reformed with" - * proposal for JS2, we'll be able to model it statically, too. - */ -#define STMT_TYPE_MAYBE_SCOPE(type) \ - (type != STMT_WITH && \ - STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) - -#define STMT_TYPE_LINKS_SCOPE(type) \ - STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) - -#define STMT_TYPE_IS_TRYING(type) \ - STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) - -#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) - -#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) -#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ - ((stmt)->flags & SIF_SCOPE)) -#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) -#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) - -typedef struct JSStmtInfo JSStmtInfo; - -struct JSStmtInfo { - uint16 type; /* statement type */ - uint16 flags; /* flags, see below */ - ptrdiff_t update; /* loop update offset (top if none) */ - ptrdiff_t breaks; /* offset of last break in loop */ - ptrdiff_t continues; /* offset of last continue in loop */ - JSAtom *atom; /* name of LABEL, or block scope object */ - JSStmtInfo *down; /* info for enclosing statement */ - JSStmtInfo *downScope; /* next enclosing lexical scope */ -}; - -#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ -#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ - -/* - * To reuse space in JSStmtInfo, rename breaks and continues for use during - * try/catch/finally code generation and backpatching. To match most common - * use cases, the macro argument is a struct, not a struct pointer. Only a - * loop, switch, or label statement info record can have breaks and continues, - * and only a for loop has an update backpatch chain, so it's safe to overlay - * these for the "trying" JSStmtTypes. - */ -#define CATCHNOTE(stmt) ((stmt).update) -#define GOSUBS(stmt) ((stmt).breaks) -#define GUARDJUMP(stmt) ((stmt).continues) - -#define AT_TOP_LEVEL(tc) \ - (!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK)) - -#define SET_STATEMENT_TOP(stmt, top) \ - ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) - -struct JSTreeContext { /* tree context for semantic checks */ - uint16 flags; /* statement state flags, see below */ - uint16 numGlobalVars; /* max. no. of global variables/regexps */ - uint32 tryCount; /* total count of try statements parsed */ - uint32 globalUses; /* optimizable global var uses in total */ - uint32 loopyGlobalUses;/* optimizable global var uses in loops */ - JSStmtInfo *topStmt; /* top of statement info stack */ - JSStmtInfo *topScopeStmt; /* top lexical scope statement */ - JSObject *blockChain; /* compile time block scope chain (NB: one - deeper than the topScopeStmt/downScope - chain when in head of let block/expr) */ - JSParseNode *blockNode; /* parse node for a lexical scope. - XXX combine with blockChain? */ - JSAtomList decls; /* function, const, and var declarations */ - JSParseNode *nodeList; /* list of recyclable parse-node structs */ -}; - -#define TCF_COMPILING 0x01 /* generating bytecode; this tc is a cg */ -#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */ -#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */ -#define TCF_RETURN_VOID 0x08 /* function has 'return;' */ -#define TCF_RETURN_FLAGS 0x0C /* propagate these out of blocks */ -#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */ -#define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */ -#define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */ -#define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */ -#define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */ -#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */ -#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */ -#define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */ - -#define TREE_CONTEXT_INIT(tc) \ - ((tc)->flags = (tc)->numGlobalVars = 0, \ - (tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \ - (tc)->topStmt = (tc)->topScopeStmt = NULL, \ - (tc)->blockChain = NULL, \ - ATOM_LIST_INIT(&(tc)->decls), \ - (tc)->nodeList = NULL, (tc)->blockNode = NULL) - -#define TREE_CONTEXT_FINISH(tc) \ - ((void)0) - -/* - * Span-dependent instructions are jumps whose span (from the jump bytecode to - * the jump target) may require 2 or 4 bytes of immediate operand. - */ -typedef struct JSSpanDep JSSpanDep; -typedef struct JSJumpTarget JSJumpTarget; - -struct JSSpanDep { - ptrdiff_t top; /* offset of first bytecode in an opcode */ - ptrdiff_t offset; /* offset - 1 within opcode of jump operand */ - ptrdiff_t before; /* original offset - 1 of jump operand */ - JSJumpTarget *target; /* tagged target pointer or backpatch delta */ -}; - -/* - * Jump targets are stored in an AVL tree, for O(log(n)) lookup with targets - * sorted by offset from left to right, so that targets after a span-dependent - * instruction whose jump offset operand must be extended can be found quickly - * and adjusted upward (toward higher offsets). - */ -struct JSJumpTarget { - ptrdiff_t offset; /* offset of span-dependent jump target */ - int balance; /* AVL tree balance number */ - JSJumpTarget *kids[2]; /* left and right AVL tree child pointers */ -}; - -#define JT_LEFT 0 -#define JT_RIGHT 1 -#define JT_OTHER_DIR(dir) (1 - (dir)) -#define JT_IMBALANCE(dir) (((dir) << 1) - 1) -#define JT_DIR(imbalance) (((imbalance) + 1) >> 1) - -/* - * Backpatch deltas are encoded in JSSpanDep.target if JT_TAG_BIT is clear, - * so we can maintain backpatch chains when using span dependency records to - * hold jump offsets that overflow 16 bits. - */ -#define JT_TAG_BIT ((jsword) 1) -#define JT_UNTAG_SHIFT 1 -#define JT_SET_TAG(jt) ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT)) -#define JT_CLR_TAG(jt) ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT)) -#define JT_HAS_TAG(jt) ((jsword)(jt) & JT_TAG_BIT) - -#define BITS_PER_PTRDIFF (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE) -#define BITS_PER_BPDELTA (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT) -#define BPDELTA_MAX (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1) -#define BPDELTA_TO_JT(bp) ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT)) -#define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) - -#define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) -#define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ - JT_CLR_TAG((sd)->target)) -#define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) -#define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ - JT_TO_BPDELTA((sd)->target)) - -/* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ -#define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ - ? JT_CLR_TAG((sd)->target)->offset - (pivot) \ - : 0) - -struct JSCodeGenerator { - JSTreeContext treeContext; /* base state: statement info stack, etc. */ - - JSArenaPool *codePool; /* pointer to thread code arena pool */ - JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ - void *codeMark; /* low watermark in cg->codePool */ - void *noteMark; /* low watermark in cg->notePool */ - void *tempMark; /* low watermark in cx->tempPool */ - - struct { - jsbytecode *base; /* base of JS bytecode vector */ - jsbytecode *limit; /* one byte beyond end of bytecode */ - jsbytecode *next; /* pointer to next free bytecode */ - jssrcnote *notes; /* source notes, see below */ - uintN noteCount; /* number of source notes so far */ - uintN noteMask; /* growth increment for notes */ - ptrdiff_t lastNoteOffset; /* code offset for last source note */ - uintN currentLine; /* line number for tree-based srcnote gen */ - } prolog, main, *current; - - const char *filename; /* null or weak link to source filename */ - uintN firstLine; /* first line, for js_NewScriptFromCG */ - JSPrincipals *principals; /* principals for constant folding eval */ - JSAtomList atomList; /* literals indexed for mapping */ - - intN stackDepth; /* current stack depth in script frame */ - uintN maxStackDepth; /* maximum stack depth so far */ - - JSTryNote *tryBase; /* first exception handling note */ - JSTryNote *tryNext; /* next available note */ - size_t tryNoteSpace; /* # of bytes allocated at tryBase */ - - JSSpanDep *spanDeps; /* span dependent instruction records */ - JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */ - JSJumpTarget *jtFreeList; /* JT_LEFT-linked list of free structs */ - uintN numSpanDeps; /* number of span dependencies */ - uintN numJumpTargets; /* number of jump targets */ - ptrdiff_t spanDepTodo; /* offset from main.base of potentially - unoptimized spandeps */ - - uintN arrayCompSlot; /* stack slot of array in comprehension */ - - uintN emitLevel; /* js_EmitTree recursion level */ - JSAtomList constList; /* compile time constants */ - JSCodeGenerator *parent; /* Enclosing function or global context */ -}; - -#define CG_BASE(cg) ((cg)->current->base) -#define CG_LIMIT(cg) ((cg)->current->limit) -#define CG_NEXT(cg) ((cg)->current->next) -#define CG_CODE(cg,offset) (CG_BASE(cg) + (offset)) -#define CG_OFFSET(cg) PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode) - -#define CG_NOTES(cg) ((cg)->current->notes) -#define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) -#define CG_NOTE_MASK(cg) ((cg)->current->noteMask) -#define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) -#define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) - -#define CG_PROLOG_BASE(cg) ((cg)->prolog.base) -#define CG_PROLOG_LIMIT(cg) ((cg)->prolog.limit) -#define CG_PROLOG_NEXT(cg) ((cg)->prolog.next) -#define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff)) -#define CG_PROLOG_OFFSET(cg) PTRDIFF(CG_PROLOG_NEXT(cg), CG_PROLOG_BASE(cg),\ - jsbytecode) - -#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) -#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) - -/* - * Initialize cg to allocate bytecode space from codePool, source note space - * from notePool, and all other arena-allocated temporaries from cx->tempPool. - * Return true on success. Report an error and return false if the initial - * code segment can't be allocated. - */ -extern JS_FRIEND_API(JSBool) -js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, - JSArenaPool *codePool, JSArenaPool *notePool, - const char *filename, uintN lineno, - JSPrincipals *principals); - -/* - * Release cg->codePool, cg->notePool, and cx->tempPool to marks set by - * js_InitCodeGenerator. Note that cgs are magic: they own the arena pool - * "tops-of-stack" space above their codeMark, noteMark, and tempMark points. - * This means you cannot alloc from tempPool and save the pointer beyond the - * next JS_FinishCodeGenerator. - */ -extern JS_FRIEND_API(void) -js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg); - -/* - * Emit one bytecode. - */ -extern ptrdiff_t -js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op); - -/* - * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). - */ -extern ptrdiff_t -js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1); - -/* - * Emit three bytecodes, an opcode with two bytes of immediate operands. - */ -extern ptrdiff_t -js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, - jsbytecode op2); - -/* - * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. - */ -extern ptrdiff_t -js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); - -/* - * Unsafe macro to call js_SetJumpOffset and return false if it does. - */ -#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off) \ - JS_BEGIN_MACRO \ - if (!js_SetJumpOffset(cx, cg, pc, off)) \ - return JS_FALSE; \ - JS_END_MACRO - -#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ - CHECK_AND_SET_JUMP_OFFSET(cx, cg, CG_CODE(cg,off), CG_OFFSET(cg) - (off)) - -extern JSBool -js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, - ptrdiff_t off); - -/* Test whether we're in a statement of given type. */ -extern JSBool -js_InStatement(JSTreeContext *tc, JSStmtType type); - -/* Test whether we're in a with statement. */ -#define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH) - -/* - * Test whether atom refers to a global variable (or is a reference error). - * Return true in *loopyp if any loops enclose the lexical reference, false - * otherwise. - */ -extern JSBool -js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp); - -/* - * Push the C-stack-allocated struct at stmt onto the stmtInfo stack. - */ -extern void -js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, - ptrdiff_t top); - -/* - * Push a block scope statement and link blockAtom's object-valued key into - * tc->blockChain. To pop this statement info record, use js_PopStatement as - * usual, or if appropriate (if generating code), js_PopStatementCG. - */ -extern void -js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom, - ptrdiff_t top); - -/* - * Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it - * is up to the caller to free it. - */ -extern void -js_PopStatement(JSTreeContext *tc); - -/* - * Like js_PopStatement(&cg->treeContext), also patch breaks and continues - * unless the top statement info record represents a try-catch-finally suite. - * May fail if a jump offset overflows. - */ -extern JSBool -js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); - -/* - * Define and lookup a primitive jsval associated with the const named by atom. - * js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn - * and saves the const's value in cg->constList, if it can be used at compile - * time. It returns true unless an error occurred. - * - * If the initializer's value could not be saved, js_LookupCompileTimeConstant - * calls will return the undefined value. js_LookupCompileTimeConstant tries - * to find a const value memorized for atom, returning true with *vp set to a - * value other than undefined if the constant was found, true with *vp set to - * JSVAL_VOID if not found, and false on error. - */ -extern JSBool -js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - JSParseNode *pn); - -extern JSBool -js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, - jsval *vp); - -/* - * Find a lexically scoped variable (one declared by let, catch, or an array - * comprehension) named by atom, looking in tc's compile-time scopes. - * - * If a WITH statement is reached along the scope stack, return its statement - * info record, so callers can tell that atom is ambiguous. If slotp is not - * null, then if atom is found, set *slotp to its stack slot, otherwise to -1. - * This means that if slotp is not null, all the block objects on the lexical - * scope chain must have had their depth slots computed by the code generator, - * so the caller must be under js_EmitTree. - * - * In any event, directly return the statement info record in which atom was - * found. Otherwise return null. - */ -extern JSStmtInfo * -js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, - JSBool letdecl); - -/* - * Emit code into cg for the tree rooted at pn. - */ -extern JSBool -js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); - -/* - * Emit function code into cg for the tree rooted at body. - */ -extern JSBool -js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); - -/* - * Emit code into cg for the tree rooted at body, then create a persistent - * script for fun from cg. - */ -extern JSBool -js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, - JSFunction *fun); - -/* - * Source notes generated along with bytecode for decompiling and debugging. - * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of - * the previous note. If 3 bits of offset aren't enough, extended delta notes - * (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits - * are emitted before the next note. Some notes have operand offsets encoded - * immediately after them, in note bytes or byte-triples. - * - * Source Note Extended Delta - * +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+ - * |note-type|delta| |1 1| ext-delta | - * +---------+-----+ +---+-----------+ - * - * At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, - * SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. - * - * NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its - * initializers need to match the order here. - * - * Note on adding new source notes: every pair of bytecodes (A, B) where A and - * B have disjoint sets of source notes that could apply to each bytecode may - * reuse the same note type value for two notes (snA, snB) that have the same - * arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is - * why SRC_IF and SRC_INITPROP have the same value below. For bad historical - * reasons, some bytecodes below that could be overlayed have not been, but - * before using SRC_EXTENDED, consider compressing the existing note types. - * - * Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such - * incompatible source note or other bytecode changes. - */ -typedef enum JSSrcNoteType { - SRC_NULL = 0, /* terminates a note vector */ - SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ - SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or - to an index label in a regular (structuring) - or a destructuring object initialiser */ - SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ - SRC_WHILE = 3, /* JSOP_IFEQ is from a while loop */ - SRC_FOR = 4, /* JSOP_NOP or JSOP_POP in for loop head */ - SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; - also used on JSOP_ENDINIT if extra comma - at end of array literal: [1,2,,] */ - SRC_DECL = 6, /* type of a declaration (var, const, let*) */ - SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment - operation, with SRC_DECL_* offset operand */ - SRC_PCDELTA = 7, /* distance forward from comma-operator to - next POP, or from CONDSWITCH to first CASE - opcode, etc. -- always a forward delta */ - SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ - SRC_ASSIGNOP = 8, /* += or another assign-op follows */ - SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ - SRC_BRACE = 10, /* mandatory brace, for scope or to avoid - dangling else */ - SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ - SRC_PCBASE = 12, /* distance back from annotated getprop or - setprop op to left-most obj.prop.subprop - bytecode -- always a backward delta */ - SRC_METHODBASE = 13, /* SRC_PCBASE variant for obj.function::foo - gets and sets; disjoint from SRC_LABEL by - bytecode to which it applies */ - SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ - SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ - SRC_ENDBRACE = 15, /* JSOP_NOP for label: {...} end brace */ - SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ - SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ - SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch, - 2nd off to first JSOP_CASE if condswitch */ - SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ - SRC_CATCH = 20, /* catch block has guard */ - SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ - SRC_NEWLINE = 22, /* bytecode follows a source newline */ - SRC_SETLINE = 23, /* a file-absolute source line number note */ - SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ -} JSSrcNoteType; - -/* - * Constants for the SRC_DECL source note. Note that span-dependent bytecode - * selection means that any SRC_DECL offset greater than SRC_DECL_LET may need - * to be adjusted, but these "offsets" are too small to span a span-dependent - * instruction, so can be used to denote distinct declaration syntaxes to the - * decompiler. - * - * NB: the var_prefix array in jsopcode.c depends on these dense indexes from - * SRC_DECL_VAR through SRC_DECL_LET. - */ -#define SRC_DECL_VAR 0 -#define SRC_DECL_CONST 1 -#define SRC_DECL_LET 2 -#define SRC_DECL_NONE 3 - -#define SN_TYPE_BITS 5 -#define SN_DELTA_BITS 3 -#define SN_XDELTA_BITS 6 -#define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS) -#define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS)) -#define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS)) - -#define SN_MAKE_NOTE(sn,t,d) (*(sn) = (jssrcnote) \ - (((t) << SN_DELTA_BITS) \ - | ((d) & SN_DELTA_MASK))) -#define SN_MAKE_XDELTA(sn,d) (*(sn) = (jssrcnote) \ - ((SRC_XDELTA << SN_DELTA_BITS) \ - | ((d) & SN_XDELTA_MASK))) - -#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) -#define SN_TYPE(sn) (SN_IS_XDELTA(sn) ? SRC_XDELTA \ - : *(sn) >> SN_DELTA_BITS) -#define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) -#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE) - -#define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \ - ? *(sn) & SN_XDELTA_MASK \ - : *(sn) & SN_DELTA_MASK)) -#define SN_SET_DELTA(sn,delta) (SN_IS_XDELTA(sn) \ - ? SN_MAKE_XDELTA(sn, delta) \ - : SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) - -#define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS)) -#define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS)) - -/* - * Offset fields follow certain notes and are frequency-encoded: an offset in - * [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and - * the high bit of the first byte is set. - */ -#define SN_3BYTE_OFFSET_FLAG 0x80 -#define SN_3BYTE_OFFSET_MASK 0x7f - -typedef struct JSSrcNoteSpec { - const char *name; /* name for disassembly/debugging output */ - uint8 arity; /* number of offset operands */ - uint8 offsetBias; /* bias of offset(s) from annotated pc */ - int8 isSpanDep; /* 1 or -1 if offsets could span extended ops, - 0 otherwise; sign tells span direction */ -} JSSrcNoteSpec; - -extern JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[]; -extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn); - -#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ - : js_SrcNoteLength(sn)) -#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) - -/* A source note array is terminated by an all-zero element. */ -#define SN_MAKE_TERMINATOR(sn) (*(sn) = SRC_NULL) -#define SN_IS_TERMINATOR(sn) (*(sn) == SRC_NULL) - -/* - * Append a new source note of the given type (and therefore size) to cg's - * notes dynamic array, updating cg->noteCount. Return the new note's index - * within the array pointed at by cg->current->notes. Return -1 if out of - * memory. - */ -extern intN -js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type); - -extern intN -js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset); - -extern intN -js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, - ptrdiff_t offset1, ptrdiff_t offset2); - -/* - * NB: this function can add at most one extra extended delta note. - */ -extern jssrcnote * -js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, - ptrdiff_t delta); - -/* - * Get and set the offset operand identified by which (0 for the first, etc.). - */ -extern JS_FRIEND_API(ptrdiff_t) -js_GetSrcNoteOffset(jssrcnote *sn, uintN which); - -extern JSBool -js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, - uintN which, ptrdiff_t offset); - -/* - * Finish taking source notes in cx's notePool, copying final notes to the new - * stable store allocated by the caller and passed in via notes. Return false - * on malloc failure, which means this function reported an error. - * - * To compute the number of jssrcnotes to allocate and pass in via notes, use - * the CG_COUNT_FINAL_SRCNOTES macro. This macro knows a lot about details of - * js_FinishTakingSrcNotes, SO DON'T CHANGE jsemit.c's js_FinishTakingSrcNotes - * FUNCTION WITHOUT CHECKING WHETHER THIS MACRO NEEDS CORRESPONDING CHANGES! - */ -#define CG_COUNT_FINAL_SRCNOTES(cg, cnt) \ - JS_BEGIN_MACRO \ - ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \ - cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1; \ - if ((cg)->prolog.noteCount && \ - (cg)->prolog.currentLine != (cg)->firstLine) { \ - if (diff_ > SN_DELTA_MASK) \ - cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK); \ - cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1); \ - } else if (diff_ > 0) { \ - if (cg->main.noteCount) { \ - jssrcnote *sn_ = (cg)->main.notes; \ - diff_ -= SN_IS_XDELTA(sn_) \ - ? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK) \ - : SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK); \ - } \ - if (diff_ > 0) \ - cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK); \ - } \ - JS_END_MACRO - -extern JSBool -js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes); - -/* - * Allocate cg->treeContext.tryCount notes (plus one for the end sentinel) - * from cx->tempPool and set up cg->tryBase/tryNext for exactly tryCount - * js_NewTryNote calls. The storage is freed by js_FinishCodeGenerator. - */ -extern JSBool -js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg); - -/* - * Grab the next trynote slot in cg, filling it in appropriately. - */ -extern JSTryNote * -js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start, - ptrdiff_t end, ptrdiff_t catchStart); - -/* - * Finish generating exception information into the space at notes. As with - * js_FinishTakingSrcNotes, the caller must use CG_COUNT_FINAL_TRYNOTES(cg) to - * preallocate enough space in a JSTryNote[] to pass as the notes parameter of - * js_FinishTakingTryNotes. - */ -#define CG_COUNT_FINAL_TRYNOTES(cg, cnt) \ - JS_BEGIN_MACRO \ - cnt = ((cg)->tryNext > (cg)->tryBase) \ - ? PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote) + 1 \ - : 0; \ - JS_END_MACRO - -extern void -js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes); - -JS_END_EXTERN_C - -#endif /* jsemit_h___ */ diff --git a/spidermonkey/libjs/jsexn.c b/spidermonkey/libjs/jsexn.c deleted file mode 100644 index e60f85e..0000000 --- a/spidermonkey/libjs/jsexn.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS standard exception implementation. - */ - -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsinterp.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsscript.h" - -/* Forward declarations for js_ErrorClass's initializer. */ -static JSBool -Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -static void -exn_finalize(JSContext *cx, JSObject *obj); - -static uint32 -exn_mark(JSContext *cx, JSObject *obj, void *arg); - -static void -exn_finalize(JSContext *cx, JSObject *obj); - -static JSBool -exn_enumerate(JSContext *cx, JSObject *obj); - -static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp); - -JSClass js_ErrorClass = { - js_Error_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_Error), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - exn_enumerate, (JSResolveOp)exn_resolve, JS_ConvertStub, exn_finalize, - NULL, NULL, NULL, Exception, - NULL, NULL, exn_mark, NULL -}; - -typedef struct JSStackTraceElem { - JSString *funName; - size_t argc; - const char *filename; - uintN ulineno; -} JSStackTraceElem; - -typedef struct JSExnPrivate { - /* A copy of the JSErrorReport originally generated. */ - JSErrorReport *errorReport; - JSString *message; - JSString *filename; - uintN lineno; - size_t stackDepth; - JSStackTraceElem stackElems[1]; -} JSExnPrivate; - -static JSString * -StackTraceToString(JSContext *cx, JSExnPrivate *priv); - -static JSErrorReport * -CopyErrorReport(JSContext *cx, JSErrorReport *report) -{ - /* - * We use a single malloc block to make a deep copy of JSErrorReport with - * the following layout: - * JSErrorReport - * array of copies of report->messageArgs - * jschar array with characters for all messageArgs - * jschar array with characters for ucmessage - * jschar array with characters for uclinebuf and uctokenptr - * char array with characters for linebuf and tokenptr - * char array with characters for filename - * Such layout together with the properties enforced by the following - * asserts does not need any extra alignment padding. - */ - JS_STATIC_ASSERT(sizeof(JSErrorReport) % sizeof(const char *) == 0); - JS_STATIC_ASSERT(sizeof(const char *) % sizeof(jschar) == 0); - - size_t filenameSize; - size_t linebufSize; - size_t uclinebufSize; - size_t ucmessageSize; - size_t i, argsArraySize, argsCopySize, argSize; - size_t mallocSize; - JSErrorReport *copy; - uint8 *cursor; - -#define JS_CHARS_SIZE(jschars) ((js_strlen(jschars) + 1) * sizeof(jschar)) - - filenameSize = report->filename ? strlen(report->filename) + 1 : 0; - linebufSize = report->linebuf ? strlen(report->linebuf) + 1 : 0; - uclinebufSize = report->uclinebuf ? JS_CHARS_SIZE(report->uclinebuf) : 0; - ucmessageSize = 0; - argsArraySize = 0; - argsCopySize = 0; - if (report->ucmessage) { - ucmessageSize = JS_CHARS_SIZE(report->ucmessage); - if (report->messageArgs) { - for (i = 0; report->messageArgs[i]; ++i) - argsCopySize += JS_CHARS_SIZE(report->messageArgs[i]); - - /* Non-null messageArgs should have at least one non-null arg. */ - JS_ASSERT(i != 0); - argsArraySize = (i + 1) * sizeof(const jschar *); - } - } - - /* - * The mallocSize can not overflow since it represents the sum of the - * sizes of already allocated objects. - */ - mallocSize = sizeof(JSErrorReport) + argsArraySize + argsCopySize + - ucmessageSize + uclinebufSize + linebufSize + filenameSize; - cursor = (uint8 *)JS_malloc(cx, mallocSize); - if (!cursor) - return NULL; - - copy = (JSErrorReport *)cursor; - memset(cursor, 0, sizeof(JSErrorReport)); - cursor += sizeof(JSErrorReport); - - if (argsArraySize != 0) { - copy->messageArgs = (const jschar **)cursor; - cursor += argsArraySize; - for (i = 0; report->messageArgs[i]; ++i) { - copy->messageArgs[i] = (const jschar *)cursor; - argSize = JS_CHARS_SIZE(report->messageArgs[i]); - memcpy(cursor, report->messageArgs[i], argSize); - cursor += argSize; - } - copy->messageArgs[i] = NULL; - JS_ASSERT(cursor == (uint8 *)copy->messageArgs[0] + argsCopySize); - } - - if (report->ucmessage) { - copy->ucmessage = (const jschar *)cursor; - memcpy(cursor, report->ucmessage, ucmessageSize); - cursor += ucmessageSize; - } - - if (report->uclinebuf) { - copy->uclinebuf = (const jschar *)cursor; - memcpy(cursor, report->uclinebuf, uclinebufSize); - cursor += uclinebufSize; - if (report->uctokenptr) { - copy->uctokenptr = copy->uclinebuf + (report->uctokenptr - - report->uclinebuf); - } - } - - if (report->linebuf) { - copy->linebuf = (const char *)cursor; - memcpy(cursor, report->linebuf, linebufSize); - cursor += linebufSize; - if (report->tokenptr) { - copy->tokenptr = copy->linebuf + (report->tokenptr - - report->linebuf); - } - } - - if (report->filename) { - copy->filename = (const char *)cursor; - memcpy(cursor, report->filename, filenameSize); - } - JS_ASSERT(cursor + filenameSize == (uint8 *)copy + mallocSize); - - /* Copy non-pointer members. */ - copy->lineno = report->lineno; - copy->errorNumber = report->errorNumber; - - /* Note that this is before it gets flagged with JSREPORT_EXCEPTION */ - copy->flags = report->flags; - -#undef JS_CHARS_SIZE - return copy; -} - -static jsval * -GetStackTraceValueBuffer(JSExnPrivate *priv) -{ - /* - * We use extra memory after JSExnPrivateInfo.stackElems to store jsvals - * that helps to produce more informative stack traces. The following - * assert allows us to assume that no gap after stackElems is necessary to - * align the buffer properly. - */ - JS_STATIC_ASSERT(sizeof(JSStackTraceElem) % sizeof(jsval) == 0); - - return (jsval *)(priv->stackElems + priv->stackDepth); -} - -static JSBool -InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message, - JSString *filename, uintN lineno, JSErrorReport *report) -{ - JSCheckAccessOp checkAccess; - JSErrorReporter older; - JSExceptionState *state; - jsval callerid, v; - JSStackFrame *fp, *fpstop; - size_t stackDepth, valueCount, size; - JSBool overflow; - JSExnPrivate *priv; - JSStackTraceElem *elem; - jsval *values; - - JS_ASSERT(OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass); - - /* - * Prepare stack trace data. - * - * Set aside any error reporter for cx and save its exception state - * so we can suppress any checkAccess failures. Such failures should stop - * the backtrace procedure, not result in a failure of this constructor. - */ - checkAccess = cx->runtime->checkObjectAccess; - older = JS_SetErrorReporter(cx, NULL); - state = JS_SaveExceptionState(cx); - - callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); - stackDepth = 0; - valueCount = 0; - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->fun && fp->argv) { - if (checkAccess) { - v = fp->argv[-2]; - if (!JSVAL_IS_PRIMITIVE(v) && - !checkAccess(cx, JSVAL_TO_OBJECT(v), callerid, - JSACC_READ, &v /* ignored */)) { - break; - } - } - valueCount += fp->argc; - } - ++stackDepth; - } - JS_RestoreExceptionState(cx, state); - JS_SetErrorReporter(cx, older); - fpstop = fp; - - size = offsetof(JSExnPrivate, stackElems); - overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem)); - size += stackDepth * sizeof(JSStackTraceElem); - overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval)); - size += valueCount * sizeof(jsval); - if (overflow) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - priv = (JSExnPrivate *)JS_malloc(cx, size); - if (!priv) - return JS_FALSE; - - /* - * We initialize errorReport with a copy of report after setting the - * private slot, to prevent GC accessing a junk value we clear the field - * here. - */ - priv->errorReport = NULL; - priv->message = message; - priv->filename = filename; - priv->lineno = lineno; - priv->stackDepth = stackDepth; - - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (fp = cx->fp; fp != fpstop; fp = fp->down) { - if (!fp->fun) { - elem->funName = NULL; - elem->argc = 0; - } else { - elem->funName = fp->fun->atom - ? ATOM_TO_STRING(fp->fun->atom) - : cx->runtime->emptyString; - elem->argc = fp->argc; - memcpy(values, fp->argv, fp->argc * sizeof(jsval)); - values += fp->argc; - } - elem->ulineno = 0; - elem->filename = NULL; - if (fp->script) { - elem->filename = fp->script->filename; - if (fp->pc) - elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->pc); - } - ++elem; - } - JS_ASSERT(priv->stackElems + stackDepth == elem); - JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values); - - OBJ_SET_SLOT(cx, exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv)); - - if (report) { - /* - * Construct a new copy of the error report struct. We can't use the - * error report struct that was passed in, because it's allocated on - * the stack, and also because it may point to transient data in the - * JSTokenStream. - */ - priv->errorReport = CopyErrorReport(cx, report); - if (!priv->errorReport) { - /* The finalizer realeases priv since it is in the private slot. */ - return JS_FALSE; - } - } - - return JS_TRUE; -} - -static JSExnPrivate * -GetExnPrivate(JSContext *cx, JSObject *obj) -{ - jsval privateValue; - JSExnPrivate *priv; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ErrorClass); - privateValue = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (JSVAL_IS_VOID(privateValue)) - return NULL; - priv = (JSExnPrivate *)JSVAL_TO_PRIVATE(privateValue); - JS_ASSERT(priv); - return priv; -} - -static uint32 -exn_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSExnPrivate *priv; - JSStackTraceElem *elem; - size_t vcount, i; - jsval *vp, v; - - priv = GetExnPrivate(cx, obj); - if (priv) { - GC_MARK(cx, priv->message, "exception message"); - GC_MARK(cx, priv->filename, "exception filename"); - elem = priv->stackElems; - for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { - if (elem->funName) - GC_MARK(cx, elem->funName, "stack trace function name"); - if (elem->filename) - js_MarkScriptFilename(elem->filename); - vcount += elem->argc; - } - vp = GetStackTraceValueBuffer(priv); - for (i = 0; i != vcount; ++i, ++vp) { - v = *vp; - if (JSVAL_IS_GCTHING(v)) - GC_MARK(cx, JSVAL_TO_GCTHING(v), "stack trace argument"); - } - } - return 0; -} - -static void -exn_finalize(JSContext *cx, JSObject *obj) -{ - JSExnPrivate *priv; - - priv = GetExnPrivate(cx, obj); - if (priv) { - if (priv->errorReport) - JS_free(cx, priv->errorReport); - JS_free(cx, priv); - } -} - -static JSBool -exn_enumerate(JSContext *cx, JSObject *obj) -{ - JSAtomState *atomState; - uintN i; - JSAtom *atom; - JSObject *pobj; - JSProperty *prop; - - JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); - static const uint16 offsets[] = { - (uint16)offsetof(JSAtomState, messageAtom), - (uint16)offsetof(JSAtomState, fileNameAtom), - (uint16)offsetof(JSAtomState, lineNumberAtom), - (uint16)offsetof(JSAtomState, stackAtom), - }; - - atomState = &cx->runtime->atomState; - for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { - atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -static JSBool -exn_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSExnPrivate *priv; - JSString *str; - JSAtom *atom; - JSString *stack; - const char *prop; - jsval v; - - *objp = NULL; - priv = GetExnPrivate(cx, obj); - if (priv && JSVAL_IS_STRING(id)) { - str = JSVAL_TO_STRING(id); - - atom = cx->runtime->atomState.messageAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_message_str; - v = STRING_TO_JSVAL(priv->message); - goto define; - } - - atom = cx->runtime->atomState.fileNameAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_fileName_str; - v = STRING_TO_JSVAL(priv->filename); - goto define; - } - - atom = cx->runtime->atomState.lineNumberAtom; - if (str == ATOM_TO_STRING(atom)) { - prop = js_lineNumber_str; - v = INT_TO_JSVAL(priv->lineno); - goto define; - } - - atom = cx->runtime->atomState.stackAtom; - if (str == ATOM_TO_STRING(atom)) { - stack = StackTraceToString(cx, priv); - if (!stack) - return JS_FALSE; - - /* Allow to GC all things that were used to build stack trace. */ - priv->stackDepth = 0; - prop = js_stack_str; - v = STRING_TO_JSVAL(stack); - goto define; - } - } - return JS_TRUE; - - define: - if (!JS_DefineProperty(cx, obj, prop, v, NULL, NULL, JSPROP_ENUMERATE)) - return JS_FALSE; - *objp = obj; - return JS_TRUE; -} - -JSErrorReport * -js_ErrorFromException(JSContext *cx, jsval exn) -{ - JSObject *obj; - JSExnPrivate *priv; - - if (JSVAL_IS_PRIMITIVE(exn)) - return NULL; - obj = JSVAL_TO_OBJECT(exn); - if (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) - return NULL; - priv = GetExnPrivate(cx, obj); - if (!priv) - return NULL; - return priv->errorReport; -} - -struct JSExnSpec { - int protoIndex; - const char *name; - JSProtoKey key; - JSNative native; -}; - -/* - * All *Error constructors share the same JSClass, js_ErrorClass. But each - * constructor function for an *Error class must have a distinct native 'call' - * function pointer, in order for instanceof to work properly across multiple - * standard class sets. See jsfun.c:fun_hasInstance. - */ -#define MAKE_EXCEPTION_CTOR(name) \ -static JSBool \ -name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) \ -{ \ - return Exception(cx, obj, argc, argv, rval); \ -} - -MAKE_EXCEPTION_CTOR(Error) -MAKE_EXCEPTION_CTOR(InternalError) -MAKE_EXCEPTION_CTOR(EvalError) -MAKE_EXCEPTION_CTOR(RangeError) -MAKE_EXCEPTION_CTOR(ReferenceError) -MAKE_EXCEPTION_CTOR(SyntaxError) -MAKE_EXCEPTION_CTOR(TypeError) -MAKE_EXCEPTION_CTOR(URIError) - -#undef MAKE_EXCEPTION_CTOR - -static struct JSExnSpec exceptions[] = { - {JSEXN_NONE, js_Error_str, JSProto_Error, Error}, - {JSEXN_ERR, js_InternalError_str, JSProto_InternalError, InternalError}, - {JSEXN_ERR, js_EvalError_str, JSProto_EvalError, EvalError}, - {JSEXN_ERR, js_RangeError_str, JSProto_RangeError, RangeError}, - {JSEXN_ERR, js_ReferenceError_str, JSProto_ReferenceError, ReferenceError}, - {JSEXN_ERR, js_SyntaxError_str, JSProto_SyntaxError, SyntaxError}, - {JSEXN_ERR, js_TypeError_str, JSProto_TypeError, TypeError}, - {JSEXN_ERR, js_URIError_str, JSProto_URIError, URIError}, - {0, NULL, JSProto_Null, NULL} -}; - -static JSString * -ValueToShortSource(JSContext *cx, jsval v) -{ - JSString *str; - - /* Avoid toSource bloat and fallibility for object types. */ - if (JSVAL_IS_PRIMITIVE(v)) { - str = js_ValueToSource(cx, v); - } else if (VALUE_IS_FUNCTION(cx, v)) { - /* - * XXX Avoid function decompilation bloat for now. - */ - str = JS_GetFunctionId(JS_ValueToFunction(cx, v)); - if (!str && !(str = js_ValueToSource(cx, v))) { - /* - * Continue to soldier on if the function couldn't be - * converted into a string. - */ - JS_ClearPendingException(cx); - str = JS_NewStringCopyZ(cx, "[unknown function]"); - } - } else { - /* - * XXX Avoid toString on objects, it takes too long and uses too much - * memory, for too many classes (see Mozilla bug 166743). - */ - char buf[100]; - JS_snprintf(buf, sizeof buf, "[object %s]", - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); - str = JS_NewStringCopyZ(cx, buf); - } - return str; -} - -static JSString * -StackTraceToString(JSContext *cx, JSExnPrivate *priv) -{ - jschar *stackbuf; - size_t stacklen, stackmax; - JSStackTraceElem *elem, *endElem; - jsval *values; - size_t i; - JSString *str; - const char *cp; - char ulnbuf[11]; - - /* After this point, failing control flow must goto bad. */ - stackbuf = NULL; - stacklen = stackmax = 0; - -/* Limit the stackbuf length to a reasonable value to avoid overflow checks. */ -#define STACK_LENGTH_LIMIT JS_BIT(20) - -#define APPEND_CHAR_TO_STACK(c) \ - JS_BEGIN_MACRO \ - if (stacklen == stackmax) { \ - void *ptr_; \ - if (stackmax >= STACK_LENGTH_LIMIT) \ - goto done; \ - stackmax = stackmax ? 2 * stackmax : 64; \ - ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ - if (!ptr_) \ - goto bad; \ - stackbuf = ptr_; \ - } \ - stackbuf[stacklen++] = (c); \ - JS_END_MACRO - -#define APPEND_STRING_TO_STACK(str) \ - JS_BEGIN_MACRO \ - JSString *str_ = str; \ - size_t length_ = JSSTRING_LENGTH(str_); \ - if (length_ > stackmax - stacklen) { \ - void *ptr_; \ - if (stackmax >= STACK_LENGTH_LIMIT || \ - length_ >= STACK_LENGTH_LIMIT - stacklen) { \ - goto done; \ - } \ - stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ - ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ - if (!ptr_) \ - goto bad; \ - stackbuf = ptr_; \ - } \ - js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ - stacklen += length_; \ - JS_END_MACRO - - values = GetStackTraceValueBuffer(priv); - elem = priv->stackElems; - for (endElem = elem + priv->stackDepth; elem != endElem; elem++) { - if (elem->funName) { - APPEND_STRING_TO_STACK(elem->funName); - APPEND_CHAR_TO_STACK('('); - for (i = 0; i != elem->argc; i++, values++) { - if (i > 0) - APPEND_CHAR_TO_STACK(','); - str = ValueToShortSource(cx, *values); - if (!str) - goto bad; - APPEND_STRING_TO_STACK(str); - } - APPEND_CHAR_TO_STACK(')'); - } - APPEND_CHAR_TO_STACK('@'); - if (elem->filename) { - for (cp = elem->filename; *cp; cp++) - APPEND_CHAR_TO_STACK(*cp); - } - APPEND_CHAR_TO_STACK(':'); - JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", elem->ulineno); - for (cp = ulnbuf; *cp; cp++) - APPEND_CHAR_TO_STACK(*cp); - APPEND_CHAR_TO_STACK('\n'); - } -#undef APPEND_CHAR_TO_STACK -#undef APPEND_STRING_TO_STACK -#undef STACK_LENGTH_LIMIT - - done: - if (stacklen == 0) { - JS_ASSERT(!stackbuf); - return cx->runtime->emptyString; - } - if (stacklen < stackmax) { - /* - * Realloc can fail when shrinking on some FreeBSD versions, so - * don't use JS_realloc here; simply let the oversized allocation - * be owned by the string in that rare case. - */ - void *shrunk = JS_realloc(cx, stackbuf, (stacklen+1) * sizeof(jschar)); - if (shrunk) - stackbuf = shrunk; - } - - stackbuf[stacklen] = 0; - str = js_NewString(cx, stackbuf, stacklen, 0); - if (str) - return str; - - bad: - if (stackbuf) - JS_free(cx, stackbuf); - return NULL; -} - -/* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8 - with these two functions. */ -static JSString * -FilenameToString(JSContext *cx, const char *filename) -{ - return JS_NewStringCopyZ(cx, filename); -} - -static const char * -StringToFilename(JSContext *cx, JSString *str) -{ - return JS_GetStringBytes(str); -} - -static JSBool -Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool ok; - uint32 lineno; - JSString *message, *filename; - JSStackFrame *fp; - - if (cx->creatingException) - return JS_FALSE; - cx->creatingException = JS_TRUE; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* - * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when - * called as functions, without operator new. But as we do not give - * each constructor a distinct JSClass, whose .name member is used by - * js_NewObject to find the class prototype, we must get the class - * prototype ourselves. - */ - ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - rval); - if (!ok) - goto out; - obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL); - if (!obj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(obj); - } - - /* - * If it's a new object of class Exception, then null out the private - * data so that the finalizer doesn't attempt to free it. - */ - if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass) - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); - - /* Set the 'message' property. */ - if (argc != 0) { - message = js_ValueToString(cx, argv[0]); - if (!message) { - ok = JS_FALSE; - goto out; - } - argv[0] = STRING_TO_JSVAL(message); - } else { - message = cx->runtime->emptyString; - } - - /* Set the 'fileName' property. */ - if (argc > 1) { - filename = js_ValueToString(cx, argv[1]); - if (!filename) { - ok = JS_FALSE; - goto out; - } - argv[1] = STRING_TO_JSVAL(filename); - fp = NULL; - } else { - fp = JS_GetScriptedCaller(cx, NULL); - if (fp) { - filename = FilenameToString(cx, fp->script->filename); - if (!filename) { - ok = JS_FALSE; - goto out; - } - } else { - filename = cx->runtime->emptyString; - } - } - - /* Set the 'lineNumber' property. */ - if (argc > 2) { - ok = js_ValueToECMAUint32(cx, argv[2], &lineno); - if (!ok) - goto out; - } else { - if (!fp) - fp = JS_GetScriptedCaller(cx, NULL); - lineno = (fp && fp->pc) ? js_PCToLineNumber(cx, fp->script, fp->pc) : 0; - } - - ok = (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || - InitExnPrivate(cx, obj, message, filename, lineno, NULL); - - out: - cx->creatingException = JS_FALSE; - return ok; -} - -/* - * Convert to string. - * - * This method only uses JavaScript-modifiable properties name, message. It - * is left to the host to check for private data and report filename and line - * number information along with this message. - */ -static JSBool -exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSString *name, *message, *result; - jschar *chars, *cp; - size_t name_length, message_length, length; - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.nameAtom), - &v)) { - return JS_FALSE; - } - name = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) : cx->runtime->emptyString; - *rval = STRING_TO_JSVAL(name); - - if (!JS_GetProperty(cx, obj, js_message_str, &v)) - return JS_FALSE; - message = JSVAL_IS_STRING(v) ? JSVAL_TO_STRING(v) - : cx->runtime->emptyString; - - if (JSSTRING_LENGTH(message) != 0) { - name_length = JSSTRING_LENGTH(name); - message_length = JSSTRING_LENGTH(message); - length = (name_length ? name_length + 2 : 0) + message_length; - cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - if (name_length) { - js_strncpy(cp, JSSTRING_CHARS(name), name_length); - cp += name_length; - *cp++ = ':'; *cp++ = ' '; - } - js_strncpy(cp, JSSTRING_CHARS(message), message_length); - cp += message_length; - *cp = 0; - - result = js_NewString(cx, chars, length, 0); - if (!result) { - JS_free(cx, chars); - return JS_FALSE; - } - } else { - result = name; - } - - *rval = STRING_TO_JSVAL(result); - return JS_TRUE; -} - -#if JS_HAS_TOSOURCE -/* - * Return a string that may eval to something similar to the original object. - */ -static JSBool -exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval *vp; - JSString *name, *message, *filename, *lineno_as_str, *result; - uint32 lineno; - size_t lineno_length, name_length, message_length, filename_length, length; - jschar *chars, *cp; - - vp = argv + argc; /* beginning of explicit local roots */ - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.nameAtom), - rval)) { - return JS_FALSE; - } - name = js_ValueToString(cx, *rval); - if (!name) - return JS_FALSE; - *rval = STRING_TO_JSVAL(name); - - if (!JS_GetProperty(cx, obj, js_message_str, &vp[0]) || - !(message = js_ValueToSource(cx, vp[0]))) { - return JS_FALSE; - } - vp[0] = STRING_TO_JSVAL(message); - - if (!JS_GetProperty(cx, obj, js_fileName_str, &vp[1]) || - !(filename = js_ValueToSource(cx, vp[1]))) { - return JS_FALSE; - } - vp[1] = STRING_TO_JSVAL(filename); - - if (!JS_GetProperty(cx, obj, js_lineNumber_str, &vp[2]) || - !js_ValueToECMAUint32 (cx, vp[2], &lineno)) { - return JS_FALSE; - } - - if (lineno != 0) { - lineno_as_str = js_ValueToString(cx, vp[2]); - if (!lineno_as_str) - return JS_FALSE; - lineno_length = JSSTRING_LENGTH(lineno_as_str); - } else { - lineno_as_str = NULL; - lineno_length = 0; - } - - /* Magic 8, for the characters in ``(new ())''. */ - name_length = JSSTRING_LENGTH(name); - message_length = JSSTRING_LENGTH(message); - length = 8 + name_length + message_length; - - filename_length = JSSTRING_LENGTH(filename); - if (filename_length != 0) { - /* append filename as ``, {filename}'' */ - length += 2 + filename_length; - if (lineno_as_str) { - /* append lineno as ``, {lineno_as_str}'' */ - length += 2 + lineno_length; - } - } else { - if (lineno_as_str) { - /* - * no filename, but have line number, - * need to append ``, "", {lineno_as_str}'' - */ - length += 6 + lineno_length; - } - } - - cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; - js_strncpy(cp, JSSTRING_CHARS(name), name_length); - cp += name_length; - *cp++ = '('; - if (message_length != 0) { - js_strncpy(cp, JSSTRING_CHARS(message), message_length); - cp += message_length; - } - - if (filename_length != 0) { - /* append filename as ``, {filename}'' */ - *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, JSSTRING_CHARS(filename), filename_length); - cp += filename_length; - } else { - if (lineno_as_str) { - /* - * no filename, but have line number, - * need to append ``, "", {lineno_as_str}'' - */ - *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; - } - } - if (lineno_as_str) { - /* append lineno as ``, {lineno_as_str}'' */ - *cp++ = ','; *cp++ = ' '; - js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length); - cp += lineno_length; - } - - *cp++ = ')'; *cp++ = ')'; *cp = 0; - - result = js_NewString(cx, chars, length, 0); - if (!result) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(result); - return JS_TRUE; -} -#endif - -static JSFunctionSpec exception_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, exn_toSource, 0,0,3}, -#endif - {js_toString_str, exn_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSObject * -js_InitExceptionClasses(JSContext *cx, JSObject *obj) -{ - JSObject *obj_proto, *protos[JSEXN_LIMIT]; - int i; - - /* - * If lazy class initialization occurs for any Error subclass, then all - * classes are initialized, starting with Error. To avoid reentry and - * redundant initialization, we must not pass a null proto parameter to - * js_NewObject below, when called for the Error superclass. We need to - * ensure that Object.prototype is the proto of Error.prototype. - * - * See the equivalent code to ensure that parent_proto is non-null when - * JS_InitClass calls js_NewObject, in jsapi.c. - */ - if (!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), - &obj_proto)) { - return NULL; - } - - if (!js_EnterLocalRootScope(cx)) - return NULL; - - /* Initialize the prototypes first. */ - for (i = 0; exceptions[i].name != 0; i++) { - JSAtom *atom; - JSFunction *fun; - JSObject *funobj; - JSString *nameString; - int protoIndex = exceptions[i].protoIndex; - - /* Make the prototype for the current constructor name. */ - protos[i] = js_NewObject(cx, &js_ErrorClass, - (protoIndex != JSEXN_NONE) - ? protos[protoIndex] - : obj_proto, - obj); - if (!protos[i]) - break; - - /* So exn_finalize knows whether to destroy private data. */ - OBJ_SET_SLOT(cx, protos[i], JSSLOT_PRIVATE, JSVAL_VOID); - - /* Make a constructor function for the current name. */ - atom = cx->runtime->atomState.classAtoms[exceptions[i].key]; - fun = js_DefineFunction(cx, obj, atom, exceptions[i].native, 3, 0); - if (!fun) - break; - - /* Make this constructor make objects of class Exception. */ - fun->clasp = &js_ErrorClass; - - /* Extract the constructor object. */ - funobj = fun->object; - - /* Make the prototype and constructor links. */ - if (!js_SetClassPrototype(cx, funobj, protos[i], - JSPROP_READONLY | JSPROP_PERMANENT)) { - break; - } - - /* proto bootstrap bit from JS_InitClass omitted. */ - nameString = JS_NewStringCopyZ(cx, exceptions[i].name); - if (!nameString) - break; - - /* Add the name property to the prototype. */ - if (!JS_DefineProperty(cx, protos[i], js_name_str, - STRING_TO_JSVAL(nameString), - NULL, NULL, - JSPROP_ENUMERATE)) { - break; - } - - /* Finally, stash the constructor for later uses. */ - if (!js_SetClassObject(cx, obj, exceptions[i].key, funobj)) - break; - } - - js_LeaveLocalRootScope(cx); - if (exceptions[i].name) - return NULL; - - /* - * Add an empty message property. (To Exception.prototype only, - * because this property will be the same for all the exception - * protos.) - */ - if (!JS_DefineProperty(cx, protos[0], js_message_str, - STRING_TO_JSVAL(cx->runtime->emptyString), - NULL, NULL, JSPROP_ENUMERATE)) { - return NULL; - } - if (!JS_DefineProperty(cx, protos[0], js_fileName_str, - STRING_TO_JSVAL(cx->runtime->emptyString), - NULL, NULL, JSPROP_ENUMERATE)) { - return NULL; - } - if (!JS_DefineProperty(cx, protos[0], js_lineNumber_str, - INT_TO_JSVAL(0), - NULL, NULL, JSPROP_ENUMERATE)) { - return NULL; - } - - /* - * Add methods only to Exception.prototype, because ostensibly all - * exception types delegate to that. - */ - if (!JS_DefineFunctions(cx, protos[0], exception_methods)) - return NULL; - - return protos[0]; -} - -const JSErrorFormatString* -js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, const uintN errorNumber) -{ - const JSErrorFormatString *errorString = NULL; - - if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) { - errorString = cx->localeCallbacks - ->localeGetErrorMessage(userRef, locale, errorNumber); - } - if (!errorString) - errorString = js_GetErrorMessage(userRef, locale, errorNumber); - return errorString; -} - -#if defined ( DEBUG_mccabe ) && defined ( PRINTNAMES ) -/* For use below... get character strings for error name and exception name */ -static struct exnname { char *name; char *exception; } errortoexnname[] = { -#define MSG_DEF(name, number, count, exception, format) \ - {#name, #exception}, -#include "js.msg" -#undef MSG_DEF -}; -#endif /* DEBUG */ - -JSBool -js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp) -{ - JSErrNum errorNumber; - const JSErrorFormatString *errorString; - JSExnType exn; - jsval tv[4]; - JSTempValueRooter tvr; - JSBool ok; - JSObject *errProto, *errObject; - JSString *messageStr, *filenameStr; - - /* - * Tell our caller to report immediately if cx has no active frames, or if - * this report is just a warning. - */ - JS_ASSERT(reportp); - if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags)) - return JS_FALSE; - - /* Find the exception index associated with this error. */ - errorNumber = (JSErrNum) reportp->errorNumber; - errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber); - exn = errorString ? errorString->exnType : JSEXN_NONE; - JS_ASSERT(exn < JSEXN_LIMIT); - -#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES ) - /* Print the error name and the associated exception name to stderr */ - fprintf(stderr, "%s\t%s\n", - errortoexnname[errorNumber].name, - errortoexnname[errorNumber].exception); -#endif - - /* - * Return false (no exception raised) if no exception is associated - * with the given error number. - */ - if (exn == JSEXN_NONE) - return JS_FALSE; - - /* - * Prevent runaway recursion, just as the Exception native constructor - * must do, via cx->creatingException. If an out-of-memory error occurs, - * no exception object will be created, but we don't assume that OOM is - * the only kind of error that subroutines of this function called below - * might raise. - */ - if (cx->creatingException) - return JS_FALSE; - - /* After this point the control must flow through the label out. */ - cx->creatingException = JS_TRUE; - - /* Protect the newly-created strings below from nesting GCs. */ - memset(tv, 0, sizeof tv); - JS_PUSH_TEMP_ROOT(cx, sizeof tv / sizeof tv[0], tv, &tvr); - - /* - * Try to get an appropriate prototype by looking up the corresponding - * exception constructor name in the scope chain of the current context's - * top stack frame, or in the global object if no frame is active. - */ - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key), - &errProto); - if (!ok) - goto out; - tv[0] = OBJECT_TO_JSVAL(errProto); - - errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL); - if (!errObject) { - ok = JS_FALSE; - goto out; - } - tv[1] = OBJECT_TO_JSVAL(errObject); - - messageStr = JS_NewStringCopyZ(cx, message); - if (!messageStr) { - ok = JS_FALSE; - goto out; - } - tv[2] = STRING_TO_JSVAL(messageStr); - - filenameStr = JS_NewStringCopyZ(cx, reportp->filename); - if (!filenameStr) { - ok = JS_FALSE; - goto out; - } - tv[3] = STRING_TO_JSVAL(filenameStr); - - ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, - reportp->lineno, reportp); - if (!ok) - goto out; - - JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); - - /* Flag the error report passed in to indicate an exception was raised. */ - reportp->flags |= JSREPORT_EXCEPTION; - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - cx->creatingException = JS_FALSE; - return ok; -} - -JSBool -js_ReportUncaughtException(JSContext *cx) -{ - jsval exn; - JSObject *exnObject; - jsval vp[5]; - JSTempValueRooter tvr; - JSErrorReport *reportp, report; - JSString *str; - const char *bytes; - JSBool ok; - - if (!JS_IsExceptionPending(cx)) - return JS_TRUE; - - if (!JS_GetPendingException(cx, &exn)) - return JS_FALSE; - - /* - * Because js_ValueToString below could error and an exception object - * could become unrooted, we must root exnObject. Later, if exnObject is - * non-null, we need to root other intermediates, so allocate an operand - * stack segment to protect all of these values. - */ - if (JSVAL_IS_PRIMITIVE(exn)) { - exnObject = NULL; - } else { - exnObject = JSVAL_TO_OBJECT(exn); - vp[0] = exn; - memset(vp + 1, 0, sizeof vp - sizeof vp[0]); - JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(vp), vp, &tvr); - } - - JS_ClearPendingException(cx); - reportp = js_ErrorFromException(cx, exn); - - /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ - str = js_ValueToString(cx, exn); - if (!str) { - bytes = "unknown (can't convert to string)"; - } else { - if (exnObject) - vp[1] = STRING_TO_JSVAL(str); - bytes = js_GetStringBytes(cx->runtime, str); - } - ok = JS_TRUE; - - if (!reportp && - exnObject && - OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { - const char *filename; - uint32 lineno; - - ok = JS_GetProperty(cx, exnObject, js_message_str, &vp[2]); - if (!ok) - goto out; - if (JSVAL_IS_STRING(vp[2])) - bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); - - ok = JS_GetProperty(cx, exnObject, js_fileName_str, &vp[3]); - if (!ok) - goto out; - str = js_ValueToString(cx, vp[3]); - if (!str) { - ok = JS_FALSE; - goto out; - } - filename = StringToFilename(cx, str); - - ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &vp[4]); - if (!ok) - goto out; - ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); - if (!ok) - goto out; - - reportp = &report; - memset(&report, 0, sizeof report); - report.filename = filename; - report.lineno = (uintN) lineno; - } - - if (!reportp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_UNCAUGHT_EXCEPTION, bytes); - } else { - /* Flag the error as an exception. */ - reportp->flags |= JSREPORT_EXCEPTION; - js_ReportErrorAgain(cx, bytes, reportp); - } - -out: - if (exnObject) - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} diff --git a/spidermonkey/libjs/jsexn.h b/spidermonkey/libjs/jsexn.h deleted file mode 100644 index 58cb984..0000000 --- a/spidermonkey/libjs/jsexn.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS runtime exception classes. - */ - -#ifndef jsexn_h___ -#define jsexn_h___ - -JS_BEGIN_EXTERN_C - -extern JSClass js_ErrorClass; - -/* - * Initialize the exception constructor/prototype hierarchy. - */ -extern JSObject * -js_InitExceptionClasses(JSContext *cx, JSObject *obj); - -/* - * Given a JSErrorReport, check to see if there is an exception associated with - * the error number. If there is, then create an appropriate exception object, - * set it as the pending exception, and set the JSREPORT_EXCEPTION flag on the - * error report. Exception-aware host error reporters should probably ignore - * error reports so flagged. Returns JS_TRUE if an associated exception is - * found and set, JS_FALSE otherwise.. - */ -extern JSBool -js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp); - -/* - * Called if a JS API call to js_Execute or js_InternalCall fails; calls the - * error reporter with the error report associated with any uncaught exception - * that has been raised. Returns true if there was an exception pending, and - * the error reporter was actually called. - * - * The JSErrorReport * that the error reporter is called with is currently - * associated with a JavaScript object, and is not guaranteed to persist after - * the object is collected. Any persistent uses of the JSErrorReport contents - * should make their own copy. - * - * The flags field of the JSErrorReport will have the JSREPORT_EXCEPTION flag - * set; embeddings that want to silently propagate JavaScript exceptions to - * other contexts may want to use an error reporter that ignores errors with - * this flag. - */ -extern JSBool -js_ReportUncaughtException(JSContext *cx); - -extern JSErrorReport * -js_ErrorFromException(JSContext *cx, jsval exn); - -extern const JSErrorFormatString * -js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale, - const uintN errorNumber); - -JS_END_EXTERN_C - -#endif /* jsexn_h___ */ diff --git a/spidermonkey/libjs/jsfile.c b/spidermonkey/libjs/jsfile.c deleted file mode 100644 index ed1c4e8..0000000 --- a/spidermonkey/libjs/jsfile.c +++ /dev/null @@ -1,2735 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS File object - */ -#if JS_HAS_FILE_OBJECT - -#include "jsstddef.h" -#include "jsfile.h" - -/* ----------------- Platform-specific includes and defines ----------------- */ -#if defined(XP_WIN) || defined(XP_OS2) -# include -# include -# include -# include -# define FILESEPARATOR '\\' -# define FILESEPARATOR2 '/' -# define CURRENT_DIR "c:\\" -# define POPEN _popen -# define PCLOSE _pclose -#elif defined(XP_UNIX) || defined(XP_BEOS) -# include -# include -# include -# include -# define FILESEPARATOR '/' -# define FILESEPARATOR2 '\0' -# define CURRENT_DIR "/" -# define POPEN popen -# define PCLOSE pclose -#endif - -/* --------------- Platform-independent includes and defines ---------------- */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsdate.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsutil.h" /* Added by JSIFY */ -#include - -/* NSPR dependencies */ -#include "prio.h" -#include "prerror.h" - -#define SPECIAL_FILE_STRING "Special File" -#define CURRENTDIR_PROPERTY "currentDir" -#define SEPARATOR_PROPERTY "separator" -#define FILE_CONSTRUCTOR "File" -#define PIPE_SYMBOL '|' - -#define ASCII 0 -#define UTF8 1 -#define UCS2 2 - -#define asciistring "text" -#define utfstring "binary" -#define unicodestring "unicode" - -#define MAX_PATH_LENGTH 1024 -#define MODE_SIZE 256 -#define NUMBER_SIZE 32 -#define MAX_LINE_LENGTH 256 -#define URL_PREFIX "file://" - -#define STDINPUT_NAME "Standard input stream" -#define STDOUTPUT_NAME "Standard output stream" -#define STDERROR_NAME "Standard error stream" - -#define RESOLVE_PATH js_canonicalPath /* js_absolutePath */ - -/* Error handling */ -typedef enum JSFileErrNum { -#define MSG_DEF(name, number, count, exception, format) \ - name = number, -#include "jsfile.msg" -#undef MSG_DEF - JSFileErr_Limit -#undef MSGDEF -} JSFileErrNum; - -#define JSFILE_HAS_DFLT_MSG_STRINGS 1 - -JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = { -#if JSFILE_HAS_DFLT_MSG_STRINGS -#define MSG_DEF(name, number, count, exception, format) \ - { format, count }, -#else -#define MSG_DEF(name, number, count, exception, format) \ - { NULL, count }, -#endif -#include "jsfile.msg" -#undef MSG_DEF -}; - -const JSErrorFormatString * -JSFile_GetErrorMessage(void *userRef, const char *locale, - const uintN errorNumber) -{ - if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit)) - return &JSFile_ErrorFormatString[errorNumber]; - else - return NULL; -} - -#define JSFILE_CHECK_NATIVE(op) \ - if (file->isNative) { \ - JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\ - op, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_WRITE \ - if (!file->isOpen) { \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for writing, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "write,append,create"); \ - } \ - if (!js_canWrite(cx, file)) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_WRITE, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_READ \ - if (!file->isOpen) { \ - JS_ReportWarning(cx, \ - "File %s is closed, will open it for reading, proceeding", \ - file->path); \ - js_FileOpen(cx, obj, file, "read"); \ - } \ - if (!js_canRead(cx, file)) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_CANNOT_READ, file->path); \ - goto out; \ - } - -#define JSFILE_CHECK_OPEN(op) \ - if (!file->isOpen) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_CLOSED, op); \ - goto out; \ - } - -#define JSFILE_CHECK_CLOSED(op) \ - if (file->isOpen) { \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_FILE_MUST_BE_OPEN, op); \ - goto out; \ - } - -#define JSFILE_CHECK_ONE_ARG(op) \ - if (argc != 1) { \ - char str[NUMBER_SIZE]; \ - sprintf(str, "%d", argc); \ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, \ - JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str); \ - goto out; \ - } - - -/* - Security mechanism, should define a callback for this. - The parameters are as follows: - SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file) - XXX Should this be a real function returning a JSBool result (and getting - some typesafety help from the compiler?). -*/ -#define SECURITY_CHECK(cx, ps, op, file) \ - /* Define a callback here... */ - - -/* Structure representing the file internally */ -typedef struct JSFile { - char *path; /* the path to the file. */ - JSBool isOpen; - int32 mode; /* mode used to open the file: read, write, append, create, etc.. */ - int32 type; /* Asciiz, utf, unicode */ - char byteBuffer[3]; /* bytes read in advance by js_FileRead ( UTF8 encoding ) */ - jsint nbBytesInBuf; /* number of bytes stored in the buffer above */ - jschar charBuffer; /* character read in advance by readln ( mac files only ) */ - JSBool charBufferUsed; /* flag indicating if the buffer above is being used */ - JSBool hasRandomAccess;/* can the file be randomly accessed? false for stdin, and - UTF-encoded files. */ - JSBool hasAutoflush; /* should we force a flush for each line break? */ - JSBool isNative; /* if the file is using OS-specific file FILE type */ - /* We can actually put the following two in a union since they should never be used at the same time */ - PRFileDesc *handle; /* the handle for the file, if open. */ - FILE *nativehandle; /* native handle, for stuff NSPR doesn't do. */ - JSBool isPipe; /* if the file is really an OS pipe */ -} JSFile; - -/* a few forward declarations... */ -JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename); -static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); -static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -/* New filename manipulation procesures */ -/* assumes we don't have leading/trailing spaces */ -static JSBool -js_filenameHasAPipe(const char *filename) -{ - if (!filename) - return JS_FALSE; - - return filename[0] == PIPE_SYMBOL || - filename[strlen(filename) - 1] == PIPE_SYMBOL; -} - -static JSBool -js_isAbsolute(const char *name) -{ -#if defined(XP_WIN) || defined(XP_OS2) - return *name && name[1] == ':'; -#else - return (name[0] -# if defined(XP_UNIX) || defined(XP_BEOS) - == -# else - != -# endif - FILESEPARATOR); -#endif -} - -/* - * Concatinates base and name to produce a valid filename. - * Returned string must be freed. -*/ -static char* -js_combinePath(JSContext *cx, const char *base, const char *name) -{ - int len = strlen(base); - char* result = JS_malloc(cx, len + strlen(name) + 2); - - if (!result) - return NULL; - - strcpy(result, base); - - if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) { - result[len] = FILESEPARATOR; - result[len + 1] = '\0'; - } - strcat(result, name); - return result; -} - -/* Extract the last component from a path name. Returned string must be freed */ -static char * -js_fileBaseName(JSContext *cx, const char *pathname) -{ - jsint index, aux; - char *result; - - index = strlen(pathname)-1; - - /* Chop off trailing seperators. */ - while (index > 0 && (pathname[index]==FILESEPARATOR || - pathname[index]==FILESEPARATOR2)) { - --index; - } - - aux = index; - - /* Now find the next separator. */ - while (index >= 0 && pathname[index] != FILESEPARATOR && - pathname[index] != FILESEPARATOR2) { - --index; - } - - /* Allocate and copy. */ - result = JS_malloc(cx, aux - index + 1); - if (!result) - return NULL; - strncpy(result, pathname + index + 1, aux - index); - result[aux - index] = '\0'; - return result; -} - -/* - * Returns everything but the last component from a path name. - * Returned string must be freed. - */ -static char * -js_fileDirectoryName(JSContext *cx, const char *pathname) -{ - char *result; - const char *cp, *end; - size_t pathsize; - - end = pathname + strlen(pathname); - cp = end - 1; - - /* If this is already a directory, chop off the trailing /s. */ - while (cp >= pathname) { - if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2) - break; - --cp; - } - - if (cp < pathname && end != pathname) { - /* There were just /s, return the root. */ - result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */ - result[0] = FILESEPARATOR; - result[1] = '\0'; - return result; - } - - /* Now chop off the last portion. */ - while (cp >= pathname) { - if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2) - break; - --cp; - } - - /* Check if this is a leaf. */ - if (cp < pathname) { - /* It is, return "pathname/". */ - if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) { - /* Already has its terminating /. */ - return JS_strdup(cx, pathname); - } - - pathsize = end - pathname + 1; - result = JS_malloc(cx, pathsize + 1); - if (!result) - return NULL; - - strcpy(result, pathname); - result[pathsize - 1] = FILESEPARATOR; - result[pathsize] = '\0'; - - return result; - } - - /* Return everything up to and including the seperator. */ - pathsize = cp - pathname + 1; - result = JS_malloc(cx, pathsize + 1); - if (!result) - return NULL; - - strncpy(result, pathname, pathsize); - result[pathsize] = '\0'; - - return result; -} - -static char * -js_absolutePath(JSContext *cx, const char * path) -{ - JSObject *obj; - JSString *str; - jsval prop; - - if (js_isAbsolute(path)) { - return JS_strdup(cx, path); - } else { - obj = JS_GetGlobalObject(cx); - if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR); - return JS_strdup(cx, path); - } - - obj = JSVAL_TO_OBJECT(prop); - if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR); - return JS_strdup(cx, path); - } - - str = JS_ValueToString(cx, prop); - if (!str) - return JS_strdup(cx, path); - - /* should we have an array of curr dirs indexed by drive for windows? */ - return js_combinePath(cx, JS_GetStringBytes(str), path); - } -} - -/* Side effect: will remove spaces in the beginning/end of the filename */ -static char * -js_canonicalPath(JSContext *cx, char *oldpath) -{ - char *tmp; - char *path = oldpath; - char *base, *dir, *current, *result; - jsint c; - jsint back = 0; - unsigned int i = 0, j = strlen(path)-1; - - /* This is probably optional */ - /* Remove possible spaces in the beginning and end */ - while (i < j && path[i] == ' ') - i++; - while (j >= 0 && path[j] == ' ') - j--; - - tmp = JS_malloc(cx, j-i+2); - if (!tmp) - return NULL; - - strncpy(tmp, path + i, j - i + 1); - tmp[j - i + 1] = '\0'; - - path = tmp; - - /* Pipe support. */ - if (js_filenameHasAPipe(path)) - return path; - - /* file:// support. */ - if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) { - tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX)); - JS_free(cx, path); - return tmp; - } - - if (!js_isAbsolute(path)) { - tmp = js_absolutePath(cx, path); - if (!tmp) - return NULL; - path = tmp; - } - - result = JS_strdup(cx, ""); - - current = path; - - base = js_fileBaseName(cx, current); - dir = js_fileDirectoryName(cx, current); - - while (strcmp(dir, current)) { - if (!strcmp(base, "..")) { - back++; - } else { - if (back > 0) { - back--; - } else { - tmp = result; - result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1); - if (!result) - goto out; - - strcpy(result, base); - c = strlen(result); - if (*tmp) { - result[c] = FILESEPARATOR; - result[c + 1] = '\0'; - strcat(result, tmp); - } - JS_free(cx, tmp); - } - } - JS_free(cx, current); - JS_free(cx, base); - current = dir; - base = js_fileBaseName(cx, current); - dir = js_fileDirectoryName(cx, current); - } - - tmp = result; - result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1); - if (!result) - goto out; - - strcpy(result, dir); - c = strlen(result); - if (tmp[0]!='\0') { - if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) { - result[c] = FILESEPARATOR; - result[c+1] = '\0'; - } - strcat(result, tmp); - } - -out: - if (tmp) - JS_free(cx, tmp); - if (dir) - JS_free(cx, dir); - if (base) - JS_free(cx, base); - if (current) - JS_free(cx, current); - - return result; -} - -/* -------------------------- Text conversion ------------------------------- */ -/* The following is ripped from libi18n/unicvt.c and include files.. */ - -/* - * UTF8 defines and macros - */ -#define ONE_OCTET_BASE 0x00 /* 0xxxxxxx */ -#define ONE_OCTET_MASK 0x7F /* x1111111 */ -#define CONTINUING_OCTET_BASE 0x80 /* 10xxxxxx */ -#define CONTINUING_OCTET_MASK 0x3F /* 00111111 */ -#define TWO_OCTET_BASE 0xC0 /* 110xxxxx */ -#define TWO_OCTET_MASK 0x1F /* 00011111 */ -#define THREE_OCTET_BASE 0xE0 /* 1110xxxx */ -#define THREE_OCTET_MASK 0x0F /* 00001111 */ -#define FOUR_OCTET_BASE 0xF0 /* 11110xxx */ -#define FOUR_OCTET_MASK 0x07 /* 00000111 */ -#define FIVE_OCTET_BASE 0xF8 /* 111110xx */ -#define FIVE_OCTET_MASK 0x03 /* 00000011 */ -#define SIX_OCTET_BASE 0xFC /* 1111110x */ -#define SIX_OCTET_MASK 0x01 /* 00000001 */ - -#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK ) == ONE_OCTET_BASE) -#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK ) == TWO_OCTET_BASE) -#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE) -#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE) -#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE) -#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK ) == SIX_OCTET_BASE) -#define IS_UTF8_2ND_THRU_6TH(x) \ - (( (x)&~CONTINUING_OCTET_MASK ) == CONTINUING_OCTET_BASE) -#define IS_UTF8_1ST_OF_UCS2(x) \ - IS_UTF8_1ST_OF_1(x) \ - || IS_UTF8_1ST_OF_2(x) \ - || IS_UTF8_1ST_OF_3(x) - - -#define MAX_UCS2 0xFFFF -#define DEFAULT_CHAR 0x003F /* Default char is "?" */ -#define BYTE_MASK 0xBF -#define BYTE_MARK 0x80 - - -/* Function: one_ucs2_to_utf8_char - * - * Function takes one UCS-2 char and writes it to a UTF-8 buffer. - * We need a UTF-8 buffer because we don't know before this - * function how many bytes of utf-8 data will be written. It also - * takes a pointer to the end of the UTF-8 buffer so that we don't - * overwrite data. This function returns the number of UTF-8 bytes - * of data written, or -1 if the buffer would have been overrun. - */ - -#define LINE_SEPARATOR 0x2028 -#define PARAGRAPH_SEPARATOR 0x2029 -static int16 one_ucs2_to_utf8_char(unsigned char *tobufp, - unsigned char *tobufendp, - uint16 onechar) -{ - int16 numUTF8bytes = 0; - - if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) { - strcpy((char*)tobufp, "\n"); - return strlen((char*)tobufp); - } - - if (onechar < 0x80) { - numUTF8bytes = 1; - } else if (onechar < 0x800) { - numUTF8bytes = 2; - } else { - /* 0x800 >= onechar <= MAX_UCS2 */ - numUTF8bytes = 3; - } - - tobufp += numUTF8bytes; - - /* return error if we don't have space for the whole character */ - if (tobufp > tobufendp) { - return(-1); - } - - switch(numUTF8bytes) { - case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | THREE_OCTET_BASE; - break; - - case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6; - *--tobufp = onechar | TWO_OCTET_BASE; - break; - - case 1: *--tobufp = (unsigned char)onechar; - break; - } - - return numUTF8bytes; -} - -/* - * utf8_to_ucs2_char - * - * Convert a utf8 multibyte character to ucs2 - * - * inputs: pointer to utf8 character(s) - * length of utf8 buffer ("read" length limit) - * pointer to return ucs2 character - * - * outputs: number of bytes in the utf8 character - * -1 if not a valid utf8 character sequence - * -2 if the buffer is too short - */ -static int16 -utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p) -{ - uint16 lead, cont1, cont2; - - /* - * Check for minimum buffer length - */ - if ((buflen < 1) || (utf8p == NULL)) { - return -2; - } - lead = (uint16) (*utf8p); - - /* - * Check for a one octet sequence - */ - if (IS_UTF8_1ST_OF_1(lead)) { - *ucs2p = lead & ONE_OCTET_MASK; - return 1; - } - - /* - * Check for a two octet sequence - */ - if (IS_UTF8_1ST_OF_2(*utf8p)) { - if (buflen < 2) - return -2; - cont1 = (uint16) *(utf8p+1); - if (!IS_UTF8_2ND_THRU_6TH(cont1)) - return -1; - *ucs2p = (lead & TWO_OCTET_MASK) << 6; - *ucs2p |= cont1 & CONTINUING_OCTET_MASK; - return 2; - } - - /* - * Check for a three octet sequence - */ - else if (IS_UTF8_1ST_OF_3(lead)) { - if (buflen < 3) - return -2; - cont1 = (uint16) *(utf8p+1); - cont2 = (uint16) *(utf8p+2); - if ( (!IS_UTF8_2ND_THRU_6TH(cont1)) - || (!IS_UTF8_2ND_THRU_6TH(cont2))) - return -1; - *ucs2p = (lead & THREE_OCTET_MASK) << 12; - *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6; - *ucs2p |= cont2 & CONTINUING_OCTET_MASK; - return 3; - } - else { /* not a valid utf8/ucs2 character */ - return -1; - } -} - -/* ----------------------------- Helper functions --------------------------- */ -/* Ripped off from lm_win.c .. */ -/* where is strcasecmp?.. for now, it's case sensitive.. - * - * strcasecmp is in strings.h, but on windows it's called _stricmp... - * will need to #ifdef this -*/ - -static int32 -js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name) -{ - char *comma, *equal, *current; - char *options = JS_strdup(cx, oldoptions); - int32 found = 0; - - current = options; - for (;;) { - comma = strchr(current, ','); - if (comma) *comma = '\0'; - equal = strchr(current, '='); - if (equal) *equal = '\0'; - if (strcmp(current, name) == 0) { - if (!equal || strcmp(equal + 1, "yes") == 0) - found = 1; - else - found = atoi(equal + 1); - } - if (equal) *equal = '='; - if (comma) *comma = ','; - if (found || !comma) - break; - current = comma + 1; - } - JS_free(cx, options); - return found; -} - -/* empty the buffer */ -static void -js_ResetBuffers(JSFile * file) -{ - file->charBufferUsed = JS_FALSE; - file->nbBytesInBuf = 0; -} - -/* Reset file attributes */ -static void -js_ResetAttributes(JSFile * file) -{ - file->mode = file->type = 0; - file->isOpen = JS_FALSE; - file->handle = NULL; - file->nativehandle = NULL; - file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */ - file->hasAutoflush = JS_FALSE; - file->isNative = JS_FALSE; - file->isPipe = JS_FALSE; - - js_ResetBuffers(file); -} - -static JSBool -js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){ - JSString *type, *mask; - jsval v[2]; - jsval rval; - - type = JS_InternString(cx, asciistring); - mask = JS_NewStringCopyZ(cx, mode); - v[0] = STRING_TO_JSVAL(mask); - v[1] = STRING_TO_JSVAL(type); - - if (!file_open(cx, obj, 2, v, &rval)) - return JS_FALSE; - return JS_TRUE; -} - -/* Buffered version of PR_Read. Used by js_FileRead */ -static int32 -js_BufferedRead(JSFile *f, unsigned char *buf, int32 len) -{ - int32 count = 0; - - while (f->nbBytesInBuf>0&&len>0) { - buf[0] = f->byteBuffer[0]; - f->byteBuffer[0] = f->byteBuffer[1]; - f->byteBuffer[1] = f->byteBuffer[2]; - f->nbBytesInBuf--; - len--; - buf+=1; - count++; - } - - if (len > 0) { - count += (!f->isNative) - ? PR_Read(f->handle, buf, len) - : fread(buf, 1, len, f->nativehandle); - } - return count; -} - -static int32 -js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) -{ - unsigned char *aux; - int32 count = 0, i; - jsint remainder; - unsigned char utfbuf[3]; - - if (file->charBufferUsed) { - buf[0] = file->charBuffer; - buf++; - len--; - file->charBufferUsed = JS_FALSE; - } - - switch (mode) { - case ASCII: - aux = (unsigned char*)JS_malloc(cx, len); - if (!aux) - return 0; - - count = js_BufferedRead(file, aux, len); - if (count == -1) { - JS_free(cx, aux); - return 0; - } - - for (i = 0; i < len; i++) - buf[i] = (jschar)aux[i]; - - JS_free(cx, aux); - break; - - case UTF8: - remainder = 0; - for (count = 0;count0) { - file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; - file->nbBytesInBuf++; - utfbuf[0] = utfbuf[1]; - utfbuf[1] = utfbuf[2]; - remainder--; - } - break; - - case UCS2: - count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1; - if (count == -1) - return 0; - - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "read", file->path); - } - - return count; -} - -static int32 -js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode) -{ - int32 count = 0, i; - jsint remainder; - unsigned char utfbuf[3]; - jschar tmp; - - switch (mode) { - case ASCII: - count = PR_Seek(file->handle, len, PR_SEEK_CUR); - break; - - case UTF8: - remainder = 0; - for (count = 0;count0) { - file->byteBuffer[file->nbBytesInBuf] = utfbuf[0]; - file->nbBytesInBuf++; - utfbuf[0] = utfbuf[1]; - utfbuf[1] = utfbuf[2]; - remainder--; - } - break; - - case UCS2: - count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2; - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "seek", file->path); - } - - return count; -} - -static int32 -js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode) -{ - unsigned char *aux; - int32 count = 0, i, j; - unsigned char *utfbuf; - - switch (mode) { - case ASCII: - aux = (unsigned char*)JS_malloc(cx, len); - if (!aux) - return 0; - - for (i = 0; iisNative) - ? PR_Write(file->handle, aux, len) - : fwrite(aux, 1, len, file->nativehandle); - - if (count==-1) { - JS_free(cx, aux); - return 0; - } - - JS_free(cx, aux); - break; - - case UTF8: - utfbuf = (unsigned char*)JS_malloc(cx, len*3); - if (!utfbuf) return 0; - i = 0; - for (count = 0;countisNative) - ? PR_Write(file->handle, utfbuf, i) - : fwrite(utfbuf, 1, i, file->nativehandle); - - if (jisNative) - ? PR_Write(file->handle, buf, len*2) >> 1 - : fwrite(buf, 1, len*2, file->nativehandle) >> 1; - - if (count == -1) - return 0; - break; - - default: - /* Not reached. */ - JS_ASSERT(0); - } - - if(count == -1) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "write", file->path); - } - - return count; -} - -/* ----------------------------- Property checkers -------------------------- */ -static JSBool -js_exists(JSContext *cx, JSFile *file) -{ - if (file->isNative) { - /* It doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; - } - - return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS; -} - -static JSBool -js_canRead(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - if (file->isOpen && !(file->mode & PR_RDONLY)) - return JS_FALSE; - return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS; - } - - if (file->isPipe) { - /* Is this pipe open for reading? */ - return file->path[0] == PIPE_SYMBOL; - } - - return !strcmp(file->path, STDINPUT_NAME); -} - -static JSBool -js_canWrite(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - if (file->isOpen && !(file->mode & PR_WRONLY)) - return JS_FALSE; - return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS; - } - - if(file->isPipe) { - /* Is this pipe open for writing? */ - return file->path[strlen(file->path)-1] == PIPE_SYMBOL; - } - - return !strcmp(file->path, STDOUTPUT_NAME) || - !strcmp(file->path, STDERROR_NAME); -} - -static JSBool -js_isFile(JSContext *cx, JSFile *file) -{ - if (!file->isNative) { - PRFileInfo info; - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JS_FALSE; - } - - return info.type == PR_FILE_FILE; - } - - /* This doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; -} - -static JSBool -js_isDirectory(JSContext *cx, JSFile *file) -{ - if(!file->isNative){ - PRFileInfo info; - - /* Hack needed to get get_property to work. */ - if (!js_exists(cx, file)) - return JS_FALSE; - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JS_FALSE; - } - - return info.type == PR_FILE_DIRECTORY; - } - - /* This doesn't make sense for a pipe of stdstream. */ - return JS_FALSE; -} - -static jsval -js_size(JSContext *cx, JSFile *file) -{ - PRFileInfo info; - - JSFILE_CHECK_NATIVE("size"); - - if (file->isOpen - ? PR_GetOpenFileInfo(file->handle, &info) - : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - return JSVAL_VOID; - } - - return INT_TO_JSVAL(info.size); - -out: - return JSVAL_VOID; -} - -/* - * Return the parent object - */ -static JSBool -js_parent(JSContext *cx, JSFile *file, jsval *resultp) -{ - char *str; - - /* Since we only care about pipes and native files, return NULL. */ - if (file->isNative) { - *resultp = JSVAL_VOID; - return JS_TRUE; - } - - str = js_fileDirectoryName(cx, file->path); - if (!str) - return JS_FALSE; - - /* If the directory is equal to the original path, we're at the root. */ - if (!strcmp(file->path, str)) { - *resultp = JSVAL_NULL; - } else { - JSObject *obj = js_NewFileObject(cx, str); - if (!obj) { - JS_free(cx, str); - return JS_FALSE; - } - *resultp = OBJECT_TO_JSVAL(obj); - } - - JS_free(cx, str); - return JS_TRUE; -} - -static JSBool -js_name(JSContext *cx, JSFile *file, jsval *vp) -{ - char *name; - JSString *str; - - if (file->isPipe) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - name = js_fileBaseName(cx, file->path); - if (!name) - return JS_FALSE; - - str = JS_NewString(cx, name, strlen(name)); - if (!str) { - JS_free(cx, name); - return JS_FALSE; - } - - *vp = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* ------------------------------ File object methods ---------------------------- */ -static JSBool -file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *strmode, *strtype; - char *ctype, *mode; - int32 mask, type; - int len; - - mode = NULL; - - SECURITY_CHECK(cx, NULL, "open", file); - - /* A native file that is already open */ - if(file->isOpen && file->isNative) { - JS_ReportWarning(cx, "Native file %s is already open, proceeding", - file->path); - goto good; - } - - /* Close before proceeding */ - if (file->isOpen) { - JS_ReportWarning(cx, "File %s is already open, we will close it and " - "reopen, proceeding", file->path); - if(!file_close(cx, obj, 0, NULL, rval)) - goto out; - } - - if (js_isDirectory(cx, file)) { - JS_ReportWarning(cx, "%s seems to be a directory, there is no point in " - "trying to open it, proceeding", file->path); - goto good; - } - - /* Path must be defined at this point */ - len = strlen(file->path); - - /* Mode */ - if (argc >= 1) { - strmode = JS_ValueToString(cx, argv[0]); - if (!strmode) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, - argv[0]); - goto out; - } - mode = JS_strdup(cx, JS_GetStringBytes(strmode)); - } else { - if(file->path[0]==PIPE_SYMBOL) { - /* pipe default mode */ - mode = JS_strdup(cx, "read"); - } else if(file->path[len-1]==PIPE_SYMBOL) { - /* pipe default mode */ - mode = JS_strdup(cx, "write"); - } else { - /* non-destructive, permissive defaults. */ - mode = JS_strdup(cx, "readWrite,append,create"); - } - } - - /* Process the mode */ - mask = 0; - /* TODO: this is pretty ugly, we walk thru the string too many times */ - mask |= js_FileHasOption(cx, mode, "read") ? PR_RDONLY : 0; - mask |= js_FileHasOption(cx, mode, "write") ? PR_WRONLY : 0; - mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR : 0; - mask |= js_FileHasOption(cx, mode, "append") ? PR_APPEND : 0; - mask |= js_FileHasOption(cx, mode, "create") ? PR_CREATE_FILE : 0; - mask |= js_FileHasOption(cx, mode, "replace") ? PR_TRUNCATE : 0; - - if (mask & PR_RDWR) - mask |= (PR_RDONLY | PR_WRONLY); - if ((mask & PR_RDONLY) && (mask & PR_WRONLY)) - mask |= PR_RDWR; - - file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush"); - - /* Type */ - if (argc > 1) { - strtype = JS_ValueToString(cx, argv[1]); - if (!strtype) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, - argv[1]); - goto out; - } - ctype = JS_GetStringBytes(strtype); - - if(!strcmp(ctype, utfstring)) { - type = UTF8; - } else if (!strcmp(ctype, unicodestring)) { - type = UCS2; - } else { - if (strcmp(ctype, asciistring)) { - JS_ReportWarning(cx, "File type %s is not supported, using " - "'text' instead, proceeding", ctype); - } - type = ASCII; - } - } else { - type = ASCII; - } - - /* Save the relevant fields */ - file->type = type; - file->mode = mask; - file->nativehandle = NULL; - file->hasRandomAccess = (type != UTF8); - - /* - * Deal with pipes here. We can't use NSPR for pipes, so we have to use - * POPEN. - */ - if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) { - if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED); - goto out; - } else { - int i = 0; - char pipemode[3]; - SECURITY_CHECK(cx, NULL, "pipe_open", file); - - if(file->path[0] == PIPE_SYMBOL){ - if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, - mode, file->path); - goto out; - } - /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */ - pipemode[i++] = 'r'; -#ifndef XP_UNIX - pipemode[i++] = file->type==UTF8 ? 'b' : 't'; -#endif - pipemode[i++] = '\0'; - file->nativehandle = POPEN(&file->path[1], pipemode); - } else if(file->path[len-1] == PIPE_SYMBOL) { - char *command = JS_malloc(cx, len); - - strncpy(command, file->path, len-1); - command[len-1] = '\0'; - /* open(STATUS, "netstat -an 2>&1 |") */ - pipemode[i++] = 'w'; -#ifndef XP_UNIX - pipemode[i++] = file->type==UTF8 ? 'b' : 't'; -#endif - pipemode[i++] = '\0'; - file->nativehandle = POPEN(command, pipemode); - JS_free(cx, command); - } - /* set the flags */ - file->isNative = JS_TRUE; - file->isPipe = JS_TRUE; - file->hasRandomAccess = JS_FALSE; - } - } else { - /* TODO: what about the permissions?? Java ignores the problem... */ - file->handle = PR_Open(file->path, mask, 0644); - } - - js_ResetBuffers(file); - JS_free(cx, mode); - mode = NULL; - - /* Set the open flag and return result */ - if (file->handle == NULL && file->nativehandle == NULL) { - file->isOpen = JS_FALSE; - - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - -good: - file->isOpen = JS_TRUE; - *rval = JSVAL_TRUE; - return JS_TRUE; - -out: - if(mode) - JS_free(cx, mode); - return JS_FALSE; -} - -static JSBool -file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "close", file); - - if(!file->isOpen){ - JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding", - file->path); - goto out; - } - - if(!file->isPipe){ - if(file->isNative){ - JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path); - goto out; - }else{ - if(file->handle && PR_Close(file->handle)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - - goto out; - } - } - }else{ - if(PCLOSE(file->nativehandle)==-1){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "pclose", file->path); - goto out; - } - } - - js_ResetAttributes(file); - *rval = JSVAL_TRUE; - return JS_TRUE; - -out: - return JS_FALSE; -} - - -static JSBool -file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "remove", file); - JSFILE_CHECK_NATIVE("remove"); - JSFILE_CHECK_CLOSED("remove"); - - if ((js_isDirectory(cx, file) ? - PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) { - js_ResetAttributes(file); - *rval = JSVAL_TRUE; - return JS_TRUE; - } else { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "remove", file->path); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -/* Raw PR-based function. No text processing. Just raw data copying. */ -static JSBool -file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *dest = NULL; - PRFileDesc *handle = NULL; - char *buffer; - jsval count, size; - JSBool fileInitiallyOpen=JS_FALSE; - - SECURITY_CHECK(cx, NULL, "copyTo", file); /* may need a second argument!*/ - JSFILE_CHECK_ONE_ARG("copyTo"); - JSFILE_CHECK_NATIVE("copyTo"); - /* remeber the state */ - fileInitiallyOpen = file->isOpen; - JSFILE_CHECK_READ; - - dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - - /* make sure we are not reading a file open for writing */ - if (file->isOpen && !js_canRead(cx, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path); - goto out; - } - - if (file->handle==NULL){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - - handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644); - - if(!handle){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", dest); - goto out; - } - - if ((size=js_size(cx, file))==JSVAL_VOID) { - goto out; - } - - buffer = JS_malloc(cx, size); - - count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size)); - - /* reading panic */ - if (count!=size) { - JS_free(cx, buffer); - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_COPY_READ_ERROR, file->path); - goto out; - } - - count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size))); - - /* writing panic */ - if (count!=size) { - JS_free(cx, buffer); - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_COPY_WRITE_ERROR, file->path); - goto out; - } - - JS_free(cx, buffer); - - if(!fileInitiallyOpen){ - if(!file_close(cx, obj, 0, NULL, rval)) goto out; - } - - if(PR_Close(handle)!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", dest); - goto out; - } - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - if(file->isOpen && !fileInitiallyOpen){ - if(PR_Close(file->handle)!=PR_SUCCESS){ - JS_ReportWarning(cx, "Can't close %s, proceeding", file->path); - } - } - - if(handle && PR_Close(handle)!=PR_SUCCESS){ - JS_ReportWarning(cx, "Can't close %s, proceeding", dest); - } - - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *dest; - - SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/ - JSFILE_CHECK_ONE_ARG("renameTo"); - JSFILE_CHECK_NATIVE("renameTo"); - JSFILE_CHECK_CLOSED("renameTo"); - - dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0]))); - - if (PR_Rename(file->path, dest)==PR_SUCCESS){ - /* copy the new filename */ - JS_free(cx, file->path); - file->path = dest; - *rval = JSVAL_TRUE; - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_RENAME_FAILED, file->path, dest); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "flush", file); - JSFILE_CHECK_NATIVE("flush"); - JSFILE_CHECK_OPEN("flush"); - - if (PR_Sync(file->handle)==PR_SUCCESS){ - *rval = JSVAL_TRUE; - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "flush", file->path); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - int32 count; - uintN i; - - SECURITY_CHECK(cx, NULL, "write", file); - JSFILE_CHECK_WRITE; - - for (i = 0; itype); - if (count==-1){ - *rval = JSVAL_FALSE; - return JS_FALSE; - } - } - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - - SECURITY_CHECK(cx, NULL, "writeln", file); - JSFILE_CHECK_WRITE; - - /* don't report an error here */ - if(!file_write(cx, obj, argc, argv, rval)) return JS_FALSE; - /* don't do security here -- we passed the check in file_write */ - str = JS_NewStringCopyZ(cx, "\n"); - - if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str), - file->type)==-1){ - *rval = JSVAL_FALSE; - return JS_FALSE; - } - - /* eol causes flush if hasAutoflush is turned on */ - if (file->hasAutoflush) - file_flush(cx, obj, 0, NULL, rval); - - *rval = JSVAL_TRUE; - return JS_TRUE; -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - jsuint i; - jsuint limit; - JSObject *array; - JSObject *elem; - jsval elemval; - - SECURITY_CHECK(cx, NULL, "writeAll", file); - JSFILE_CHECK_ONE_ARG("writeAll"); - JSFILE_CHECK_WRITE; - - if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR); - goto out; - } - - array = JSVAL_TO_OBJECT(argv[0]); - - JS_GetArrayLength(cx, array, &limit); - - for (i = 0; i262144)?262144:want; * arbitrary size limitation */ - - buf = JS_malloc(cx, want*sizeof buf[0]); - if (!buf) goto out; - - count = js_FileRead(cx, file, buf, want, file->type); - if (count>0) { - str = JS_NewUCStringCopyN(cx, buf, count); - *rval = STRING_TO_JSVAL(str); - JS_free(cx, buf); - return JS_TRUE; - } else { - JS_free(cx, buf); - goto out; - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - jschar *buf = NULL, *tmp; - int32 offset, read; - intN room; - jschar data, data2; - - SECURITY_CHECK(cx, NULL, "readln", file); - JSFILE_CHECK_READ; - - buf = JS_malloc(cx, MAX_LINE_LENGTH * sizeof data); - if (!buf) - return JS_FALSE; - - room = MAX_LINE_LENGTH - 1; - offset = 0; - - for (;;) { - read = js_FileRead(cx, file, &data, 1, file->type); - if (read < 0) - goto out; - if (read == 0) - goto eof; - - switch (data) { - case '\r': - read = js_FileRead(cx, file, &data2, 1, file->type); - if (read < 0) - goto out; - - if (read == 1 && data2 != '\n') { - /* We read one char too far. Buffer it. */ - file->charBuffer = data2; - file->charBufferUsed = JS_TRUE; - } - - /* Fall through. */ - case '\n': - goto done; - - default: - if (--room < 0) { - tmp = JS_realloc(cx, buf, - (offset + MAX_LINE_LENGTH) * sizeof data); - if (!tmp) - goto out; - - room = MAX_LINE_LENGTH - 1; - buf = tmp; - } - - buf[offset++] = data; - break; - } - } - -eof: - if (offset == 0) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - -done: - buf[offset] = 0; - tmp = JS_realloc(cx, buf, (offset + 1) * sizeof data); - if (!tmp) - goto out; - - str = JS_NewUCString(cx, tmp, offset); - if (!str) - goto out; - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - -out: - if (buf) - JS_free(cx, buf); - - return JS_FALSE; -} - -static JSBool -file_readAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSObject *array; - jsint len; - jsval line; - JSBool lineok = JS_FALSE; - - SECURITY_CHECK(cx, NULL, "readAll", file); - JSFILE_CHECK_READ; - - array = JS_NewArrayObject(cx, 0, NULL); - if (!array) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(array); - - len = 0; - - lineok = file_readln(cx, obj, 0, NULL, &line); - while (lineok && !JSVAL_IS_NULL(line)) { - JS_SetElement(cx, array, len++, &line); - lineok = file_readln(cx, obj, 0, NULL, &line); - } - -out: - return lineok; -} - -static JSBool -file_seek(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - int32 toskip; - int32 pos; - - SECURITY_CHECK(cx, NULL, "seek", file); - JSFILE_CHECK_ONE_ARG("seek"); - JSFILE_CHECK_NATIVE("seek"); - JSFILE_CHECK_READ; - - if (!JS_ValueToInt32(cx, argv[0], &toskip)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "seek", argv[0]); - goto out; - } - - if(!file->hasRandomAccess){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_NO_RANDOM_ACCESS, file->path); - goto out; - } - - if(js_isDirectory(cx, file)){ - JS_ReportWarning(cx,"Seek on directories is not supported, proceeding"); - goto out; - } - - pos = js_FileSeek(cx, file, toskip, file->type); - - if (pos!=-1) { - *rval = INT_TO_JSVAL(pos); - return JS_TRUE; - } -out: - *rval = JSVAL_VOID; - return JS_FALSE; -} - -static JSBool -file_list(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - PRDir *dir; - PRDirEntry *entry; - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSObject *array; - JSObject *eachFile; - jsint len; - jsval v; - JSRegExp *re = NULL; - JSFunction *func = NULL; - JSString *str; - jsval args[1]; - char *filePath; - - SECURITY_CHECK(cx, NULL, "list", file); - JSFILE_CHECK_NATIVE("list"); - - if (argc==1) { - if (JSVAL_IS_REGEXP(cx, argv[0])) { - re = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - }else - if (VALUE_IS_FUNCTION(cx, argv[0])) { - func = JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, argv[0]); - goto out; - } - } - - if (!js_isDirectory(cx, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, file->path); - goto out; - } - - dir = PR_OpenDir(file->path); - if(!dir){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "open", file->path); - goto out; - } - - /* create JSArray here... */ - array = JS_NewArrayObject(cx, 0, NULL); - len = 0; - - while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))!=NULL) { - /* first, check if we have a regexp */ - if (re!=NULL) { - size_t index = 0; - - str = JS_NewStringCopyZ(cx, entry->name); - if(!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &v)){ - /* don't report anything here */ - goto out; - } - /* not matched! */ - if (JSVAL_IS_NULL(v)) { - continue; - } - }else - if (func!=NULL) { - str = JS_NewStringCopyZ(cx, entry->name); - args[0] = STRING_TO_JSVAL(str); - if(!JS_CallFunction(cx, obj, func, 1, args, &v)){ - goto out; - } - - if (v==JSVAL_FALSE) { - continue; - } - } - - filePath = js_combinePath(cx, file->path, (char*)entry->name); - - eachFile = js_NewFileObject(cx, filePath); - JS_free(cx, filePath); - if (!eachFile){ - JS_ReportWarning(cx, "File %s cannot be retrieved", filePath); - continue; - } - v = OBJECT_TO_JSVAL(eachFile); - JS_SetElement(cx, array, len, &v); - JS_SetProperty(cx, array, entry->name, &v); - len++; - } - - if(PR_CloseDir(dir)!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - goto out; - } - *rval = OBJECT_TO_JSVAL(array); - return JS_TRUE; -out: - *rval = JSVAL_NULL; - return JS_FALSE; -} - -static JSBool -file_mkdir(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - SECURITY_CHECK(cx, NULL, "mkdir", file); - JSFILE_CHECK_ONE_ARG("mkdir"); - JSFILE_CHECK_NATIVE("mkdir"); - - /* if the current file is not a directory, find out the directory name */ - if (!js_isDirectory(cx, file)) { - char *dir = js_fileDirectoryName(cx, file->path); - JSObject *dirObj = js_NewFileObject(cx, dir); - - JS_free(cx, dir); - - /* call file_mkdir with the right set of parameters if needed */ - if (file_mkdir(cx, dirObj, argc, argv, rval)) - return JS_TRUE; - else - goto out; - }else{ - char *dirName = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); - char *fullName; - - fullName = js_combinePath(cx, file->path, dirName); - if (PR_MkDir(fullName, 0755)==PR_SUCCESS){ - *rval = JSVAL_TRUE; - JS_free(cx, fullName); - return JS_TRUE; - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "mkdir", fullName); - JS_free(cx, fullName); - goto out; - } - } -out: - *rval = JSVAL_FALSE; - return JS_FALSE; -} - -static JSBool -file_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval*rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - JSString *str; - - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -file_toURL(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char url[MAX_PATH_LENGTH]; - jschar *urlChars; - size_t len; - JSString *str; - - JSFILE_CHECK_NATIVE("toURL"); - - sprintf(url, "file://%s", file->path); - - len = strlen(url); - urlChars = js_InflateString(cx, url, &len); - if (!urlChars) - return JS_FALSE; - str = js_NewString(cx, urlChars, len, 0); - if (!str) { - JS_free(cx, urlChars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - - /* TODO: js_escape in jsstr.h may go away at some point */ - return js_str_escape(cx, obj, 0, rval, rval); - -out: - *rval = JSVAL_VOID; - return JS_FALSE; -} - - -static void -file_finalize(JSContext *cx, JSObject *obj) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - if(file) { - /* Close the file before exiting. */ - if(file->isOpen && !file->isNative) { - jsval vp; - file_close(cx, obj, 0, NULL, &vp); - } - - if (file->path) - JS_free(cx, file->path); - - JS_free(cx, file); - } -} - -/* - Allocates memory for the file object, sets fields to defaults. -*/ -static JSFile* -file_init(JSContext *cx, JSObject *obj, char *bytes) -{ - JSFile *file; - - file = JS_malloc(cx, sizeof *file); - if (!file) - return NULL; - memset(file, 0 , sizeof *file); - - js_ResetAttributes(file); - - file->path = RESOLVE_PATH(cx, bytes); - - if (!JS_SetPrivate(cx, obj, file)) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_PRIVATE_FILE, file->path); - JS_free(cx, file); - return NULL; - } - - return file; -} - -/* Returns a JSObject. This function is globally visible */ -JS_PUBLIC_API(JSObject*) -js_NewFileObject(JSContext *cx, char *filename) -{ - JSObject *obj; - JSFile *file; - - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObject"); - return NULL; - } - file = file_init(cx, obj, filename); - if(!file) return NULL; - return obj; -} - -/* Internal function, used for cases which NSPR file support doesn't cover */ -JSObject* -js_NewFileObjectFromFILE(JSContext *cx, FILE *nativehandle, char *filename, - int32 mode, JSBool open, JSBool randomAccess) -{ - JSObject *obj; - JSFile *file; - - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OBJECT_CREATION_FAILED, "js_NewFileObjectFromFILE"); - return NULL; - } - file = file_init(cx, obj, filename); - if(!file) return NULL; - - file->nativehandle = nativehandle; - - /* free result of RESOLVE_PATH from file_init. */ - JS_ASSERT(file->path != NULL); - JS_free(cx, file->path); - - file->path = strdup(filename); - file->isOpen = open; - file->mode = mode; - file->hasRandomAccess = randomAccess; - file->isNative = JS_TRUE; - return obj; -} - -/* - Real file constructor that is called from JavaScript. - Basically, does error processing and calls file_init. -*/ -static JSBool -file_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSFile *file; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* Replace obj with a new File object. */ - obj = JS_NewObject(cx, &js_FileClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - - str = (argc == 0) - ? JS_InternString(cx, "") - : JS_ValueToString(cx, argv[0]); - - if (!str) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, - argv[0]); - return JS_FALSE; - } - - file = file_init(cx, obj, JS_GetStringBytes(str)); - if (!file) - return JS_FALSE; - - SECURITY_CHECK(cx, NULL, "constructor", file); - - return JS_TRUE; -} - -/* -------------------- File methods and properties ------------------------- */ -static JSFunctionSpec file_functions[] = { - { "open", file_open, 0}, - { "close", file_close, 0}, - { "remove", file_remove, 0}, - { "copyTo", file_copyTo, 0}, - { "renameTo", file_renameTo, 0}, - { "flush", file_flush, 0}, - { "seek", file_seek, 0}, - { "read", file_read, 0}, - { "readln", file_readln, 0}, - { "readAll", file_readAll, 0}, - { "write", file_write, 0}, - { "writeln", file_writeln, 0}, - { "writeAll", file_writeAll, 0}, - { "list", file_list, 0}, - { "mkdir", file_mkdir, 0}, - { "toString", file_toString, 0}, - { "toURL", file_toURL, 0}, - {0} -}; - -enum file_tinyid { - FILE_LENGTH = -2, - FILE_PARENT = -3, - FILE_PATH = -4, - FILE_NAME = -5, - FILE_ISDIR = -6, - FILE_ISFILE = -7, - FILE_EXISTS = -8, - FILE_CANREAD = -9, - FILE_CANWRITE = -10, - FILE_OPEN = -11, - FILE_TYPE = -12, - FILE_MODE = -13, - FILE_CREATED = -14, - FILE_MODIFIED = -15, - FILE_SIZE = -16, - FILE_RANDOMACCESS = -17, - FILE_POSITION = -18, - FILE_APPEND = -19, - FILE_REPLACE = -20, - FILE_AUTOFLUSH = -21, - FILE_ISNATIVE = -22, -}; - -static JSPropertySpec file_props[] = { - {"length", FILE_LENGTH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"parent", FILE_PARENT, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"path", FILE_PATH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"name", FILE_NAME, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isDirectory", FILE_ISDIR, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isFile", FILE_ISFILE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"exists", FILE_EXISTS, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canRead", FILE_CANREAD, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canWrite", FILE_CANWRITE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canAppend", FILE_APPEND, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"canReplace", FILE_REPLACE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"isOpen", FILE_OPEN, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"type", FILE_TYPE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"mode", FILE_MODE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"creationTime", FILE_CREATED, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"lastModified", FILE_MODIFIED, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"size", FILE_SIZE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"hasRandomAccess", FILE_RANDOMACCESS, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"hasAutoFlush", FILE_AUTOFLUSH, JSPROP_ENUMERATE | JSPROP_READONLY }, - {"position", FILE_POSITION, JSPROP_ENUMERATE }, - {"isNative", FILE_ISNATIVE, JSPROP_ENUMERATE | JSPROP_READONLY }, - {0} -}; - -/* ------------------------- Property getter/setter ------------------------- */ -static JSBool -file_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - char *bytes; - JSString *str; - jsint tiny; - PRFileInfo info; - JSBool flag; - PRExplodedTime expandedTime; - - tiny = JSVAL_TO_INT(id); - if (!file) - return JS_TRUE; - - switch (tiny) { - case FILE_PARENT: - SECURITY_CHECK(cx, NULL, "parent", file); - if (!js_parent(cx, file, vp)) - return JS_FALSE; - break; - case FILE_PATH: - str = JS_NewStringCopyZ(cx, file->path); - if (!str) - return JS_FALSE; - *vp = STRING_TO_JSVAL(str); - break; - case FILE_NAME: - if (!js_name(cx, file, vp)) - return JS_FALSE; - break; - case FILE_ISDIR: - SECURITY_CHECK(cx, NULL, "isDirectory", file); - *vp = BOOLEAN_TO_JSVAL(js_isDirectory(cx, file)); - break; - case FILE_ISFILE: - SECURITY_CHECK(cx, NULL, "isFile", file); - *vp = BOOLEAN_TO_JSVAL(js_isFile(cx, file)); - break; - case FILE_EXISTS: - SECURITY_CHECK(cx, NULL, "exists", file); - *vp = BOOLEAN_TO_JSVAL(js_exists(cx, file)); - break; - case FILE_ISNATIVE: - SECURITY_CHECK(cx, NULL, "isNative", file); - *vp = BOOLEAN_TO_JSVAL(file->isNative); - break; - case FILE_CANREAD: - SECURITY_CHECK(cx, NULL, "canRead", file); - *vp = BOOLEAN_TO_JSVAL(js_canRead(cx, file)); - break; - case FILE_CANWRITE: - SECURITY_CHECK(cx, NULL, "canWrite", file); - *vp = BOOLEAN_TO_JSVAL(js_canWrite(cx, file)); - break; - case FILE_OPEN: - SECURITY_CHECK(cx, NULL, "isOpen", file); - *vp = BOOLEAN_TO_JSVAL(file->isOpen); - break; - case FILE_APPEND : - SECURITY_CHECK(cx, NULL, "canAppend", file); - JSFILE_CHECK_OPEN("canAppend"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && - (file->mode&PR_APPEND)==PR_APPEND); - break; - case FILE_REPLACE : - SECURITY_CHECK(cx, NULL, "canReplace", file); - JSFILE_CHECK_OPEN("canReplace"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && - (file->mode&PR_TRUNCATE)==PR_TRUNCATE); - break; - case FILE_AUTOFLUSH : - SECURITY_CHECK(cx, NULL, "hasAutoFlush", file); - JSFILE_CHECK_OPEN("hasAutoFlush"); - *vp = BOOLEAN_TO_JSVAL(!file->isNative && file->hasAutoflush); - break; - case FILE_TYPE: - SECURITY_CHECK(cx, NULL, "type", file); - JSFILE_CHECK_OPEN("type"); - if(js_isDirectory(cx, file)){ - *vp = JSVAL_VOID; - break; - } - - switch (file->type) { - case ASCII: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, asciistring)); - break; - case UTF8: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, utfstring)); - break; - case UCS2: - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, unicodestring)); - break; - default: - JS_ReportWarning(cx, "Unsupported file type %d, proceeding", - file->type); - } - break; - case FILE_MODE: - SECURITY_CHECK(cx, NULL, "mode", file); - JSFILE_CHECK_OPEN("mode"); - bytes = JS_malloc(cx, MODE_SIZE); - bytes[0] = '\0'; - flag = JS_FALSE; - - if ((file->mode&PR_RDONLY)==PR_RDONLY) { - if (flag) strcat(bytes, ","); - strcat(bytes, "read"); - flag = JS_TRUE; - } - if ((file->mode&PR_WRONLY)==PR_WRONLY) { - if (flag) strcat(bytes, ","); - strcat(bytes, "write"); - flag = JS_TRUE; - } - if ((file->mode&PR_RDWR)==PR_RDWR) { - if (flag) strcat(bytes, ","); - strcat(bytes, "readWrite"); - flag = JS_TRUE; - } - if ((file->mode&PR_APPEND)==PR_APPEND) { - if (flag) strcat(bytes, ","); - strcat(bytes, "append"); - flag = JS_TRUE; - } - if ((file->mode&PR_CREATE_FILE)==PR_CREATE_FILE) { - if (flag) strcat(bytes, ","); - strcat(bytes, "create"); - flag = JS_TRUE; - } - if ((file->mode&PR_TRUNCATE)==PR_TRUNCATE) { - if (flag) strcat(bytes, ","); - strcat(bytes, "replace"); - flag = JS_TRUE; - } - if (file->hasAutoflush) { - if (flag) strcat(bytes, ","); - strcat(bytes, "hasAutoFlush"); - flag = JS_TRUE; - } - *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, bytes)); - JS_free(cx, bytes); - break; - case FILE_CREATED: - SECURITY_CHECK(cx, NULL, "creationTime", file); - JSFILE_CHECK_NATIVE("creationTime"); - if(((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - goto out; - } - - PR_ExplodeTime(info.creationTime, PR_LocalTimeParameters,&expandedTime); - *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, - expandedTime.tm_month, - expandedTime.tm_mday, - expandedTime.tm_hour, - expandedTime.tm_min, - expandedTime.tm_sec)); - break; - case FILE_MODIFIED: - SECURITY_CHECK(cx, NULL, "lastModified", file); - JSFILE_CHECK_NATIVE("lastModified"); - if(((file->isOpen)? - PR_GetOpenFileInfo(file->handle, &info): - PR_GetFileInfo(file->path, &info))!=PR_SUCCESS){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path); - goto out; - } - - PR_ExplodeTime(info.modifyTime, PR_LocalTimeParameters, &expandedTime); - *vp = OBJECT_TO_JSVAL(js_NewDateObject(cx, expandedTime.tm_year, - expandedTime.tm_month, - expandedTime.tm_mday, - expandedTime.tm_hour, - expandedTime.tm_min, - expandedTime.tm_sec)); - break; - case FILE_SIZE: - SECURITY_CHECK(cx, NULL, "size", file); - *vp = js_size(cx, file); - break; - case FILE_LENGTH: - SECURITY_CHECK(cx, NULL, "length", file); - JSFILE_CHECK_NATIVE("length"); - - if (js_isDirectory(cx, file)) { /* XXX debug me */ - PRDir *dir; - PRDirEntry *entry; - jsint count = 0; - - if(!(dir = PR_OpenDir(file->path))){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_OPEN_DIR, file->path); - goto out; - } - - while ((entry = PR_ReadDir(dir, PR_SKIP_BOTH))) { - count++; - } - - if(!PR_CloseDir(dir)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_OP_FAILED, "close", file->path); - - goto out; - } - - *vp = INT_TO_JSVAL(count); - break; - }else{ - /* return file size */ - *vp = js_size(cx, file); - } - break; - case FILE_RANDOMACCESS: - SECURITY_CHECK(cx, NULL, "hasRandomAccess", file); - JSFILE_CHECK_OPEN("hasRandomAccess"); - *vp = BOOLEAN_TO_JSVAL(file->hasRandomAccess); - break; - case FILE_POSITION: - SECURITY_CHECK(cx, NULL, "position", file); - JSFILE_CHECK_NATIVE("position"); - JSFILE_CHECK_OPEN("position"); - - if(!file->hasRandomAccess){ - JS_ReportWarning(cx, "File %s doesn't support random access, can't report the position, proceeding"); - *vp = JSVAL_VOID; - break; - } - - if (file->isOpen && js_isFile(cx, file)) { - int pos = PR_Seek(file->handle, 0, PR_SEEK_CUR); - if(pos!=-1){ - *vp = INT_TO_JSVAL(pos); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_REPORT_POSITION, file->path); - goto out; - } - }else { - JS_ReportWarning(cx, "File %s is closed or not a plain file," - " can't report position, proceeding"); - goto out; - } - break; - default: - SECURITY_CHECK(cx, NULL, "file_access", file); - - /* this is some other property -- try to use the dir["file"] syntax */ - if (js_isDirectory(cx, file)) { - PRDir *dir = NULL; - PRDirEntry *entry = NULL; - char *prop_name; - - str = JS_ValueToString(cx, id); - if (!str) - return JS_FALSE; - - prop_name = JS_GetStringBytes(str); - - /* no native files past this point */ - dir = PR_OpenDir(file->path); - if(!dir) { - /* This is probably not a directory */ - JS_ReportWarning(cx, "Can't open directory %s", file->path); - return JS_FALSE; - } - - while ((entry = PR_ReadDir(dir, PR_SKIP_NONE)) != NULL) { - if (!strcmp(entry->name, prop_name)){ - bytes = js_combinePath(cx, file->path, prop_name); - *vp = OBJECT_TO_JSVAL(js_NewFileObject(cx, bytes)); - PR_CloseDir(dir); - JS_free(cx, bytes); - return !JSVAL_IS_NULL(*vp); - } - } - PR_CloseDir(dir); - } - } - return JS_TRUE; - -out: - return JS_FALSE; -} - -static JSBool -file_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - jsint slot; - - if (JSVAL_IS_STRING(id)){ - return JS_TRUE; - } - - slot = JSVAL_TO_INT(id); - - switch (slot) { - /* File.position = 10 */ - case FILE_POSITION: - SECURITY_CHECK(cx, NULL, "set_position", file); - JSFILE_CHECK_NATIVE("set_position"); - - if(!file->hasRandomAccess){ - JS_ReportWarning(cx, "File %s doesn't support random access, can't " - "report the position, proceeding"); - goto out; - } - - if (file->isOpen && js_isFile(cx, file)) { - int32 pos; - int32 offset; - - if (!JS_ValueToInt32(cx, *vp, &offset)){ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, "position", *vp); - goto out; - } - - pos = PR_Seek(file->handle, offset, PR_SEEK_SET); - - if(pos!=-1){ - *vp = INT_TO_JSVAL(pos); - }else{ - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_CANNOT_SET_POSITION, file->path); - goto out; - } - } else { - JS_ReportWarning(cx, "File %s is closed or not a file, can't set " - "position, proceeding", file->path); - goto out; - } - } - - return JS_TRUE; -out: - return JS_FALSE; -} - -/* - File.currentDir = new File("D:\") or File.currentDir = "D:\" -*/ -static JSBool -file_currentDirSetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSFile *file; - - file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL); - - /* Look at the rhs and extract a file object from it */ - if (JSVAL_IS_OBJECT(*vp)) { - if (JS_InstanceOf(cx, obj, &js_FileClass, NULL)) { - /* Braindamaged rhs -- just return the old value */ - if (file && (!js_exists(cx, file) || !js_isDirectory(cx, file))) { - JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - return JS_FALSE; - } else { - chdir(file->path); - return JS_TRUE; - } - } else { - return JS_FALSE; - } - } else { - JSObject *rhsObject; - char *path; - - path = JS_GetStringBytes(JS_ValueToString(cx, *vp)); - rhsObject = js_NewFileObject(cx, path); - if (!rhsObject) - return JS_FALSE; - - if (!file || !js_exists(cx, file) || !js_isDirectory(cx, file)){ - JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, vp); - } else { - *vp = OBJECT_TO_JSVAL(rhsObject); - chdir(path); - } - } - - return JS_TRUE; -} - -/* Declare class */ -JSClass js_FileClass = { - "File", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_File), - JS_PropertyStub, JS_PropertyStub, file_getProperty, file_setProperty, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, file_finalize -}; - -/* -------------------- Functions exposed to the outside -------------------- */ -JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj) -{ - JSObject *file, *ctor, *afile; - jsval vp; - char *currentdir; - char separator[2]; - - file = JS_InitClass(cx, obj, NULL, &js_FileClass, file_constructor, 1, - file_props, file_functions, NULL, NULL); - if (!file) { - JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL, - JSFILEMSG_INIT_FAILED); - return NULL; - } - - ctor = JS_GetConstructor(cx, file); - if (!ctor) return NULL; - - /* Define CURRENTDIR property. We are doing this to get a - slash at the end of the current dir */ - afile = js_NewFileObject(cx, CURRENT_DIR); - currentdir = JS_malloc(cx, MAX_PATH_LENGTH); - currentdir = getcwd(currentdir, MAX_PATH_LENGTH); - afile = js_NewFileObject(cx, currentdir); - JS_free(cx, currentdir); - vp = OBJECT_TO_JSVAL(afile); - JS_DefinePropertyWithTinyId(cx, ctor, CURRENTDIR_PROPERTY, 0, vp, - JS_PropertyStub, file_currentDirSetter, - JSPROP_ENUMERATE | JSPROP_READONLY ); - - /* Define input */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdin, - STDINPUT_NAME, PR_RDONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "input", &vp); - - /* Define output */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stdout, - STDOUTPUT_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "output", &vp); - - /* Define error */ - vp = OBJECT_TO_JSVAL(js_NewFileObjectFromFILE(cx, stderr, - STDERROR_NAME, PR_WRONLY, JS_TRUE, JS_FALSE)); - JS_SetProperty(cx, ctor, "error", &vp); - - separator[0] = FILESEPARATOR; - separator[1] = '\0'; - vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, separator)); - JS_DefinePropertyWithTinyId(cx, ctor, SEPARATOR_PROPERTY, 0, vp, - JS_PropertyStub, JS_PropertyStub, - JSPROP_ENUMERATE | JSPROP_READONLY ); - return file; -} -#endif /* JS_HAS_FILE_OBJECT */ diff --git a/spidermonkey/libjs/jsfile.h b/spidermonkey/libjs/jsfile.h deleted file mode 100644 index 78707e8..0000000 --- a/spidermonkey/libjs/jsfile.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef _jsfile_h__ -#define _jsfile_h__ - -#if JS_HAS_FILE_OBJECT - -#include "jsobj.h" - -extern JS_PUBLIC_API(JSObject*) -js_InitFileClass(JSContext *cx, JSObject* obj); - -extern JS_PUBLIC_API(JSObject*) -js_NewFileObject(JSContext *cx, char *bytes); - -extern JSClass js_FileClass; - -#endif /* JS_HAS_FILE_OBJECT */ -#endif /* _jsfile_h__ */ diff --git a/spidermonkey/libjs/jsfile.msg b/spidermonkey/libjs/jsfile.msg deleted file mode 100644 index 137b35d..0000000 --- a/spidermonkey/libjs/jsfile.msg +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - Error messages for jsfile.c. See js.msg for format specification. -*/ - -MSG_DEF(JSFILEMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR, 1, 0, JSEXN_NONE, "File constructor is undefined") -MSG_DEF(JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR, 2, 0, JSEXN_NONE, "File.currentDir is undefined") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR, 3, 1, JSEXN_NONE, "The first argument {0} to file.open must be a string") -MSG_DEF(JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR, 4, 0, JSEXN_NONE, "The second argument to file.open must be a string") -MSG_DEF(JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, 5, 1, JSEXN_NONE, "Cannot copy file {0} open for writing") -MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_INFO_ERROR, 6, 1, JSEXN_NONE, "Cannot access file information for {0}") -MSG_DEF(JSFILEMSG_COPY_READ_ERROR, 7, 1, JSEXN_NONE, "An error occured while attempting to read a file {0} to copy") -MSG_DEF(JSFILEMSG_COPY_WRITE_ERROR, 8, 1, JSEXN_NONE, "An error occured while attempting to copy into file {0}") -MSG_DEF(JSFILEMSG_EXPECTS_ONE_ARG_ERROR, 9, 0, JSEXN_NONE, "Operation {0} expects one argument, not {1}") -MSG_DEF(JSFILEMSG_CANNOT_FLUSH_CLOSE_FILE_ERROR, 10, 1, JSEXN_NONE, "Cannot flush closed file {0}") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_WRITING_ERROR, 11, 1, JSEXN_NONE, "Cannot open file {0} for writing") -MSG_DEF(JSFILEMSG_WRITEALL_EXPECTS_ONE_ARG_ERROR, 12, 0, JSEXN_NONE, "writeAll expects one argument") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR, 13, 0, JSEXN_NONE, "writeAll expects an array as an argument") -MSG_DEF(JSFILEMSG_UNUSED0, 14, 0, JSEXN_NONE, "Unused error message slot") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_FILE_ERROR, 15, 1, JSEXN_NONE, "Cannot open file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_CONSTRUCTOR_NOT_STRING_ERROR, 16, 1, JSEXN_NONE, "The argument to the File constructor {0} must be a string") -MSG_DEF(JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED, 17, 0, JSEXN_NONE, "Bidirectional pipes are not supported") -MSG_DEF(JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES, 18, 2, JSEXN_NONE, "The opening mode you have chosen {0} is not supported by the pipe you are trying to open: {1}") -MSG_DEF(JSFILEMSG_OPEN_FAILED, 19, 1, JSEXN_NONE, "open on file {0} failed") -MSG_DEF(JSFILEMSG_CLOSE_FAILED, 20, 1, JSEXN_NONE, "close on file {0} failed") -MSG_DEF(JSFILEMSG_PCLOSE_FAILED, 21, 1, JSEXN_NONE, "pclose on file {0} failed") -MSG_DEF(JSFILEMSG_REMOVE_FAILED, 22, 1, JSEXN_NONE, "remove on file {0} failed") -MSG_DEF(JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, 23, 1, JSEXN_NONE, "Cannot access file status for {0}") -MSG_DEF(JSFILEMSG_RENAME_FAILED, 24, 2, JSEXN_NONE, "Cannot rename {0} to {1}") -MSG_DEF(JSFILEMSG_WRITE_FAILED, 25, 1, JSEXN_NONE, "Write failed on file {0}") -MSG_DEF(JSFILEMSG_READ_FAILED, 26, 1, JSEXN_NONE, "Read failed on file {0}") -MSG_DEF(JSFILEMSG_SKIP_FAILED, 27, 1, JSEXN_NONE, "Skip failed on file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_FUNCTION_OR_REGEX, 28, 1, JSEXN_NONE, "The first argument to file.list must be a function or a regex") -MSG_DEF(JSFILEMSG_CANNOT_DO_LIST_ON_A_FILE, 29, 1, JSEXN_NONE, "{0} must be a directory, cannot do list") -MSG_DEF(JSFILEMSG_NATIVE_OPERATION_IS_NOT_SUPPORTED, 30, 2, JSEXN_NONE, "Native operation {0} is not supported on {1}") -MSG_DEF(JSFILEMSG_CANNOT_SET_PRIVATE_FILE, 31, 1, JSEXN_NONE, "Cannot set private data for file {0}") -MSG_DEF(JSFILEMSG_FIRST_ARGUMENT_MUST_BE_A_NUMBER, 32, 2, JSEXN_NONE, "First argument to {0} must be a number, not {1}") -MSG_DEF(JSFILEMSG_CANNOT_WRITE, 33, 1, JSEXN_NONE, "Cannot write to {0}, file mode is different") -MSG_DEF(JSFILEMSG_CANNOT_READ, 34, 1, JSEXN_NONE, "Cannot read from {0}, file mode is different") -MSG_DEF(JSFILEMSG_CANNOT_FLUSH, 35, 1, JSEXN_NONE, "Flush failed on {0}") -MSG_DEF(JSFILEMSG_OP_FAILED, 36, 1, JSEXN_NONE, "File operation {0} failed") -MSG_DEF(JSFILEMSG_FILE_MUST_BE_OPEN, 37, 1, JSEXN_NONE, "File must be open for {0}") -MSG_DEF(JSFILEMSG_FILE_MUST_BE_CLOSED, 38, 1, JSEXN_NONE, "File must be closed for {0}") -MSG_DEF(JSFILEMSG_NO_RANDOM_ACCESS, 39, 1, JSEXN_NONE, "File {0} doesn't allow random access") -MSG_DEF(JSFILEMSG_OBJECT_CREATION_FAILED, 40, 1, JSEXN_NONE, "Couldn't create {0}") -MSG_DEF(JSFILEMSG_CANNOT_OPEN_DIR, 41, 1, JSEXN_NONE, "Couldn't open directory {0}") -MSG_DEF(JSFILEMSG_CANNOT_REPORT_POSITION, 42, 1, JSEXN_NONE, "Couldn't report position for {0}") -MSG_DEF(JSFILEMSG_CANNOT_SET_POSITION, 43, 1, JSEXN_NONE, "Couldn't set position for {0}") -MSG_DEF(JSFILEMSG_INIT_FAILED, 44, 0, JSEXN_NONE, "File class initialization failed") - - diff --git a/spidermonkey/libjs/jsfun.c b/spidermonkey/libjs/jsfun.c deleted file mode 100644 index 2a2df53..0000000 --- a/spidermonkey/libjs/jsfun.c +++ /dev/null @@ -1,2330 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS function support. - */ -#include "jsstddef.h" -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsexn.h" - -#if JS_HAS_GENERATORS -# include "jsiter.h" -#endif - -/* Generic function/call/arguments tinyids -- also reflected bit numbers. */ -enum { - CALL_ARGUMENTS = -1, /* predefined arguments local variable */ - CALL_CALLEE = -2, /* reference to active function's object */ - ARGS_LENGTH = -3, /* number of actual args, arity if inactive */ - ARGS_CALLEE = -4, /* reference from arguments to active funobj */ - FUN_ARITY = -5, /* number of formal parameters; desired argc */ - FUN_NAME = -6, /* function name, "" if anonymous */ - FUN_CALLER = -7 /* Function.prototype.caller, backward compat */ -}; - -#if JSFRAME_OVERRIDE_BITS < 8 -# error "not enough override bits in JSStackFrame.flags!" -#endif - -#define TEST_OVERRIDE_BIT(fp, tinyid) \ - ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) - -#define SET_OVERRIDE_BIT(fp, tinyid) \ - ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1))) - -JSBool -js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) -{ - JSObject *argsobj; - - if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { - JS_ASSERT(fp->callobj); - return OBJ_GET_PROPERTY(cx, fp->callobj, - ATOM_TO_JSID(cx->runtime->atomState - .argumentsAtom), - vp); - } - argsobj = js_GetArgsObject(cx, fp); - if (!argsobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(argsobj); - return JS_TRUE; -} - -static JSBool -MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) -{ - JSObject *argsobj; - jsval bmapval, bmapint; - size_t nbits, nbytes; - jsbitmap *bitmap; - - argsobj = fp->argsobj; - (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - nbits = fp->argc; - JS_ASSERT(slot < nbits); - if (JSVAL_IS_VOID(bmapval)) { - if (nbits <= JSVAL_INT_BITS) { - bmapint = 0; - bitmap = (jsbitmap *) &bmapint; - } else { - nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap); - bitmap = (jsbitmap *) JS_malloc(cx, nbytes); - if (!bitmap) - return JS_FALSE; - memset(bitmap, 0, nbytes); - bmapval = PRIVATE_TO_JSVAL(bitmap); - JS_SetReservedSlot(cx, argsobj, 0, bmapval); - } - } else { - if (nbits <= JSVAL_INT_BITS) { - bmapint = JSVAL_TO_INT(bmapval); - bitmap = (jsbitmap *) &bmapint; - } else { - bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); - } - } - JS_SET_BIT(bitmap, slot); - if (bitmap == (jsbitmap *) &bmapint) { - bmapval = INT_TO_JSVAL(bmapint); - JS_SetReservedSlot(cx, argsobj, 0, bmapval); - } - return JS_TRUE; -} - -/* NB: Infallible predicate, false does not mean error/exception. */ -static JSBool -ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot) -{ - JSObject *argsobj; - jsval bmapval, bmapint; - jsbitmap *bitmap; - - argsobj = fp->argsobj; - (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - if (JSVAL_IS_VOID(bmapval)) - return JS_FALSE; - if (fp->argc <= JSVAL_INT_BITS) { - bmapint = JSVAL_TO_INT(bmapval); - bitmap = (jsbitmap *) &bmapint; - } else { - bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); - } - return JS_TEST_BIT(bitmap, slot) != 0; -} - -JSBool -js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, - JSObject **objp, jsval *vp) -{ - jsval val; - JSObject *obj; - uintN slot; - - if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) { - JS_ASSERT(fp->callobj); - if (!OBJ_GET_PROPERTY(cx, fp->callobj, - ATOM_TO_JSID(cx->runtime->atomState - .argumentsAtom), - &val)) { - return JS_FALSE; - } - if (JSVAL_IS_PRIMITIVE(val)) { - obj = js_ValueToNonNullObject(cx, val); - if (!obj) - return JS_FALSE; - } else { - obj = JSVAL_TO_OBJECT(val); - } - *objp = obj; - return OBJ_GET_PROPERTY(cx, obj, id, vp); - } - - *objp = NULL; - *vp = JSVAL_VOID; - if (JSID_IS_INT(id)) { - slot = (uintN) JSID_TO_INT(id); - if (slot < fp->argc) { - if (fp->argsobj && ArgWasDeleted(cx, fp, slot)) - return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); - *vp = fp->argv[slot]; - } else { - /* - * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share - * storage between the formal parameter and arguments[k] for all - * k >= fp->argc && k < fp->fun->nargs. For example, in - * - * function f(x) { x = 42; return arguments[0]; } - * f(); - * - * the call to f should return undefined, not 42. If fp->argsobj - * is null at this point, as it would be in the example, return - * undefined in *vp. - */ - if (fp->argsobj) - return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); - } - } else { - if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) { - if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH)) - return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); - *vp = INT_TO_JSVAL((jsint) fp->argc); - } - } - return JS_TRUE; -} - -JSObject * -js_GetArgsObject(JSContext *cx, JSStackFrame *fp) -{ - JSObject *argsobj, *global, *parent; - - /* - * We must be in a function activation; the function must be lightweight - * or else fp must have a variable object. - */ - JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj)); - - /* Skip eval and debugger frames. */ - while (fp->flags & JSFRAME_SPECIAL) - fp = fp->down; - - /* Create an arguments object for fp only if it lacks one. */ - argsobj = fp->argsobj; - if (argsobj) - return argsobj; - - /* Link the new object to fp so it can get actual argument values. */ - argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); - if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - - /* - * Give arguments an intrinsic scope chain link to fp's global object. - * Since the arguments object lacks a prototype because js_ArgumentsClass - * is not initialized, js_NewObject won't assign a default parent to it. - * - * Therefore if arguments is used as the head of an eval scope chain (via - * a direct or indirect call to eval(program, arguments)), any reference - * to a standard class object in the program will fail to resolve due to - * js_GetClassPrototype not being able to find a global object containing - * the standard prototype by starting from arguments and following parent. - */ - global = fp->scopeChain; - while ((parent = OBJ_GET_PARENT(cx, global)) != NULL) - global = parent; - argsobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(global); - fp->argsobj = argsobj; - return argsobj; -} - -static JSBool -args_enumerate(JSContext *cx, JSObject *obj); - -JSBool -js_PutArgsObject(JSContext *cx, JSStackFrame *fp) -{ - JSObject *argsobj; - jsval bmapval, rval; - JSBool ok; - JSRuntime *rt; - - /* - * Reuse args_enumerate here to reflect fp's actual arguments as indexed - * elements of argsobj. Do this first, before clearing and freeing the - * deleted argument slot bitmap, because args_enumerate depends on that. - */ - argsobj = fp->argsobj; - ok = args_enumerate(cx, argsobj); - - /* - * Now clear the deleted argument number bitmap slot and free the bitmap, - * if one was actually created due to 'delete arguments[0]' or similar. - */ - (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval); - if (!JSVAL_IS_VOID(bmapval)) { - JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID); - if (fp->argc > JSVAL_INT_BITS) - JS_free(cx, JSVAL_TO_PRIVATE(bmapval)); - } - - /* - * Now get the prototype properties so we snapshot fp->fun and fp->argc - * before fp goes away. - */ - rt = cx->runtime; - ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), - &rval); - ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom), - &rval); - ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), - &rval); - ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom), - &rval); - - /* - * Clear the private pointer to fp, which is about to go away (js_Invoke). - * Do this last because the args_enumerate and js_GetProperty calls above - * need to follow the private slot to find fp. - */ - ok &= JS_SetPrivate(cx, argsobj, NULL); - fp->argsobj = NULL; - return ok; -} - -static JSBool -args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSStackFrame *fp; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case ARGS_CALLEE: - case ARGS_LENGTH: - SET_OVERRIDE_BIT(fp, slot); - break; - - default: - if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot)) - return JS_FALSE; - break; - } - return JS_TRUE; -} - -static JSBool -args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSStackFrame *fp; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case ARGS_CALLEE: - if (!TEST_OVERRIDE_BIT(fp, slot)) - *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); - break; - - case ARGS_LENGTH: - if (!TEST_OVERRIDE_BIT(fp, slot)) - *vp = INT_TO_JSVAL((jsint)fp->argc); - break; - - default: - if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) - *vp = fp->argv[slot]; - break; - } - return JS_TRUE; -} - -static JSBool -args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case ARGS_CALLEE: - case ARGS_LENGTH: - SET_OVERRIDE_BIT(fp, slot); - break; - - default: - if (FUN_INTERPRETED(fp->fun) && - (uintN)slot < fp->argc && - !ArgWasDeleted(cx, fp, slot)) { - fp->argv[slot] = *vp; - } - break; - } - return JS_TRUE; -} - -static JSBool -args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSStackFrame *fp; - uintN slot; - JSString *str; - JSAtom *atom; - intN tinyid; - jsval value; - - *objp = NULL; - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - if (JSVAL_IS_INT(id)) { - slot = JSVAL_TO_INT(id); - if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) { - /* XXX ECMA specs DontEnum, contrary to other array-like objects */ - if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id), - fp->argv[slot], - args_getProperty, args_setProperty, - JS_VERSION_IS_ECMA(cx) - ? 0 - : JSPROP_ENUMERATE, - NULL)) { - return JS_FALSE; - } - *objp = obj; - } - } else { - str = JSVAL_TO_STRING(id); - atom = cx->runtime->atomState.lengthAtom; - if (str == ATOM_TO_STRING(atom)) { - tinyid = ARGS_LENGTH; - value = INT_TO_JSVAL(fp->argc); - } else { - atom = cx->runtime->atomState.calleeAtom; - if (str == ATOM_TO_STRING(atom)) { - tinyid = ARGS_CALLEE; - value = fp->argv ? fp->argv[-2] - : OBJECT_TO_JSVAL(fp->fun->object); - } else { - atom = NULL; - - /* Quell GCC overwarnings. */ - tinyid = 0; - value = JSVAL_NULL; - } - } - - if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) { - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - args_getProperty, args_setProperty, 0, - SPROP_HAS_SHORTID, tinyid, NULL)) { - return JS_FALSE; - } - *objp = obj; - } - } - - return JS_TRUE; -} - -static JSBool -args_enumerate(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - JSObject *pobj; - JSProperty *prop; - uintN slot, argc; - - fp = (JSStackFrame *) - JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->argsobj); - - /* - * Trigger reflection with value snapshot in args_resolve using a series - * of js_LookupProperty calls. We handle length, callee, and the indexed - * argument properties. We know that args_resolve covers all these cases - * and creates direct properties of obj, but that it may fail to resolve - * length or callee if overridden. - */ - if (!js_LookupProperty(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), - &pobj, &prop)) { - return JS_FALSE; - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - - if (!js_LookupProperty(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.calleeAtom), - &pobj, &prop)) { - return JS_FALSE; - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - - argc = fp->argc; - for (slot = 0; slot < argc; slot++) { - if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -#if JS_HAS_GENERATORS -/* - * If a generator-iterator's arguments or call object escapes, it needs to - * mark its generator object. - */ -static uint32 -args_or_call_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSStackFrame *fp; - - fp = JS_GetPrivate(cx, obj); - if (fp && (fp->flags & JSFRAME_GENERATOR)) - GC_MARK(cx, FRAME_TO_GENERATOR(fp)->obj, "FRAME_TO_GENERATOR(fp)->obj"); - return 0; -} -#else -# define args_or_call_mark NULL -#endif - -/* - * The Arguments class is not initialized via JS_InitClass, and must not be, - * because its name is "Object". Per ECMA, that causes instances of it to - * delegate to the object named by Object.prototype. It also ensures that - * arguments.toString() returns "[object Object]". - * - * The JSClass functions below collaborate to lazily reflect and synchronize - * actual argument values, argument count, and callee function object stored - * in a JSStackFrame with their corresponding property values in the frame's - * arguments object. - */ -JSClass js_ArgumentsClass = { - js_Object_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - JS_PropertyStub, args_delProperty, - args_getProperty, args_setProperty, - args_enumerate, (JSResolveOp) args_resolve, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, NULL, - args_or_call_mark, NULL -}; - -JSObject * -js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent) -{ - JSObject *callobj, *funobj; - - /* Create a call object for fp only if it lacks one. */ - JS_ASSERT(fp->fun); - callobj = fp->callobj; - if (callobj) - return callobj; - JS_ASSERT(fp->fun); - - /* The default call parent is its function's parent (static link). */ - if (!parent) { - funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; - if (funobj) - parent = OBJ_GET_PARENT(cx, funobj); - } - - /* Create the call object and link it to its stack frame. */ - callobj = js_NewObject(cx, &js_CallClass, NULL, parent); - if (!callobj || !JS_SetPrivate(cx, callobj, fp)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - fp->callobj = callobj; - - /* Make callobj be the scope chain and the variables object. */ - JS_ASSERT(fp->scopeChain == parent); - fp->scopeChain = callobj; - fp->varobj = callobj; - return callobj; -} - -static JSBool -call_enumerate(JSContext *cx, JSObject *obj); - -JSBool -js_PutCallObject(JSContext *cx, JSStackFrame *fp) -{ - JSObject *callobj; - JSBool ok; - jsid argsid; - jsval aval; - - /* - * Reuse call_enumerate here to reflect all actual args and vars into the - * call object from fp. - */ - callobj = fp->callobj; - if (!callobj) - return JS_TRUE; - ok = call_enumerate(cx, callobj); - - /* - * Get the arguments object to snapshot fp's actual argument values. - */ - if (fp->argsobj) { - argsid = ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom); - ok &= js_GetProperty(cx, callobj, argsid, &aval); - ok &= js_SetProperty(cx, callobj, argsid, &aval); - ok &= js_PutArgsObject(cx, fp); - } - - /* - * Clear the private pointer to fp, which is about to go away (js_Invoke). - * Do this last because the call_enumerate and js_GetProperty calls above - * need to follow the private slot to find fp. - */ - ok &= JS_SetPrivate(cx, callobj, NULL); - fp->callobj = NULL; - return ok; -} - -static JSPropertySpec call_props[] = { - {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT,0,0}, - {"__callee__", CALL_CALLEE, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->fun); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case CALL_ARGUMENTS: - if (!TEST_OVERRIDE_BIT(fp, slot)) { - JSObject *argsobj = js_GetArgsObject(cx, fp); - if (!argsobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(argsobj); - } - break; - - case CALL_CALLEE: - if (!TEST_OVERRIDE_BIT(fp, slot)) - *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); - break; - - default: - if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) - *vp = fp->argv[slot]; - break; - } - return JS_TRUE; -} - -static JSBool -call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->fun); - - slot = JSVAL_TO_INT(id); - switch (slot) { - case CALL_ARGUMENTS: - case CALL_CALLEE: - SET_OVERRIDE_BIT(fp, slot); - break; - - default: - if ((uintN)slot < JS_MAX(fp->argc, fp->fun->nargs)) - fp->argv[slot] = *vp; - break; - } - return JS_TRUE; -} - -JSBool -js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - - JS_ASSERT(JSVAL_IS_INT(id)); - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (fp) { - /* XXX no jsint slot commoning here to avoid MSVC1.52 crashes */ - if ((uintN)JSVAL_TO_INT(id) < fp->nvars) - *vp = fp->vars[JSVAL_TO_INT(id)]; - } - return JS_TRUE; -} - -JSBool -js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - - JS_ASSERT(JSVAL_IS_INT(id)); - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (fp) { - /* XXX jsint slot is block-local here to avoid MSVC1.52 crashes */ - jsint slot = JSVAL_TO_INT(id); - if ((uintN)slot < fp->nvars) - fp->vars[slot] = *vp; - } - return JS_TRUE; -} - -static JSBool -call_enumerate(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - JSObject *funobj, *pobj; - JSScope *scope; - JSScopeProperty *sprop, *cprop; - JSPropertyOp getter; - jsval *vec; - JSAtom *atom; - JSProperty *prop; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - /* - * Do not enumerate a cloned function object at fp->argv[-2], it may have - * gained its own (mutable) scope (e.g., a brutally-shared XUL script sets - * the clone's prototype property). We must enumerate the function object - * that was decorated with parameter and local variable properties by the - * compiler when the compiler created fp->fun, namely fp->fun->object. - * - * Contrast with call_resolve, where we prefer fp->argv[-2], because we'll - * use js_LookupProperty to find any overridden properties in that object, - * if it was a mutated clone; and if not, we will search its prototype, - * fp->fun->object, to find compiler-created params and locals. - */ - funobj = fp->fun->object; - if (!funobj) - return JS_TRUE; - - /* - * Reflect actual args from fp->argv for formal parameters, and local vars - * and functions in fp->vars for declared variables and nested-at-top-level - * local functions. - */ - scope = OBJ_SCOPE(funobj); - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - getter = sprop->getter; - if (getter == js_GetArgument) - vec = fp->argv; - else if (getter == js_GetLocalVariable) - vec = fp->vars; - else - continue; - - /* Trigger reflection by looking up the unhidden atom for sprop->id. */ - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - atom = JSID_TO_ATOM(sprop->id); - JS_ASSERT(atom->flags & ATOM_HIDDEN); - atom = atom->entry.value; - - if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - - /* - * If we found the property in a different object, don't try sticking - * it into wrong slots vector. This can occur because we have a mutable - * __proto__ slot, and cloned function objects rely on their __proto__ - * to delegate to the object that contains the var and arg properties. - */ - if (!prop || pobj != obj) { - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - continue; - } - cprop = (JSScopeProperty *)prop; - LOCKED_OBJ_SET_SLOT(obj, cprop->slot, vec[(uint16) sprop->shortid]); - OBJ_DROP_PROPERTY(cx, obj, prop); - } - - return JS_TRUE; -} - -static JSBool -call_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSStackFrame *fp; - JSObject *funobj; - JSString *str; - JSAtom *atom; - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - JSPropertyOp getter, setter; - uintN attrs, slot, nslots, spflags; - jsval *vp, value; - intN shortid; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - JS_ASSERT(fp->fun); - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - - funobj = fp->argv ? JSVAL_TO_OBJECT(fp->argv[-2]) : fp->fun->object; - if (!funobj) - return JS_TRUE; - JS_ASSERT((JSFunction *) JS_GetPrivate(cx, funobj) == fp->fun); - - str = JSVAL_TO_STRING(id); - atom = js_AtomizeString(cx, str, 0); - if (!atom) - return JS_FALSE; - if (!js_LookupHiddenProperty(cx, funobj, ATOM_TO_JSID(atom), &obj2, &prop)) - return JS_FALSE; - - if (prop) { - if (!OBJ_IS_NATIVE(obj2)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; - } - - sprop = (JSScopeProperty *) prop; - getter = sprop->getter; - attrs = sprop->attrs & ~JSPROP_SHARED; - slot = (uintN) sprop->shortid; - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* Ensure we found an arg or var property for the same function. */ - if ((sprop->flags & SPROP_IS_HIDDEN) && - (obj2 == funobj || - (JSFunction *) JS_GetPrivate(cx, obj2) == fp->fun)) { - if (getter == js_GetArgument) { - vp = fp->argv; - nslots = JS_MAX(fp->argc, fp->fun->nargs); - getter = setter = NULL; - } else { - JS_ASSERT(getter == js_GetLocalVariable); - vp = fp->vars; - nslots = fp->nvars; - getter = js_GetCallVariable; - setter = js_SetCallVariable; - } - if (slot < nslots) { - value = vp[slot]; - spflags = SPROP_HAS_SHORTID; - shortid = (intN) slot; - } else { - value = JSVAL_VOID; - spflags = 0; - shortid = 0; - } - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value, - getter, setter, attrs, - spflags, shortid, NULL)) { - return JS_FALSE; - } - *objp = obj; - } - } - - return JS_TRUE; -} - -static JSBool -call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - JSStackFrame *fp; - - if (type == JSTYPE_FUNCTION) { - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (fp) { - JS_ASSERT(fp->fun); - *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); - } - } - return JS_TRUE; -} - -JSClass js_CallClass = { - js_Call_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Call), - JS_PropertyStub, JS_PropertyStub, - call_getProperty, call_setProperty, - call_enumerate, (JSResolveOp)call_resolve, - call_convert, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, NULL, - args_or_call_mark, NULL, -}; - -/* - * ECMA-262 specifies that length is a property of function object instances, - * but we can avoid that space cost by delegating to a prototype property that - * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes - * a fresh length value based on the arity of the individual function object's - * private data. - * - * The extensions below other than length, i.e., the ones not in ECMA-262, - * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility - * with ECMA we must allow a delegating object to override them. - */ -#define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED) - -static JSPropertySpec function_props[] = { - {js_arguments_str, CALL_ARGUMENTS, JSPROP_PERMANENT, 0,0}, - {js_arity_str, FUN_ARITY, JSPROP_PERMANENT, 0,0}, - {js_caller_str, FUN_CALLER, JSPROP_PERMANENT, 0,0}, - {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, 0,0}, - {js_name_str, FUN_NAME, JSPROP_PERMANENT, 0,0}, - {0,0,0,0,0} -}; - -static JSBool -fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSFunction *fun; - JSStackFrame *fp; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - slot = JSVAL_TO_INT(id); - - /* - * Loop because getter and setter can be delegated from another class, - * but loop only for ARGS_LENGTH because we must pretend that f.length - * is in each function instance f, per ECMA-262, instead of only in the - * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED - * to make it appear so). - * - * This code couples tightly to the attributes for the function_props[] - * initializers above, and to js_SetProperty and js_HasOwnPropertyHelper. - * - * It's important to allow delegating objects, even though they inherit - * this getter (fun_getProperty), to override arguments, arity, caller, - * and name. If we didn't return early for slot != ARGS_LENGTH, we would - * clobber *vp with the native property value, instead of letting script - * override that value in delegating objects. - * - * Note how that clobbering is what simulates JSPROP_READONLY for all of - * the non-standard properties when the directly addressed object (obj) - * is a function object (i.e., when this loop does not iterate). - */ - while (!(fun = (JSFunction *) - JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) { - if (slot != ARGS_LENGTH) - return JS_TRUE; - obj = OBJ_GET_PROTO(cx, obj); - if (!obj) - return JS_TRUE; - } - - /* Find fun's top-most activation record. */ - for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL)); - fp = fp->down) { - continue; - } - - switch (slot) { - case CALL_ARGUMENTS: - /* Warn if strict about f.arguments or equivalent unqualified uses. */ - if (!JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_DEPRECATED_USAGE, - js_arguments_str)) { - return JS_FALSE; - } - if (fp) { - if (!js_GetArgsValue(cx, fp, vp)) - return JS_FALSE; - } else { - *vp = JSVAL_NULL; - } - break; - - case ARGS_LENGTH: - case FUN_ARITY: - *vp = INT_TO_JSVAL((jsint)fun->nargs); - break; - - case FUN_NAME: - *vp = fun->atom - ? ATOM_KEY(fun->atom) - : STRING_TO_JSVAL(cx->runtime->emptyString); - break; - - case FUN_CALLER: - while (fp && (fp->flags & JSFRAME_SKIP_CALLER) && fp->down) - fp = fp->down; - if (fp && fp->down && fp->down->fun && fp->down->argv) - *vp = fp->down->argv[-2]; - else - *vp = JSVAL_NULL; - if (!JSVAL_IS_PRIMITIVE(*vp) && cx->runtime->checkObjectAccess) { - id = ATOM_KEY(cx->runtime->atomState.callerAtom); - if (!cx->runtime->checkObjectAccess(cx, obj, id, JSACC_READ, vp)) - return JS_FALSE; - } - break; - - default: - /* XXX fun[0] and fun.arguments[0] are equivalent. */ - if (fp && fp->fun && (uintN)slot < fp->fun->nargs) - *vp = fp->argv[slot]; - break; - } - - return JS_TRUE; -} - -static JSBool -fun_enumerate(JSContext *cx, JSObject *obj) -{ - jsid prototypeId; - JSObject *pobj; - JSProperty *prop; - - prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom); - if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop)) - return JS_FALSE; - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - return JS_TRUE; -} - -static JSBool -fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - JSFunction *fun; - JSString *str; - JSAtom *prototypeAtom; - - /* - * No need to reflect fun.prototype in 'fun.prototype = ...' or in an - * unqualified reference to prototype, which the emitter looks up as a - * hidden atom when attempting to bind to a formal parameter or local - * variable slot. - */ - if (flags & (JSRESOLVE_ASSIGNING | JSRESOLVE_HIDDEN)) - return JS_TRUE; - - if (!JSVAL_IS_STRING(id)) - return JS_TRUE; - - /* No valid function object should lack private data, but check anyway. */ - fun = (JSFunction *)JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL); - if (!fun || !fun->object) - return JS_TRUE; - - /* - * Ok, check whether id is 'prototype' and bootstrap the function object's - * prototype property. - */ - str = JSVAL_TO_STRING(id); - prototypeAtom = cx->runtime->atomState.classPrototypeAtom; - if (str == ATOM_TO_STRING(prototypeAtom)) { - JSObject *proto, *parentProto; - jsval pval; - - proto = parentProto = NULL; - if (fun->object != obj && fun->object) { - /* - * Clone of a function: make its prototype property value have the - * same class as the clone-parent's prototype. - */ - if (!OBJ_GET_PROPERTY(cx, fun->object, ATOM_TO_JSID(prototypeAtom), - &pval)) { - return JS_FALSE; - } - if (!JSVAL_IS_PRIMITIVE(pval)) { - /* - * We are about to allocate a new object, so hack the newborn - * root until then to protect pval in case it is figuratively - * up in the air, with no strong refs protecting it. - */ - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(pval); - parentProto = JSVAL_TO_OBJECT(pval); - } - } - - /* - * Beware of the wacky case of a user function named Object -- trying - * to find a prototype for that will recur back here _ad perniciem_. - */ - if (!parentProto && fun->atom == CLASS_ATOM(cx, Object)) - return JS_TRUE; - - /* - * If resolving "prototype" in a clone, clone the parent's prototype. - * Pass the constructor's (obj's) parent as the prototype parent, to - * avoid defaulting to parentProto.constructor.__parent__. - */ - proto = js_NewObject(cx, &js_ObjectClass, parentProto, - OBJ_GET_PARENT(cx, obj)); - if (!proto) - return JS_FALSE; - - /* - * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for - * user-defined functions, but DontEnum | ReadOnly | DontDelete for - * native "system" constructors such as Object or Function. So lazily - * set the former here in fun_resolve, but eagerly define the latter - * in JS_InitClass, with the right attributes. - */ - if (!js_SetClassPrototype(cx, obj, proto, - JSPROP_ENUMERATE | JSPROP_PERMANENT)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return JS_FALSE; - } - *objp = obj; - } - - return JS_TRUE; -} - -static JSBool -fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp) -{ - switch (type) { - case JSTYPE_FUNCTION: - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - default: - return js_TryValueOf(cx, obj, type, vp); - } -} - -static void -fun_finalize(JSContext *cx, JSObject *obj) -{ - JSFunction *fun; - JSScript *script; - - /* No valid function object should lack private data, but check anyway. */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (!fun) - return; - if (fun->object == obj) - fun->object = NULL; - - /* Null-check required since the parser sets interpreted very early. */ - if (FUN_INTERPRETED(fun) && fun->u.i.script && - js_IsAboutToBeFinalized(cx, fun)) - { - script = fun->u.i.script; - fun->u.i.script = NULL; - js_DestroyScript(cx, script); - } -} - -#if JS_HAS_XDR - -#include "jsxdrapi.h" - -enum { - JSXDR_FUNARG = 1, - JSXDR_FUNVAR = 2, - JSXDR_FUNCONST = 3 -}; - -/* XXX store parent and proto, if defined */ -static JSBool -fun_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - JSFunction *fun; - uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ - JSTempValueRooter tvr; - uint32 flagsword; /* originally only flags was JS_XDRUint8'd */ - uint16 extraUnused; /* variable for no longer used field */ - JSAtom *propAtom; - JSScopeProperty *sprop; - uint32 userid; /* NB: holds a signed int-tagged jsval */ - uintN i, n, dupflag; - uint32 type; - JSBool ok; -#ifdef DEBUG - uintN nvars = 0, nargs = 0; -#endif - - cx = xdr->cx; - if (xdr->mode == JSXDR_ENCODE) { - /* - * No valid function object should lack private data, but fail soft - * (return true, no error report) in case one does due to API pilot - * or internal error. - */ - fun = (JSFunction *) JS_GetPrivate(cx, *objp); - if (!fun) - return JS_TRUE; - if (!FUN_INTERPRETED(fun)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NOT_SCRIPTED_FUNCTION, - JS_GetFunctionName(fun)); - return JS_FALSE; - } - nullAtom = !fun->atom; - flagsword = ((uint32)fun->u.i.nregexps << 16) | fun->flags; - extraUnused = 0; - } else { - fun = js_NewFunction(cx, NULL, NULL, 0, 0, NULL, NULL); - if (!fun) - return JS_FALSE; - } - - /* From here on, control flow must flow through label out. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, fun->object, &tvr); - ok = JS_TRUE; - - if (!JS_XDRUint32(xdr, &nullAtom)) - goto bad; - if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom)) - goto bad; - - if (!JS_XDRUint16(xdr, &fun->nargs) || - !JS_XDRUint16(xdr, &extraUnused) || - !JS_XDRUint16(xdr, &fun->u.i.nvars) || - !JS_XDRUint32(xdr, &flagsword)) { - goto bad; - } - - /* Assert that all previous writes of extraUnused were writes of 0. */ - JS_ASSERT(extraUnused == 0); - - /* do arguments and local vars */ - if (fun->object) { - n = fun->nargs + fun->u.i.nvars; - if (xdr->mode == JSXDR_ENCODE) { - JSScope *scope; - JSScopeProperty **spvec, *auto_spvec[8]; - void *mark; - - if (n <= sizeof auto_spvec / sizeof auto_spvec[0]) { - spvec = auto_spvec; - mark = NULL; - } else { - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(spvec, JSScopeProperty **, &cx->tempPool, - n * sizeof(JSScopeProperty *)); - if (!spvec) { - JS_ReportOutOfMemory(cx); - goto bad; - } - } - scope = OBJ_SCOPE(fun->object); - for (sprop = SCOPE_LAST_PROP(scope); sprop; - sprop = sprop->parent) { - if (sprop->getter == js_GetArgument) { - JS_ASSERT(nargs++ <= fun->nargs); - spvec[sprop->shortid] = sprop; - } else if (sprop->getter == js_GetLocalVariable) { - JS_ASSERT(nvars++ <= fun->u.i.nvars); - spvec[fun->nargs + sprop->shortid] = sprop; - } - } - for (i = 0; i < n; i++) { - sprop = spvec[i]; - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - type = (i < fun->nargs) - ? JSXDR_FUNARG - : (sprop->attrs & JSPROP_READONLY) - ? JSXDR_FUNCONST - : JSXDR_FUNVAR; - userid = INT_TO_JSVAL(sprop->shortid); - propAtom = JSID_TO_ATOM(sprop->id); - if (!JS_XDRUint32(xdr, &type) || - !JS_XDRUint32(xdr, &userid) || - !js_XDRCStringAtom(xdr, &propAtom)) { - if (mark) - JS_ARENA_RELEASE(&cx->tempPool, mark); - goto bad; - } - } - if (mark) - JS_ARENA_RELEASE(&cx->tempPool, mark); - } else { - JSPropertyOp getter, setter; - - for (i = n; i != 0; i--) { - uintN attrs = JSPROP_PERMANENT; - - if (!JS_XDRUint32(xdr, &type) || - !JS_XDRUint32(xdr, &userid) || - !js_XDRCStringAtom(xdr, &propAtom)) { - goto bad; - } - JS_ASSERT(type == JSXDR_FUNARG || type == JSXDR_FUNVAR || - type == JSXDR_FUNCONST); - if (type == JSXDR_FUNARG) { - getter = js_GetArgument; - setter = js_SetArgument; - JS_ASSERT(nargs++ <= fun->nargs); - } else if (type == JSXDR_FUNVAR || type == JSXDR_FUNCONST) { - getter = js_GetLocalVariable; - setter = js_SetLocalVariable; - if (type == JSXDR_FUNCONST) - attrs |= JSPROP_READONLY; - JS_ASSERT(nvars++ <= fun->u.i.nvars); - } else { - getter = NULL; - setter = NULL; - } - - /* Flag duplicate argument if atom is bound in fun->object. */ - dupflag = SCOPE_GET_PROPERTY(OBJ_SCOPE(fun->object), - ATOM_TO_JSID(propAtom)) - ? SPROP_IS_DUPLICATE - : 0; - - if (!js_AddHiddenProperty(cx, fun->object, - ATOM_TO_JSID(propAtom), - getter, setter, SPROP_INVALID_SLOT, - attrs | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - JSVAL_TO_INT(userid))) { - goto bad; - } - } - } - } - - if (!js_XDRScript(xdr, &fun->u.i.script, NULL)) - goto bad; - - if (xdr->mode == JSXDR_DECODE) { - fun->flags = (uint16) flagsword | JSFUN_INTERPRETED; - fun->u.i.nregexps = (uint16) (flagsword >> 16); - - *objp = fun->object; - js_CallNewScriptHook(cx, fun->u.i.script, fun); - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - -bad: - ok = JS_FALSE; - goto out; -} - -#else /* !JS_HAS_XDR */ - -#define fun_xdrObject NULL - -#endif /* !JS_HAS_XDR */ - -/* - * [[HasInstance]] internal method for Function objects: fetch the .prototype - * property of its 'this' parameter, and walks the prototype chain of v (only - * if v is an object) returning true if .prototype is found. - */ -static JSBool -fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - jsval pval; - JSString *str; - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &pval)) { - return JS_FALSE; - } - - if (JSVAL_IS_PRIMITIVE(pval)) { - /* - * Throw a runtime error if instanceof is called on a function that - * has a non-object as its .prototype value. - */ - str = js_DecompileValueGenerator(cx, -1, OBJECT_TO_JSVAL(obj), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_PROTOTYPE, JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp); -} - -static uint32 -fun_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSFunction *fun; - - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (fun) { - GC_MARK(cx, fun, "private"); - if (fun->atom) - GC_MARK_ATOM(cx, fun->atom); - if (FUN_INTERPRETED(fun) && fun->u.i.script) - js_MarkScript(cx, fun->u.i.script); - } - return 0; -} - -static uint32 -fun_reserveSlots(JSContext *cx, JSObject *obj) -{ - JSFunction *fun; - - fun = (JSFunction *) JS_GetPrivate(cx, obj); - return (fun && FUN_INTERPRETED(fun)) ? fun->u.i.nregexps : 0; -} - -/* - * Reserve two slots in all function objects for XPConnect. Note that this - * does not bloat every instance, only those on which reserved slots are set, - * and those on which ad-hoc properties are defined. - */ -JS_FRIEND_DATA(JSClass) js_FunctionClass = { - js_Function_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) | - JSCLASS_HAS_CACHED_PROTO(JSProto_Function), - JS_PropertyStub, JS_PropertyStub, - fun_getProperty, JS_PropertyStub, - fun_enumerate, (JSResolveOp)fun_resolve, - fun_convert, fun_finalize, - NULL, NULL, - NULL, NULL, - fun_xdrObject, fun_hasInstance, - fun_mark, fun_reserveSlots -}; - -JSBool -js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, - uintN argc, jsval *argv, jsval *rval) -{ - jsval fval; - JSFunction *fun; - JSString *str; - - if (!argv) { - JS_ASSERT(JS_ObjectIsFunction(cx, obj)); - } else { - fval = argv[-1]; - if (!VALUE_IS_FUNCTION(cx, fval)) { - /* - * If we don't have a function to start off with, try converting - * the object to a function. If that doesn't work, complain. - */ - if (JSVAL_IS_OBJECT(fval)) { - obj = JSVAL_TO_OBJECT(fval); - if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION, - &fval)) { - return JS_FALSE; - } - argv[-1] = fval; - } - if (!VALUE_IS_FUNCTION(cx, fval)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, js_toString_str, - JS_GetTypeName(cx, - JS_TypeOfValue(cx, fval))); - return JS_FALSE; - } - } - - obj = JSVAL_TO_OBJECT(fval); - } - - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (!fun) - return JS_TRUE; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - str = JS_DecompileFunction(cx, fun, (uintN)indent); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -fun_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return js_fun_toString(cx, obj, 0, argc, argv, rval); -} - -#if JS_HAS_TOSOURCE -static JSBool -fun_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return js_fun_toString(cx, obj, JS_DONT_PRETTY_PRINT, argc, argv, rval); -} -#endif - -static const char call_str[] = "call"; - -static JSBool -fun_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval fval, *sp, *oldsp; - JSString *str; - void *mark; - uintN i; - JSStackFrame *fp; - JSBool ok; - - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) - return JS_FALSE; - fval = argv[-1]; - - if (!VALUE_IS_FUNCTION(cx, fval)) { - str = JS_ValueToString(cx, fval); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, call_str, - JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - if (argc == 0) { - /* Call fun with its global object as the 'this' param if no args. */ - obj = NULL; - } else { - /* Otherwise convert the first arg to 'this' and skip over it. */ - if (!js_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - argc--; - argv++; - } - - /* Allocate stack space for fval, obj, and the args. */ - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) - return JS_FALSE; - - /* Push fval, obj, and the args. */ - *sp++ = fval; - *sp++ = OBJECT_TO_JSVAL(obj); - for (i = 0; i < argc; i++) - *sp++ = argv[i]; - - /* Lift current frame to include the args and do the call. */ - fp = cx->fp; - oldsp = fp->sp; - fp->sp = sp; - ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); - - /* Store rval and pop stack back to our frame's sp. */ - *rval = fp->sp[-1]; - fp->sp = oldsp; - js_FreeStack(cx, mark); - return ok; -} - -static JSBool -fun_apply(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval fval, *sp, *oldsp; - JSString *str; - JSObject *aobj; - jsuint length; - JSBool arraylike, ok; - void *mark; - uintN i; - JSStackFrame *fp; - - if (argc == 0) { - /* Will get globalObject as 'this' and no other arguments. */ - return fun_call(cx, obj, argc, argv, rval); - } - - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &argv[-1])) - return JS_FALSE; - fval = argv[-1]; - - if (!VALUE_IS_FUNCTION(cx, fval)) { - str = JS_ValueToString(cx, fval); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_PROTO, - js_Function_str, "apply", - JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - /* Quell GCC overwarnings. */ - aobj = NULL; - length = 0; - - if (argc >= 2) { - /* If the 2nd arg is null or void, call the function with 0 args. */ - if (JSVAL_IS_NULL(argv[1]) || JSVAL_IS_VOID(argv[1])) { - argc = 0; - } else { - /* The second arg must be an array (or arguments object). */ - arraylike = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(argv[1])) { - aobj = JSVAL_TO_OBJECT(argv[1]); - if (!js_IsArrayLike(cx, aobj, &arraylike, &length)) - return JS_FALSE; - } - if (!arraylike) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_APPLY_ARGS, "apply"); - return JS_FALSE; - } - } - } - - /* Convert the first arg to 'this' and skip over it. */ - if (!js_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - - /* Allocate stack space for fval, obj, and the args. */ - argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1); - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) - return JS_FALSE; - - /* Push fval, obj, and aobj's elements as args. */ - *sp++ = fval; - *sp++ = OBJECT_TO_JSVAL(obj); - for (i = 0; i < argc; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); - if (!ok) - goto out; - sp++; - } - - /* Lift current frame to include the args and do the call. */ - fp = cx->fp; - oldsp = fp->sp; - fp->sp = sp; - ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL | JSINVOKE_SKIP_CALLER); - - /* Store rval and pop stack back to our frame's sp. */ - *rval = fp->sp[-1]; - fp->sp = oldsp; -out: - js_FreeStack(cx, mark); - return ok; -} - -#ifdef NARCISSUS -static JSBool -fun_applyConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *aobj; - uintN length, i; - void *mark; - jsval *sp, *newsp, *oldsp; - JSStackFrame *fp; - JSBool ok; - - if (JSVAL_IS_PRIMITIVE(argv[0]) || - (aobj = JSVAL_TO_OBJECT(argv[0]), - OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass && - OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_APPLY_ARGS, "__applyConstruct__"); - return JS_FALSE; - } - - if (!js_GetLengthProperty(cx, aobj, &length)) - return JS_FALSE; - - if (length >= ARRAY_INIT_LIMIT) - length = ARRAY_INIT_LIMIT - 1; - newsp = sp = js_AllocStack(cx, 2 + length, &mark); - if (!sp) - return JS_FALSE; - - fp = cx->fp; - oldsp = fp->sp; - *sp++ = OBJECT_TO_JSVAL(obj); - *sp++ = JSVAL_NULL; /* This is filled automagically. */ - for (i = 0; i < length; i++) { - ok = JS_GetElement(cx, aobj, (jsint)i, sp); - if (!ok) - goto out; - sp++; - } - - oldsp = fp->sp; - fp->sp = sp; - ok = js_InvokeConstructor(cx, newsp, length); - - *rval = fp->sp[-1]; - fp->sp = oldsp; -out: - js_FreeStack(cx, mark); - return ok; -} -#endif - -static JSFunctionSpec function_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, fun_toSource, 0,0,0}, -#endif - {js_toString_str, fun_toString, 1,0,0}, - {"apply", fun_apply, 2,0,0}, - {call_str, fun_call, 1,0,0}, -#ifdef NARCISSUS - {"__applyConstructor__", fun_applyConstructor, 1,0,0}, -#endif - {0,0,0,0,0} -}; - -JSBool -js_IsIdentifier(JSString *str) -{ - size_t length; - jschar c, *chars, *end, *s; - - length = JSSTRING_LENGTH(str); - if (length == 0) - return JS_FALSE; - chars = JSSTRING_CHARS(str); - c = *chars; - if (!JS_ISIDSTART(c)) - return JS_FALSE; - end = chars + length; - for (s = chars + 1; s != end; ++s) { - c = *s; - if (!JS_ISIDENT(c)) - return JS_FALSE; - } - return !js_IsKeyword(chars, length); -} - -static JSBool -Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSStackFrame *fp, *caller; - JSFunction *fun; - JSObject *parent; - uintN i, n, lineno, dupflag; - JSAtom *atom; - const char *filename; - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - JSString *str, *arg; - void *mark; - JSTokenStream *ts; - JSPrincipals *principals; - jschar *collected_args, *cp; - size_t arg_length, args_length, old_args_length; - JSTokenType tt; - JSBool ok; - - fp = cx->fp; - if (!(fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - fun = (JSFunction *) JS_GetPrivate(cx, obj); - if (fun) - return JS_TRUE; - - /* - * NB: (new Function) is not lexically closed by its caller, it's just an - * anonymous function in the top-level scope that its constructor inhabits. - * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42, - * and so would a call to f from another top-level's script or function. - * - * In older versions, before call objects, a new Function was adopted by - * its running context's globalObject, which might be different from the - * top-level reachable from scopeChain (in HTML frames, e.g.). - */ - parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2])); - - fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA, parent, - cx->runtime->atomState.anonymousAtom); - - if (!fun) - return JS_FALSE; - - /* - * Function is static and not called directly by other functions in this - * file, therefore it is callable only as a native function by js_Invoke. - * Find the scripted caller, possibly skipping other native frames such as - * are built for Function.prototype.call or .apply activations that invoke - * Function indirectly from a script. - */ - JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function); - caller = JS_GetScriptedCaller(cx, fp); - if (caller) { - filename = caller->script->filename; - lineno = js_PCToLineNumber(cx, caller->script, caller->pc); - principals = JS_EvalFramePrincipals(cx, fp, caller); - } else { - filename = NULL; - lineno = 0; - principals = NULL; - } - - /* Belt-and-braces: check that the caller has access to parent. */ - if (!js_CheckPrincipalsAccess(cx, parent, principals, - CLASS_ATOM(cx, Function))) { - return JS_FALSE; - } - - n = argc ? argc - 1 : 0; - if (n > 0) { - /* - * Collect the function-argument arguments into one string, separated - * by commas, then make a tokenstream from that string, and scan it to - * get the arguments. We need to throw the full scanner at the - * problem, because the argument string can legitimately contain - * comments and linefeeds. XXX It might be better to concatenate - * everything up into a function definition and pass it to the - * compiler, but doing it this way is less of a delta from the old - * code. See ECMA 15.3.2.1. - */ - args_length = 0; - for (i = 0; i < n; i++) { - /* Collect the lengths for all the function-argument arguments. */ - arg = js_ValueToString(cx, argv[i]); - if (!arg) - return JS_FALSE; - argv[i] = STRING_TO_JSVAL(arg); - - /* - * Check for overflow. The < test works because the maximum - * JSString length fits in 2 fewer bits than size_t has. - */ - old_args_length = args_length; - args_length = old_args_length + JSSTRING_LENGTH(arg); - if (args_length < old_args_length) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - - /* Add 1 for each joining comma and check for overflow (two ways). */ - old_args_length = args_length; - args_length = old_args_length + n - 1; - if (args_length < old_args_length || - args_length >= ~(size_t)0 / sizeof(jschar)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* - * Allocate a string to hold the concatenated arguments, including room - * for a terminating 0. Mark cx->tempPool for later release, to free - * collected_args and its tokenstream in one swoop. - */ - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool, - (args_length+1) * sizeof(jschar)); - if (!cp) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - collected_args = cp; - - /* - * Concatenate the arguments into the new string, separated by commas. - */ - for (i = 0; i < n; i++) { - arg = JSVAL_TO_STRING(argv[i]); - arg_length = JSSTRING_LENGTH(arg); - (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length); - cp += arg_length; - - /* Add separating comma or terminating 0. */ - *cp++ = (i + 1 < n) ? ',' : 0; - } - - /* - * Make a tokenstream (allocated from cx->tempPool) that reads from - * the given string. - */ - ts = js_NewTokenStream(cx, collected_args, args_length, filename, - lineno, principals); - if (!ts) { - JS_ARENA_RELEASE(&cx->tempPool, mark); - return JS_FALSE; - } - - /* The argument string may be empty or contain no tokens. */ - tt = js_GetToken(cx, ts); - if (tt != TOK_EOF) { - for (;;) { - /* - * Check that it's a name. This also implicitly guards against - * TOK_ERROR, which was already reported. - */ - if (tt != TOK_NAME) - goto bad_formal; - - /* - * Get the atom corresponding to the name from the tokenstream; - * we're assured at this point that it's a valid identifier. - */ - atom = CURRENT_TOKEN(ts).t_atom; - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &obj2, &prop)) { - goto bad_formal; - } - sprop = (JSScopeProperty *) prop; - dupflag = 0; - if (sprop) { - ok = JS_TRUE; - if (obj2 == obj) { - const char *name = js_AtomToPrintableString(cx, atom); - - /* - * A duplicate parameter name. We force a duplicate - * node on the SCOPE_LAST_PROP(scope) list with the - * same id, distinguished by the SPROP_IS_DUPLICATE - * flag, and not mapped by an entry in scope. - */ - JS_ASSERT(sprop->getter == js_GetArgument); - ok = name && - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_DUPLICATE_FORMAL, - name); - - dupflag = SPROP_IS_DUPLICATE; - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - goto bad_formal; - sprop = NULL; - } - if (!js_AddHiddenProperty(cx, fun->object, ATOM_TO_JSID(atom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - fun->nargs)) { - goto bad_formal; - } - if (fun->nargs == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - goto bad; - } - fun->nargs++; - - /* - * Get the next token. Stop on end of stream. Otherwise - * insist on a comma, get another name, and iterate. - */ - tt = js_GetToken(cx, ts); - if (tt == TOK_EOF) - break; - if (tt != TOK_COMMA) - goto bad_formal; - tt = js_GetToken(cx, ts); - } - } - - /* Clean up. */ - ok = js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!ok) - return JS_FALSE; - } - - if (argc) { - str = js_ValueToString(cx, argv[argc-1]); - } else { - /* Can't use cx->runtime->emptyString because we're called too early. */ - str = js_NewStringCopyZ(cx, js_empty_ucstr, 0); - } - if (!str) - return JS_FALSE; - if (argv) { - /* Use the last arg (or this if argc == 0) as a local GC root. */ - argv[(intN)(argc-1)] = STRING_TO_JSVAL(str); - } - - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewTokenStream(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), - filename, lineno, principals); - if (!ts) { - ok = JS_FALSE; - } else { - ok = js_CompileFunctionBody(cx, ts, fun) && - js_CloseTokenStream(cx, ts); - } - JS_ARENA_RELEASE(&cx->tempPool, mark); - return ok; - -bad_formal: - /* - * Report "malformed formal parameter" iff no illegal char or similar - * scanner error was already reported. - */ - if (!(ts->flags & TSF_ERROR)) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); - -bad: - /* - * Clean up the arguments string and tokenstream if we failed to parse - * the arguments. - */ - (void)js_CloseTokenStream(cx, ts); - JS_ARENA_RELEASE(&cx->tempPool, mark); - return JS_FALSE; -} - -JSObject * -js_InitFunctionClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - JSAtom *atom; - JSFunction *fun; - - proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1, - function_props, function_methods, NULL, NULL); - if (!proto) - return NULL; - atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), - 0); - if (!atom) - goto bad; - fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); - if (!fun) - goto bad; - fun->u.i.script = js_NewScript(cx, 1, 0, 0); - if (!fun->u.i.script) - goto bad; - fun->u.i.script->code[0] = JSOP_STOP; - fun->flags |= JSFUN_INTERPRETED; - return proto; - -bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -JSObject * -js_InitCallClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0, - call_props, NULL, NULL, NULL); - if (!proto) - return NULL; - - /* - * Null Call.prototype's proto slot so that Object.prototype.* does not - * pollute the scope of heavyweight functions. - */ - OBJ_SET_PROTO(cx, proto, NULL); - return proto; -} - -JSFunction * -js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom) -{ - JSFunction *fun; - JSTempValueRooter tvr; - - /* If funobj is null, allocate an object for it. */ - if (funobj) { - OBJ_SET_PARENT(cx, funobj, parent); - } else { - funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent); - if (!funobj) - return NULL; - } - - /* Protect fun from any potential GC callback. */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(funobj), &tvr); - - /* - * Allocate fun after allocating funobj so slot allocation in js_NewObject - * does not wipe out fun from newborn[GCX_PRIVATE]. - */ - fun = (JSFunction *) js_NewGCThing(cx, GCX_PRIVATE, sizeof(JSFunction)); - if (!fun) - goto out; - - /* Initialize all function members. */ - fun->object = NULL; - fun->nargs = nargs; - fun->flags = flags & JSFUN_FLAGS_MASK; - fun->u.n.native = native; - fun->u.n.extra = 0; - fun->u.n.spare = 0; - fun->atom = atom; - fun->clasp = NULL; - - /* Link fun to funobj and vice versa. */ - if (!js_LinkFunctionObject(cx, fun, funobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - fun = NULL; - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return fun; -} - -JSObject * -js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) -{ - JSObject *newfunobj; - JSFunction *fun; - - JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass); - newfunobj = js_NewObject(cx, &js_FunctionClass, funobj, parent); - if (!newfunobj) - return NULL; - fun = (JSFunction *) JS_GetPrivate(cx, funobj); - if (!js_LinkFunctionObject(cx, fun, newfunobj)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - return newfunobj; -} - -JSBool -js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *funobj) -{ - if (!fun->object) - fun->object = funobj; - return JS_SetPrivate(cx, funobj, fun); -} - -JSFunction * -js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, - uintN nargs, uintN attrs) -{ - JSFunction *fun; - - fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); - if (!fun) - return NULL; - if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), - OBJECT_TO_JSVAL(fun->object), - NULL, NULL, - attrs & ~JSFUN_FLAGS_MASK, NULL)) { - return NULL; - } - return fun; -} - -#if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK) -# error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!" -#endif - -JSFunction * -js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags) -{ - jsval v; - JSObject *obj; - - v = *vp; - obj = NULL; - if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v)) - return NULL; - obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL; - } - } - if (!obj) { - js_ReportIsNotFunction(cx, vp, flags); - return NULL; - } - return (JSFunction *) JS_GetPrivate(cx, obj); -} - -JSObject * -js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags) -{ - JSFunction *fun; - JSObject *funobj; - JSStackFrame *caller; - JSPrincipals *principals; - - if (VALUE_IS_FUNCTION(cx, *vp)) - return JSVAL_TO_OBJECT(*vp); - - fun = js_ValueToFunction(cx, vp, flags); - if (!fun) - return NULL; - funobj = fun->object; - *vp = OBJECT_TO_JSVAL(funobj); - - caller = JS_GetScriptedCaller(cx, cx->fp); - if (caller) { - principals = caller->script->principals; - } else { - /* No scripted caller, don't allow access. */ - principals = NULL; - } - - if (!js_CheckPrincipalsAccess(cx, funobj, principals, - fun->atom - ? fun->atom - : cx->runtime->atomState.anonymousAtom)) { - return NULL; - } - return funobj; -} - -JSObject * -js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags) -{ - JSObject *callable; - - callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp); - if (callable && - ((callable->map->ops == &js_ObjectOps) - ? OBJ_GET_CLASS(cx, callable)->call - : callable->map->ops->call)) { - *vp = OBJECT_TO_JSVAL(callable); - } else { - callable = js_ValueToFunctionObject(cx, vp, flags); - } - return callable; -} - -void -js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) -{ - JSStackFrame *fp; - JSString *str; - JSTempValueRooter tvr; - const char *bytes, *source; - - for (fp = cx->fp; fp && !fp->spbase; fp = fp->down) - continue; - str = js_DecompileValueGenerator(cx, - (fp && fp->spbase <= vp && vp < fp->sp) - ? vp - fp->sp - : (flags & JSV2F_SEARCH_STACK) - ? JSDVG_SEARCH_STACK - : JSDVG_IGNORE_STACK, - *vp, - NULL); - if (str) { - JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); - bytes = JS_GetStringBytes(str); - if (flags & JSV2F_ITERATOR) { - source = js_ValueToPrintableSource(cx, *vp); - if (source) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR, - bytes, js_iterator_str, source); - } - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - (uintN)((flags & JSV2F_CONSTRUCT) - ? JSMSG_NOT_CONSTRUCTOR - : JSMSG_NOT_FUNCTION), - bytes); - } - JS_POP_TEMP_ROOT(cx, &tvr); - } -} diff --git a/spidermonkey/libjs/jsfun.h b/spidermonkey/libjs/jsfun.h deleted file mode 100644 index 8d5c185..0000000 --- a/spidermonkey/libjs/jsfun.h +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsfun_h___ -#define jsfun_h___ -/* - * JS function definitions. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -struct JSFunction { - JSObject *object; /* back-pointer to GC'ed object header */ - uint16 nargs; /* minimum number of actual arguments */ - uint16 flags; /* bound method and other flags, see jsapi.h */ - union { - struct { - uint16 extra; /* number of arg slots for local GC roots */ - uint16 spare; /* reserved for future use */ - JSNative native; /* native method pointer or null */ - } n; - struct { - uint16 nvars; /* number of local variables */ - uint16 nregexps; /* number of regular expressions literals */ - JSScript *script; /* interpreted bytecode descriptor or null */ - } i; - } u; - JSAtom *atom; /* name for diagnostics and decompiling */ - JSClass *clasp; /* if non-null, constructor for this class */ -}; - -#define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */ - -#define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED) -#define FUN_NATIVE(fun) (FUN_INTERPRETED(fun) ? NULL : (fun)->u.n.native) -#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) - -extern JSClass js_ArgumentsClass; -extern JSClass js_CallClass; - -/* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */ -extern JS_FRIEND_DATA(JSClass) js_FunctionClass; - -/* - * NB: jsapi.h and jsobj.h must be included before any call to this macro. - */ -#define VALUE_IS_FUNCTION(cx, v) \ - (!JSVAL_IS_PRIMITIVE(v) && \ - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_FunctionClass) - -extern JSBool -js_fun_toString(JSContext *cx, JSObject *obj, uint32 indent, - uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_IsIdentifier(JSString *str); - -extern JSObject * -js_InitFunctionClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitArgumentsClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitCallClass(JSContext *cx, JSObject *obj); - -extern JSFunction * -js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs, - uintN flags, JSObject *parent, JSAtom *atom); - -extern JSObject * -js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent); - -extern JSBool -js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object); - -extern JSFunction * -js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, - uintN nargs, uintN flags); - -/* - * Flags for js_ValueToFunction and js_ReportIsNotFunction. We depend on the - * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that - * with #if/#error in jsfun.c. - */ -#define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT -#define JSV2F_ITERATOR JSINVOKE_ITERATOR -#define JSV2F_SEARCH_STACK 0x10000 - -extern JSFunction * -js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags); - -extern JSObject * -js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags); - -extern JSObject * -js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags); - -extern void -js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags); - -extern JSObject * -js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent); - -extern JSBool -js_PutCallObject(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp); - -extern JSBool -js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, - JSObject **objp, jsval *vp); - -extern JSObject * -js_GetArgsObject(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_PutArgsObject(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_XDRFunction(JSXDRState *xdr, JSObject **objp); - -JS_END_EXTERN_C - -#endif /* jsfun_h___ */ diff --git a/spidermonkey/libjs/jsgc.c b/spidermonkey/libjs/jsgc.c deleted file mode 100644 index 7fae096..0000000 --- a/spidermonkey/libjs/jsgc.c +++ /dev/null @@ -1,3201 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS Mark-and-Sweep Garbage Collector. - * - * This GC allocates fixed-sized things with sizes up to GC_NBYTES_MAX (see - * jsgc.h). It allocates from a special GC arena pool with each arena allocated - * using malloc. It uses an ideally parallel array of flag bytes to hold the - * mark bit, finalizer type index, etc. - * - * XXX swizzle page to freelist for better locality of reference - */ -#include "jsstddef.h" -#include /* for free */ -#include /* for memset used when DEBUG */ -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jsbit.h" -#include "jsclist.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -/* - * GC arena sizing depends on amortizing arena overhead using a large number - * of things per arena, and on the thing/flags ratio of 8:1 on most platforms. - * - * On 64-bit platforms, we would have half as many things per arena because - * pointers are twice as big, so we double the bytes for things per arena. - * This preserves the 1024 byte flags sub-arena size, which relates to the - * GC_PAGE_SIZE (see below for why). - */ -#if JS_BYTES_PER_WORD == 8 -# define GC_THINGS_SHIFT 14 /* 16KB for things on Alpha, etc. */ -#else -# define GC_THINGS_SHIFT 13 /* 8KB for things on most platforms */ -#endif -#define GC_THINGS_SIZE JS_BIT(GC_THINGS_SHIFT) -#define GC_FLAGS_SIZE (GC_THINGS_SIZE / sizeof(JSGCThing)) - -/* - * A GC arena contains one flag byte for each thing in its heap, and supports - * O(1) lookup of a flag given its thing's address. - * - * To implement this, we take advantage of the thing/flags numerology: given - * the 8K bytes worth of GC-things, there are 1K flag bytes. Within each 9K - * allocation for things+flags there are always 8 consecutive 1K-pages each - * aligned on 1K boundary. We use these pages to allocate things and the - * remaining 1K of space before and after the aligned pages to store flags. - * If we are really lucky and things+flags starts on a 1K boundary, then - * flags would consist of a single 1K chunk that comes after 8K of things. - * Otherwise there are 2 chunks of flags, one before and one after things. - * - * To be able to find the flag byte for a particular thing, we put a - * JSGCPageInfo record at the beginning of each 1K-aligned page to hold that - * page's offset from the beginning of things+flags allocation and we allocate - * things after this record. Thus for each thing |thing_address & ~1023| - * gives the address of a JSGCPageInfo record from which we read page_offset. - * Due to page alignment - * (page_offset & ~1023) + (thing_address & 1023) - * gives thing_offset from the beginning of 8K paged things. We then divide - * thing_offset by sizeof(JSGCThing) to get thing_index. - * - * Now |page_address - page_offset| is things+flags arena_address and - * (page_offset & 1023) is the offset of the first page from the start of - * things+flags area. Thus if - * thing_index < (page_offset & 1023) - * then - * allocation_start_address + thing_index < address_of_the_first_page - * and we use - * allocation_start_address + thing_index - * as the address to store thing's flags. If - * thing_index >= (page_offset & 1023), - * then we use the chunk of flags that comes after the pages with things - * and calculate the address for the flag byte as - * address_of_the_first_page + 8K + (thing_index - (page_offset & 1023)) - * which is just - * allocation_start_address + thing_index + 8K. - * - * When we allocate things with size equal to sizeof(JSGCThing), the overhead - * of this scheme for 32 bit platforms is (8+8*(8+1))/(8+9K) or 0.87% - * (assuming 4 bytes for each JSGCArena header, and 8 bytes for each - * JSGCThing and JSGCPageInfo). When thing_size > 8, the scheme wastes the - * flag byte for each extra 8 bytes beyond sizeof(JSGCThing) in thing_size - * and the overhead is close to 1/8 or 12.5%. - * FIXME: How can we avoid this overhead? - * - * Here's some ASCII art showing an arena: - * - * split or the first 1-K aligned address. - * | - * V - * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ - * |fB| tp0 | tp1 | tp2 | tp3 | tp4 | tp5 | tp6 | tp7 | fA | - * +--+-------+-------+-------+-------+-------+-------+-------+-------+-----+ - * ^ ^ - * tI ---------+ | - * tJ -------------------------------------------+ - * - * - fB are the "before split" flags, fA are the "after split" flags - * - tp0-tp7 are the 8 thing pages - * - thing tI points into tp1, whose flags are below the split, in fB - * - thing tJ points into tp5, clearly above the split - * - * In general, one of the thing pages will have some of its things' flags on - * the low side of the split, and the rest of its things' flags on the high - * side. All the other pages have flags only below or only above. - * - * (If we need to implement card-marking for an incremental GC write barrier, - * we can replace word-sized offsetInArena in JSGCPageInfo by pair of - * uint8 card_mark and uint16 offsetInArena fields as the offset can not exceed - * GC_THINGS_SIZE. This would gives an extremely efficient write barrier: - * when mutating an object obj, just store a 1 byte at - * (uint8 *) ((jsuword)obj & ~1023) on 32-bit platforms.) - */ -#define GC_PAGE_SHIFT 10 -#define GC_PAGE_MASK ((jsuword) JS_BITMASK(GC_PAGE_SHIFT)) -#define GC_PAGE_SIZE JS_BIT(GC_PAGE_SHIFT) -#define GC_PAGE_COUNT (1 << (GC_THINGS_SHIFT - GC_PAGE_SHIFT)) - -typedef struct JSGCPageInfo { - jsuword offsetInArena; /* offset from the arena start */ - jsuword unscannedBitmap; /* bitset for fast search of marked - but not yet scanned GC things */ -} JSGCPageInfo; - -struct JSGCArena { - JSGCArenaList *list; /* allocation list for the arena */ - JSGCArena *prev; /* link field for allocation list */ - JSGCArena *prevUnscanned; /* link field for the list of arenas - with marked but not yet scanned - things */ - jsuword unscannedPages; /* bitset for fast search of pages - with marked but not yet scanned - things */ - uint8 base[1]; /* things+flags allocation area */ -}; - -#define GC_ARENA_SIZE \ - (offsetof(JSGCArena, base) + GC_THINGS_SIZE + GC_FLAGS_SIZE) - -#define FIRST_THING_PAGE(a) \ - (((jsuword)(a)->base + GC_FLAGS_SIZE - 1) & ~GC_PAGE_MASK) - -#define PAGE_TO_ARENA(pi) \ - ((JSGCArena *)((jsuword)(pi) - (pi)->offsetInArena \ - - offsetof(JSGCArena, base))) - -#define PAGE_INDEX(pi) \ - ((size_t)((pi)->offsetInArena >> GC_PAGE_SHIFT)) - -#define THING_TO_PAGE(thing) \ - ((JSGCPageInfo *)((jsuword)(thing) & ~GC_PAGE_MASK)) - -/* - * Given a thing size n, return the size of the gap from the page start before - * the first thing. We know that any n not a power of two packs from - * the end of the page leaving at least enough room for one JSGCPageInfo, but - * not for another thing, at the front of the page (JS_ASSERTs below insist - * on this). - * - * This works because all allocations are a multiple of sizeof(JSGCThing) == - * sizeof(JSGCPageInfo) in size. - */ -#define PAGE_THING_GAP(n) (((n) & ((n) - 1)) ? (GC_PAGE_SIZE % (n)) : (n)) - -#ifdef JS_THREADSAFE -/* - * The maximum number of things to put to the local free list by taking - * several things from the global free list or from the tail of the last - * allocated arena to amortize the cost of rt->gcLock. - * - * We use number 8 based on benchmarks from bug 312238. - */ -#define MAX_THREAD_LOCAL_THINGS 8 - -#endif - -JS_STATIC_ASSERT(sizeof(JSGCThing) == sizeof(JSGCPageInfo)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSObject)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(JSString)); -JS_STATIC_ASSERT(sizeof(JSGCThing) >= sizeof(jsdouble)); -JS_STATIC_ASSERT(GC_FLAGS_SIZE >= GC_PAGE_SIZE); -JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval)); - -/* - * JSPtrTable capacity growth descriptor. The table grows by powers of two - * starting from capacity JSPtrTableInfo.minCapacity, but switching to linear - * growth when capacity reaches JSPtrTableInfo.linearGrowthThreshold. - */ -typedef struct JSPtrTableInfo { - uint16 minCapacity; - uint16 linearGrowthThreshold; -} JSPtrTableInfo; - -#define GC_ITERATOR_TABLE_MIN 4 -#define GC_ITERATOR_TABLE_LINEAR 1024 - -static const JSPtrTableInfo iteratorTableInfo = { - GC_ITERATOR_TABLE_MIN, - GC_ITERATOR_TABLE_LINEAR -}; - -/* Calculate table capacity based on the current value of JSPtrTable.count. */ -static size_t -PtrTableCapacity(size_t count, const JSPtrTableInfo *info) -{ - size_t linear, log, capacity; - - linear = info->linearGrowthThreshold; - JS_ASSERT(info->minCapacity <= linear); - - if (count == 0) { - capacity = 0; - } else if (count < linear) { - log = JS_CEILING_LOG2W(count); - JS_ASSERT(log != JS_BITS_PER_WORD); - capacity = (size_t)1 << log; - if (capacity < info->minCapacity) - capacity = info->minCapacity; - } else { - capacity = JS_ROUNDUP(count, linear); - } - - JS_ASSERT(capacity >= count); - return capacity; -} - -static void -FreePtrTable(JSPtrTable *table, const JSPtrTableInfo *info) -{ - if (table->array) { - JS_ASSERT(table->count > 0); - free(table->array); - table->array = NULL; - table->count = 0; - } - JS_ASSERT(table->count == 0); -} - -static JSBool -AddToPtrTable(JSContext *cx, JSPtrTable *table, const JSPtrTableInfo *info, - void *ptr) -{ - size_t count, capacity; - void **array; - - count = table->count; - capacity = PtrTableCapacity(count, info); - - if (count == capacity) { - if (capacity < info->minCapacity) { - JS_ASSERT(capacity == 0); - JS_ASSERT(!table->array); - capacity = info->minCapacity; - } else { - /* - * Simplify the overflow detection assuming pointer is bigger - * than byte. - */ - JS_STATIC_ASSERT(2 <= sizeof table->array[0]); - capacity = (capacity < info->linearGrowthThreshold) - ? 2 * capacity - : capacity + info->linearGrowthThreshold; - if (capacity > (size_t)-1 / sizeof table->array[0]) - goto bad; - } - array = (void **) realloc(table->array, - capacity * sizeof table->array[0]); - if (!array) - goto bad; -#ifdef DEBUG - memset(array + count, JS_FREE_PATTERN, - (capacity - count) * sizeof table->array[0]); -#endif - table->array = array; - } - - table->array[count] = ptr; - table->count = count + 1; - - return JS_TRUE; - - bad: - JS_ReportOutOfMemory(cx); - return JS_FALSE; -} - -static void -ShrinkPtrTable(JSPtrTable *table, const JSPtrTableInfo *info, - size_t newCount) -{ - size_t oldCapacity, capacity; - void **array; - - JS_ASSERT(newCount <= table->count); - if (newCount == table->count) - return; - - oldCapacity = PtrTableCapacity(table->count, info); - table->count = newCount; - capacity = PtrTableCapacity(newCount, info); - - if (oldCapacity != capacity) { - array = table->array; - JS_ASSERT(array); - if (capacity == 0) { - free(array); - table->array = NULL; - return; - } - array = (void **) realloc(array, capacity * sizeof array[0]); - if (array) - table->array = array; - } -#ifdef DEBUG - memset(table->array + newCount, JS_FREE_PATTERN, - (capacity - newCount) * sizeof table->array[0]); -#endif -} - -#ifdef JS_GCMETER -# define METER(x) x -#else -# define METER(x) ((void) 0) -#endif - -static JSBool -NewGCArena(JSRuntime *rt, JSGCArenaList *arenaList) -{ - JSGCArena *a; - jsuword offset; - JSGCPageInfo *pi; - uint32 *bytesptr; - - /* Check if we are allowed and can allocate a new arena. */ - if (rt->gcBytes >= rt->gcMaxBytes) - return JS_FALSE; - a = (JSGCArena *)malloc(GC_ARENA_SIZE); - if (!a) - return JS_FALSE; - - /* Initialize the JSGCPageInfo records at the start of every thing page. */ - offset = (GC_PAGE_SIZE - ((jsuword)a->base & GC_PAGE_MASK)) & GC_PAGE_MASK; - JS_ASSERT((jsuword)a->base + offset == FIRST_THING_PAGE(a)); - do { - pi = (JSGCPageInfo *) (a->base + offset); - pi->offsetInArena = offset; - pi->unscannedBitmap = 0; - offset += GC_PAGE_SIZE; - } while (offset < GC_THINGS_SIZE); - - METER(++arenaList->stats.narenas); - METER(arenaList->stats.maxarenas - = JS_MAX(arenaList->stats.maxarenas, arenaList->stats.narenas)); - - a->list = arenaList; - a->prev = arenaList->last; - a->prevUnscanned = NULL; - a->unscannedPages = 0; - arenaList->last = a; - arenaList->lastLimit = 0; - - bytesptr = (arenaList == &rt->gcArenaList[0]) - ? &rt->gcBytes - : &rt->gcPrivateBytes; - *bytesptr += GC_ARENA_SIZE; - - return JS_TRUE; -} - -static void -DestroyGCArena(JSRuntime *rt, JSGCArenaList *arenaList, JSGCArena **ap) -{ - JSGCArena *a; - uint32 *bytesptr; - - a = *ap; - JS_ASSERT(a); - bytesptr = (arenaList == &rt->gcArenaList[0]) - ? &rt->gcBytes - : &rt->gcPrivateBytes; - JS_ASSERT(*bytesptr >= GC_ARENA_SIZE); - *bytesptr -= GC_ARENA_SIZE; - METER(rt->gcStats.afree++); - METER(--arenaList->stats.narenas); - if (a == arenaList->last) - arenaList->lastLimit = (uint16)(a->prev ? GC_THINGS_SIZE : 0); - *ap = a->prev; - -#ifdef DEBUG - memset(a, JS_FREE_PATTERN, GC_ARENA_SIZE); -#endif - free(a); -} - -static void -InitGCArenaLists(JSRuntime *rt) -{ - uintN i, thingSize; - JSGCArenaList *arenaList; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - thingSize = GC_FREELIST_NBYTES(i); - JS_ASSERT((size_t)(uint16)thingSize == thingSize); - arenaList->last = NULL; - arenaList->lastLimit = 0; - arenaList->thingSize = (uint16)thingSize; - arenaList->freeList = NULL; - METER(memset(&arenaList->stats, 0, sizeof arenaList->stats)); - } -} - -static void -FinishGCArenaLists(JSRuntime *rt) -{ - uintN i; - JSGCArenaList *arenaList; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - while (arenaList->last) - DestroyGCArena(rt, arenaList, &arenaList->last); - arenaList->freeList = NULL; - } -} - -uint8 * -js_GetGCThingFlags(void *thing) -{ - JSGCPageInfo *pi; - jsuword offsetInArena, thingIndex; - - pi = THING_TO_PAGE(thing); - offsetInArena = pi->offsetInArena; - JS_ASSERT(offsetInArena < GC_THINGS_SIZE); - thingIndex = ((offsetInArena & ~GC_PAGE_MASK) | - ((jsuword)thing & GC_PAGE_MASK)) / sizeof(JSGCThing); - JS_ASSERT(thingIndex < GC_PAGE_SIZE); - if (thingIndex >= (offsetInArena & GC_PAGE_MASK)) - thingIndex += GC_THINGS_SIZE; - return (uint8 *)pi - offsetInArena + thingIndex; -} - -JSRuntime* -js_GetGCStringRuntime(JSString *str) -{ - JSGCPageInfo *pi; - JSGCArenaList *list; - - pi = THING_TO_PAGE(str); - list = PAGE_TO_ARENA(pi)->list; - - JS_ASSERT(list->thingSize == sizeof(JSGCThing)); - JS_ASSERT(GC_FREELIST_INDEX(sizeof(JSGCThing)) == 0); - - return (JSRuntime *)((uint8 *)list - offsetof(JSRuntime, gcArenaList)); -} - -JSBool -js_IsAboutToBeFinalized(JSContext *cx, void *thing) -{ - uint8 flags = *js_GetGCThingFlags(thing); - - return !(flags & (GCF_MARK | GCF_LOCK | GCF_FINAL)); -} - -typedef void (*GCFinalizeOp)(JSContext *cx, JSGCThing *thing); - -#ifndef DEBUG -# define js_FinalizeDouble NULL -#endif - -#if !JS_HAS_XML_SUPPORT -# define js_FinalizeXMLNamespace NULL -# define js_FinalizeXMLQName NULL -# define js_FinalizeXML NULL -#endif - -static GCFinalizeOp gc_finalizers[GCX_NTYPES] = { - (GCFinalizeOp) js_FinalizeObject, /* GCX_OBJECT */ - (GCFinalizeOp) js_FinalizeString, /* GCX_STRING */ - (GCFinalizeOp) js_FinalizeDouble, /* GCX_DOUBLE */ - (GCFinalizeOp) js_FinalizeString, /* GCX_MUTABLE_STRING */ - NULL, /* GCX_PRIVATE */ - (GCFinalizeOp) js_FinalizeXMLNamespace, /* GCX_NAMESPACE */ - (GCFinalizeOp) js_FinalizeXMLQName, /* GCX_QNAME */ - (GCFinalizeOp) js_FinalizeXML, /* GCX_XML */ - NULL, /* GCX_EXTERNAL_STRING */ - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - -#ifdef GC_MARK_DEBUG -static const char newborn_external_string[] = "newborn external string"; - -static const char *gc_typenames[GCX_NTYPES] = { - "newborn object", - "newborn string", - "newborn double", - "newborn mutable string", - "newborn private", - "newborn Namespace", - "newborn QName", - "newborn XML", - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string, - newborn_external_string -}; -#endif - -intN -js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, - JSStringFinalizeOp newop) -{ - uintN i; - - for (i = GCX_EXTERNAL_STRING; i < GCX_NTYPES; i++) { - if (gc_finalizers[i] == (GCFinalizeOp) oldop) { - gc_finalizers[i] = (GCFinalizeOp) newop; - return (intN) i; - } - } - return -1; -} - -/* This is compatible with JSDHashEntryStub. */ -typedef struct JSGCRootHashEntry { - JSDHashEntryHdr hdr; - void *root; - const char *name; -} JSGCRootHashEntry; - -/* Initial size of the gcRootsHash table (SWAG, small enough to amortize). */ -#define GC_ROOTS_SIZE 256 -#define GC_FINALIZE_LEN 1024 - -JSBool -js_InitGC(JSRuntime *rt, uint32 maxbytes) -{ - InitGCArenaLists(rt); - if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL, - sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) { - rt->gcRootsHash.ops = NULL; - return JS_FALSE; - } - rt->gcLocksHash = NULL; /* create lazily */ - - /* - * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes - * for default backward API compatibility. - */ - rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes; - - return JS_TRUE; -} - -#ifdef JS_GCMETER -JS_FRIEND_API(void) -js_DumpGCStats(JSRuntime *rt, FILE *fp) -{ - uintN i; - size_t totalThings, totalMaxThings, totalBytes; - - fprintf(fp, "\nGC allocation statistics:\n"); - -#define UL(x) ((unsigned long)(x)) -#define ULSTAT(x) UL(rt->gcStats.x) - totalThings = 0; - totalMaxThings = 0; - totalBytes = 0; - for (i = 0; i < GC_NUM_FREELISTS; i++) { - JSGCArenaList *list = &rt->gcArenaList[i]; - JSGCArenaStats *stats = &list->stats; - if (stats->maxarenas == 0) { - fprintf(fp, "ARENA LIST %u (thing size %lu): NEVER USED\n", - i, UL(GC_FREELIST_NBYTES(i))); - continue; - } - fprintf(fp, "ARENA LIST %u (thing size %lu):\n", - i, UL(GC_FREELIST_NBYTES(i))); - fprintf(fp, " arenas: %lu\n", UL(stats->narenas)); - fprintf(fp, " max arenas: %lu\n", UL(stats->maxarenas)); - fprintf(fp, " things: %lu\n", UL(stats->nthings)); - fprintf(fp, " max things: %lu\n", UL(stats->maxthings)); - fprintf(fp, " free list: %lu\n", UL(stats->freelen)); - fprintf(fp, " free list density: %.1f%%\n", - stats->narenas == 0 - ? 0.0 - : (100.0 * list->thingSize * (jsdouble)stats->freelen / - (GC_THINGS_SIZE * (jsdouble)stats->narenas))); - fprintf(fp, " average free list density: %.1f%%\n", - stats->totalarenas == 0 - ? 0.0 - : (100.0 * list->thingSize * (jsdouble)stats->totalfreelen / - (GC_THINGS_SIZE * (jsdouble)stats->totalarenas))); - fprintf(fp, " recycles: %lu\n", UL(stats->recycle)); - fprintf(fp, " recycle/alloc ratio: %.2f\n", - (jsdouble)stats->recycle / - (jsdouble)(stats->totalnew - stats->recycle)); - totalThings += stats->nthings; - totalMaxThings += stats->maxthings; - totalBytes += GC_FREELIST_NBYTES(i) * stats->nthings; - } - fprintf(fp, "TOTAL STATS:\n"); - fprintf(fp, " public bytes allocated: %lu\n", UL(rt->gcBytes)); - fprintf(fp, " private bytes allocated: %lu\n", UL(rt->gcPrivateBytes)); - fprintf(fp, " alloc attempts: %lu\n", ULSTAT(alloc)); -#ifdef JS_THREADSAFE - fprintf(fp, " alloc without locks: %1u\n", ULSTAT(localalloc)); -#endif - fprintf(fp, " total GC things: %lu\n", UL(totalThings)); - fprintf(fp, " max total GC things: %lu\n", UL(totalMaxThings)); - fprintf(fp, " GC things size: %lu\n", UL(totalBytes)); - fprintf(fp, "allocation retries after GC: %lu\n", ULSTAT(retry)); - fprintf(fp, " allocation failures: %lu\n", ULSTAT(fail)); - fprintf(fp, " things born locked: %lu\n", ULSTAT(lockborn)); - fprintf(fp, " valid lock calls: %lu\n", ULSTAT(lock)); - fprintf(fp, " valid unlock calls: %lu\n", ULSTAT(unlock)); - fprintf(fp, " mark recursion depth: %lu\n", ULSTAT(depth)); - fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); - fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); - fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); - fprintf(fp, " delayed scan bag adds: %lu\n", ULSTAT(unscanned)); -#ifdef DEBUG - fprintf(fp, " max delayed scan bag size: %lu\n", ULSTAT(maxunscanned)); -#endif - fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); - fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); - fprintf(fp, " useless GC calls: %lu\n", ULSTAT(nopoke)); - fprintf(fp, " thing arenas freed so far: %lu\n", ULSTAT(afree)); - fprintf(fp, " stack segments scanned: %lu\n", ULSTAT(stackseg)); - fprintf(fp, "stack segment slots scanned: %lu\n", ULSTAT(segslots)); - fprintf(fp, "reachable closeable objects: %lu\n", ULSTAT(nclose)); - fprintf(fp, " max reachable closeable: %lu\n", ULSTAT(maxnclose)); - fprintf(fp, " scheduled close hooks: %lu\n", ULSTAT(closelater)); - fprintf(fp, " max scheduled close hooks: %lu\n", ULSTAT(maxcloselater)); -#undef UL -#undef US - -#ifdef JS_ARENAMETER - JS_DumpArenaStats(fp); -#endif -} -#endif - -#ifdef DEBUG -static void -CheckLeakedRoots(JSRuntime *rt); -#endif - -void -js_FinishGC(JSRuntime *rt) -{ -#ifdef JS_ARENAMETER - JS_DumpArenaStats(stdout); -#endif -#ifdef JS_GCMETER - js_DumpGCStats(rt, stdout); -#endif - - FreePtrTable(&rt->gcIteratorTable, &iteratorTableInfo); -#if JS_HAS_GENERATORS - rt->gcCloseState.reachableList = NULL; - METER(rt->gcStats.nclose = 0); - rt->gcCloseState.todoQueue = NULL; -#endif - FinishGCArenaLists(rt); - - if (rt->gcRootsHash.ops) { -#ifdef DEBUG - CheckLeakedRoots(rt); -#endif - JS_DHashTableFinish(&rt->gcRootsHash); - rt->gcRootsHash.ops = NULL; - } - if (rt->gcLocksHash) { - JS_DHashTableDestroy(rt->gcLocksHash); - rt->gcLocksHash = NULL; - } -} - -JSBool -js_AddRoot(JSContext *cx, void *rp, const char *name) -{ - JSBool ok = js_AddRootRT(cx->runtime, rp, name); - if (!ok) - JS_ReportOutOfMemory(cx); - return ok; -} - -JSBool -js_AddRootRT(JSRuntime *rt, void *rp, const char *name) -{ - JSBool ok; - JSGCRootHashEntry *rhe; - - /* - * Due to the long-standing, but now removed, use of rt->gcLock across the - * bulk of js_GC, API users have come to depend on JS_AddRoot etc. locking - * properly with a racing GC, without calling JS_AddRoot from a request. - * We have to preserve API compatibility here, now that we avoid holding - * rt->gcLock across the mark phase (including the root hashtable mark). - * - * If the GC is running and we're called on another thread, wait for this - * GC activation to finish. We can safely wait here (in the case where we - * are called within a request on another thread's context) without fear - * of deadlock because the GC doesn't set rt->gcRunning until after it has - * waited for all active requests to end. - */ - JS_LOCK_GC(rt); -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcLevel > 0); - } -#endif - rhe = (JSGCRootHashEntry *) JS_DHashTableOperate(&rt->gcRootsHash, rp, - JS_DHASH_ADD); - if (rhe) { - rhe->root = rp; - rhe->name = name; - ok = JS_TRUE; - } else { - ok = JS_FALSE; - } - JS_UNLOCK_GC(rt); - return ok; -} - -JSBool -js_RemoveRoot(JSRuntime *rt, void *rp) -{ - /* - * Due to the JS_RemoveRootRT API, we may be called outside of a request. - * Same synchronization drill as above in js_AddRoot. - */ - JS_LOCK_GC(rt); -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->gcRunning || rt->gcLevel > 0); - if (rt->gcRunning && rt->gcThread->id != js_CurrentThreadId()) { - do { - JS_AWAIT_GC_DONE(rt); - } while (rt->gcLevel > 0); - } -#endif - (void) JS_DHashTableOperate(&rt->gcRootsHash, rp, JS_DHASH_REMOVE); - rt->gcPoke = JS_TRUE; - JS_UNLOCK_GC(rt); - return JS_TRUE; -} - -#ifdef DEBUG - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_root_printer(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 i, void *arg) -{ - uint32 *leakedroots = (uint32 *)arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - - (*leakedroots)++; - fprintf(stderr, - "JS engine warning: leaking GC root \'%s\' at %p\n", - rhe->name ? (char *)rhe->name : "", rhe->root); - - return JS_DHASH_NEXT; -} - -static void -CheckLeakedRoots(JSRuntime *rt) -{ - uint32 leakedroots = 0; - - /* Warn (but don't assert) debug builds of any remaining roots. */ - JS_DHashTableEnumerate(&rt->gcRootsHash, js_root_printer, - &leakedroots); - if (leakedroots > 0) { - if (leakedroots == 1) { - fprintf(stderr, -"JS engine warning: 1 GC root remains after destroying the JSRuntime.\n" -" This root may point to freed memory. Objects reachable\n" -" through it have not been finalized.\n"); - } else { - fprintf(stderr, -"JS engine warning: %lu GC roots remain after destroying the JSRuntime.\n" -" These roots may point to freed memory. Objects reachable\n" -" through them have not been finalized.\n", - (unsigned long) leakedroots); - } - } -} - -typedef struct NamedRootDumpArgs { - void (*dump)(const char *name, void *rp, void *data); - void *data; -} NamedRootDumpArgs; - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_named_root_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - NamedRootDumpArgs *args = (NamedRootDumpArgs *) arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - - if (rhe->name) - args->dump(rhe->name, rhe->root, args->data); - return JS_DHASH_NEXT; -} - -void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data) -{ - NamedRootDumpArgs args; - - args.dump = dump; - args.data = data; - JS_DHashTableEnumerate(&rt->gcRootsHash, js_named_root_dumper, &args); -} - -#endif /* DEBUG */ - -typedef struct GCRootMapArgs { - JSGCRootMapFun map; - void *data; -} GCRootMapArgs; - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_gcroot_mapper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - GCRootMapArgs *args = (GCRootMapArgs *) arg; - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - intN mapflags; - JSDHashOperator op; - - mapflags = args->map(rhe->root, rhe->name, args->data); - -#if JS_MAP_GCROOT_NEXT == JS_DHASH_NEXT && \ - JS_MAP_GCROOT_STOP == JS_DHASH_STOP && \ - JS_MAP_GCROOT_REMOVE == JS_DHASH_REMOVE - op = (JSDHashOperator)mapflags; -#else - op = JS_DHASH_NEXT; - if (mapflags & JS_MAP_GCROOT_STOP) - op |= JS_DHASH_STOP; - if (mapflags & JS_MAP_GCROOT_REMOVE) - op |= JS_DHASH_REMOVE; -#endif - - return op; -} - -uint32 -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data) -{ - GCRootMapArgs args; - uint32 rv; - - args.map = map; - args.data = data; - JS_LOCK_GC(rt); - rv = JS_DHashTableEnumerate(&rt->gcRootsHash, js_gcroot_mapper, &args); - JS_UNLOCK_GC(rt); - return rv; -} - -JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj) -{ - JSRuntime *rt; - JSBool ok; - - rt = cx->runtime; - JS_ASSERT(!rt->gcRunning); - - JS_LOCK_GC(rt); - ok = AddToPtrTable(cx, &rt->gcIteratorTable, &iteratorTableInfo, obj); - JS_UNLOCK_GC(rt); - return ok; -} - -static void -CloseIteratorStates(JSContext *cx) -{ - JSRuntime *rt; - size_t count, newCount, i; - void **array; - JSObject *obj; - - rt = cx->runtime; - count = rt->gcIteratorTable.count; - array = rt->gcIteratorTable.array; - - newCount = 0; - for (i = 0; i != count; ++i) { - obj = (JSObject *)array[i]; - if (js_IsAboutToBeFinalized(cx, obj)) - js_CloseIteratorState(cx, obj); - else - array[newCount++] = obj; - } - ShrinkPtrTable(&rt->gcIteratorTable, &iteratorTableInfo, newCount); -} - -#if JS_HAS_GENERATORS - -void -js_RegisterGenerator(JSContext *cx, JSGenerator *gen) -{ - JSRuntime *rt; - - rt = cx->runtime; - JS_ASSERT(!rt->gcRunning); - JS_ASSERT(rt->state != JSRTS_LANDING); - JS_ASSERT(gen->state == JSGEN_NEWBORN); - - JS_LOCK_GC(rt); - gen->next = rt->gcCloseState.reachableList; - rt->gcCloseState.reachableList = gen; - METER(rt->gcStats.nclose++); - METER(rt->gcStats.maxnclose = JS_MAX(rt->gcStats.maxnclose, - rt->gcStats.nclose)); - JS_UNLOCK_GC(rt); -} - -/* - * We do not run close hooks when the parent scope of the generator instance - * becomes unreachable to prevent denial-of-service and resource leakage from - * misbehaved generators. - * - * Called from the GC. - */ -static JSBool -CanScheduleCloseHook(JSGenerator *gen) -{ - JSObject *parent; - JSBool canSchedule; - - /* Avoid OBJ_GET_PARENT overhead as we are in GC. */ - parent = JSVAL_TO_OBJECT(gen->obj->slots[JSSLOT_PARENT]); - canSchedule = *js_GetGCThingFlags(parent) & GCF_MARK; -#ifdef DEBUG_igor - if (!canSchedule) { - fprintf(stderr, "GEN: Kill without schedule, gen=%p parent=%p\n", - (void *)gen, (void *)parent); - } -#endif - return canSchedule; -} - -/* - * Check if we should delay execution of the close hook. - * - * Called outside GC or any locks. - * - * XXX The current implementation is a hack that embeds the knowledge of the - * browser embedding pending the resolution of bug 352788. In the browser we - * must not close any generators that came from a page that is currently in - * the browser history. We detect that using the fact in the browser the scope - * is history if scope->outerObject->innerObject != scope. - */ -static JSBool -ShouldDeferCloseHook(JSContext *cx, JSGenerator *gen, JSBool *defer) -{ - JSObject *parent, *obj; - JSClass *clasp; - JSExtendedClass *xclasp; - - /* - * This is called outside any locks, so use thread-safe macros to access - * parent and classes. - */ - *defer = JS_FALSE; - parent = OBJ_GET_PARENT(cx, gen->obj); - clasp = OBJ_GET_CLASS(cx, parent); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *)clasp; - if (xclasp->outerObject) { - obj = xclasp->outerObject(cx, parent); - if (!obj) - return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - *defer = obj != parent; - } - } -#ifdef DEBUG_igor - if (*defer) { - fprintf(stderr, "GEN: deferring, gen=%p parent=%p\n", - (void *)gen, (void *)parent); - } -#endif - return JS_TRUE; -} - -/* - * Find all unreachable generators and move them to the todo queue from - * rt->gcCloseState.reachableList to execute thier close hooks after the GC - * cycle completes. To ensure liveness during the sweep phase we mark all - * generators we are going to close later. - */ -static void -FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind, - JSGenerator **todoQueueTail) -{ - JSRuntime *rt; - JSGenerator *todo, **genp, *gen; - - rt = cx->runtime; - todo = NULL; - genp = &rt->gcCloseState.reachableList; - while ((gen = *genp) != NULL) { - if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) { - genp = &gen->next; - } else { - /* Generator must not be executing when it becomes unreachable. */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || - gen->state == JSGEN_OPEN || - gen->state == JSGEN_CLOSED); - - *genp = gen->next; - if (gen->state == JSGEN_OPEN && - js_FindFinallyHandler(gen->frame.script, gen->frame.pc) && - CanScheduleCloseHook(gen)) { - /* - * Generator yielded inside a try with a finally block. - * Schedule it for closing. - * - * We keep generators that yielded outside try-with-finally - * with gen->state == JSGEN_OPEN. The finalizer must deal with - * open generators as we may skip the close hooks, see below. - */ - gen->next = NULL; - *todoQueueTail = gen; - todoQueueTail = &gen->next; - if (!todo) - todo = gen; - METER(JS_ASSERT(rt->gcStats.nclose)); - METER(rt->gcStats.nclose--); - METER(rt->gcStats.closelater++); - METER(rt->gcStats.maxcloselater - = JS_MAX(rt->gcStats.maxcloselater, - rt->gcStats.closelater)); - } - } - } - - if (gckind == GC_LAST_CONTEXT) { - /* - * Remove scheduled hooks on shutdown as it is too late to run them: - * we do not allow execution of arbitrary scripts at this point. - */ - rt->gcCloseState.todoQueue = NULL; - } else { - /* - * Mark just-found unreachable generators *after* we scan the global - * list to prevent a generator that refers to other unreachable - * generators from keeping them on gcCloseState.reachableList. - */ - for (gen = todo; gen; gen = gen->next) - GC_MARK(cx, gen->obj, "newly scheduled generator"); - } -} - -/* - * Mark unreachable generators already scheduled to close and return the tail - * pointer to JSGCCloseState.todoQueue. - */ -static JSGenerator ** -MarkScheduledGenerators(JSContext *cx) -{ - JSRuntime *rt; - JSGenerator **genp, *gen; - - rt = cx->runtime; - genp = &rt->gcCloseState.todoQueue; - while ((gen = *genp) != NULL) { - if (CanScheduleCloseHook(gen)) { - GC_MARK(cx, gen->obj, "scheduled generator"); - genp = &gen->next; - } else { - /* Discard the generator from the list if its schedule is over. */ - *genp = gen->next; - METER(JS_ASSERT(rt->gcStats.closelater > 0)); - METER(rt->gcStats.closelater--); - } - } - return genp; -} - -#ifdef JS_THREADSAFE -# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ - (&(cx)->thread->gcRunningCloseHooks) -#else -# define GC_RUNNING_CLOSE_HOOKS_PTR(cx) \ - (&(cx)->runtime->gcCloseState.runningCloseHook) -#endif - -typedef struct JSTempCloseList { - JSTempValueRooter tvr; - JSGenerator *head; -} JSTempCloseList; - -JS_STATIC_DLL_CALLBACK(void) -mark_temp_close_list(JSContext *cx, JSTempValueRooter *tvr) -{ - JSTempCloseList *list = (JSTempCloseList *)tvr; - JSGenerator *gen; - - for (gen = list->head; gen; gen = gen->next) - GC_MARK(cx, gen->obj, "temp list generator"); -} - -#define JS_PUSH_TEMP_CLOSE_LIST(cx, tempList) \ - JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_close_list, &(tempList)->tvr) - -#define JS_POP_TEMP_CLOSE_LIST(cx, tempList) \ - JS_BEGIN_MACRO \ - JS_ASSERT((tempList)->tvr.u.marker == mark_temp_close_list); \ - JS_POP_TEMP_ROOT(cx, &(tempList)->tvr); \ - JS_END_MACRO - -JSBool -js_RunCloseHooks(JSContext *cx) -{ - JSRuntime *rt; - JSTempCloseList tempList; - JSStackFrame *fp; - JSGenerator **genp, *gen; - JSBool ok, defer; -#if JS_GCMETER - uint32 deferCount; -#endif - - rt = cx->runtime; - - /* - * It is OK to access todoQueue outside the lock here. When many threads - * update the todo list, accessing some older value of todoQueue in the - * worst case just delays the excution of close hooks. - */ - if (!rt->gcCloseState.todoQueue) - return JS_TRUE; - - /* - * To prevent an infinite loop when a close hook creats more objects with - * close hooks and then triggers GC we ignore recursive invocations of - * js_RunCloseHooks and limit number of hooks to execute to the initial - * size of the list. - */ - if (*GC_RUNNING_CLOSE_HOOKS_PTR(cx)) - return JS_TRUE; - - *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_TRUE; - - JS_LOCK_GC(rt); - tempList.head = rt->gcCloseState.todoQueue; - JS_PUSH_TEMP_CLOSE_LIST(cx, &tempList); - rt->gcCloseState.todoQueue = NULL; - METER(rt->gcStats.closelater = 0); - rt->gcPoke = JS_TRUE; - JS_UNLOCK_GC(rt); - - /* - * Set aside cx->fp since we do not want a close hook using caller or - * other means to backtrace into whatever stack might be active when - * running the hook. We store the current frame on the dormant list to - * protect against GC that the hook can trigger. - */ - fp = cx->fp; - if (fp) { - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - } - cx->fp = NULL; - - genp = &tempList.head; - ok = JS_TRUE; - while ((gen = *genp) != NULL) { - ok = ShouldDeferCloseHook(cx, gen, &defer); - if (!ok) { - /* Quit ASAP discarding the hook. */ - *genp = gen->next; - break; - } - if (defer) { - genp = &gen->next; - METER(deferCount++); - continue; - } - ok = js_CloseGeneratorObject(cx, gen); - - /* - * Unlink the generator after closing it to make sure it always stays - * rooted through tempList. - */ - *genp = gen->next; - - if (cx->throwing) { - /* - * Report the exception thrown by the close hook and continue to - * execute the rest of the hooks. - */ - if (!js_ReportUncaughtException(cx)) - JS_ClearPendingException(cx); - ok = JS_TRUE; - } else if (!ok) { - /* - * Assume this is a stop signal from the branch callback or - * other quit ASAP condition. Break execution until the next - * invocation of js_RunCloseHooks. - */ - break; - } - } - - cx->fp = fp; - if (fp) { - JS_ASSERT(cx->dormantFrameChain == fp); - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; - } - - if (tempList.head) { - /* - * Some close hooks were not yet executed, put them back into the - * scheduled list. - */ - while ((gen = *genp) != NULL) { - genp = &gen->next; - METER(deferCount++); - } - - /* Now genp is a pointer to the tail of tempList. */ - JS_LOCK_GC(rt); - *genp = rt->gcCloseState.todoQueue; - rt->gcCloseState.todoQueue = tempList.head; - METER(rt->gcStats.closelater += deferCount); - METER(rt->gcStats.maxcloselater - = JS_MAX(rt->gcStats.maxcloselater, rt->gcStats.closelater)); - JS_UNLOCK_GC(rt); - } - - JS_POP_TEMP_CLOSE_LIST(cx, &tempList); - *GC_RUNNING_CLOSE_HOOKS_PTR(cx) = JS_FALSE; - - return ok; -} - -#endif /* JS_HAS_GENERATORS */ - -#if defined(DEBUG_brendan) || defined(DEBUG_timeless) -#define DEBUG_gchist -#endif - -#ifdef DEBUG_gchist -#define NGCHIST 64 - -static struct GCHist { - JSBool lastDitch; - JSGCThing *freeList; -} gchist[NGCHIST]; - -unsigned gchpos; -#endif - -void * -js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes) -{ - JSRuntime *rt; - uintN flindex; - JSBool doGC; - JSGCThing *thing; - uint8 *flagp, *firstPage; - JSGCArenaList *arenaList; - jsuword offset; - JSGCArena *a; - JSLocalRootStack *lrs; -#ifdef JS_THREADSAFE - JSBool gcLocked; - uintN localMallocBytes; - JSGCThing **flbase, **lastptr; - JSGCThing *tmpthing; - uint8 *tmpflagp; - uintN maxFreeThings; /* max to take from the global free list */ - METER(size_t nfree); -#endif - - rt = cx->runtime; - METER(rt->gcStats.alloc++); /* this is not thread-safe */ - nbytes = JS_ROUNDUP(nbytes, sizeof(JSGCThing)); - flindex = GC_FREELIST_INDEX(nbytes); - -#ifdef JS_THREADSAFE - gcLocked = JS_FALSE; - JS_ASSERT(cx->thread); - flbase = cx->thread->gcFreeLists; - JS_ASSERT(flbase); - thing = flbase[flindex]; - localMallocBytes = cx->thread->gcMallocBytes; - if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) { - flagp = thing->flagp; - flbase[flindex] = thing->next; - METER(rt->gcStats.localalloc++); /* this is not thread-safe */ - goto success; - } - - JS_LOCK_GC(rt); - gcLocked = JS_TRUE; - - /* Transfer thread-local counter to global one. */ - if (localMallocBytes != 0) { - cx->thread->gcMallocBytes = 0; - if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes) - rt->gcMallocBytes = rt->gcMaxMallocBytes; - else - rt->gcMallocBytes += localMallocBytes; - } -#endif - JS_ASSERT(!rt->gcRunning); - if (rt->gcRunning) { - METER(rt->gcStats.finalfail++); - JS_UNLOCK_GC(rt); - return NULL; - } - -#ifdef TOO_MUCH_GC -#ifdef WAY_TOO_MUCH_GC - rt->gcPoke = JS_TRUE; -#endif - doGC = JS_TRUE; -#else - doGC = (rt->gcMallocBytes >= rt->gcMaxMallocBytes); -#endif - - arenaList = &rt->gcArenaList[flindex]; - for (;;) { - if (doGC) { - /* - * Keep rt->gcLock across the call into js_GC so we don't starve - * and lose to racing threads who deplete the heap just after - * js_GC has replenished it (or has synchronized with a racing - * GC that collected a bunch of garbage). This unfair scheduling - * can happen on certain operating systems. For the gory details, - * see bug 162779 at https://bugzilla.mozilla.org/. - */ - js_GC(cx, GC_LAST_DITCH); - METER(rt->gcStats.retry++); - } - - /* Try to get thing from the free list. */ - thing = arenaList->freeList; - if (thing) { - arenaList->freeList = thing->next; - flagp = thing->flagp; - JS_ASSERT(*flagp & GCF_FINAL); - METER(arenaList->stats.freelen--); - METER(arenaList->stats.recycle++); - -#ifdef JS_THREADSAFE - /* - * Refill the local free list by taking several things from the - * global free list unless we are still at rt->gcMaxMallocBytes - * barrier or the free list is already populated. The former - * happens when GC is canceled due to !gcCallback(cx, JSGC_BEGIN) - * or no gcPoke. The latter is caused via allocating new things - * in gcCallback(cx, JSGC_END). - */ - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) - break; - tmpthing = arenaList->freeList; - if (tmpthing) { - maxFreeThings = MAX_THREAD_LOCAL_THINGS; - do { - if (!tmpthing->next) - break; - tmpthing = tmpthing->next; - } while (--maxFreeThings != 0); - - flbase[flindex] = arenaList->freeList; - arenaList->freeList = tmpthing->next; - tmpthing->next = NULL; - } -#endif - break; - } - - /* Allocate from the tail of last arena or from new arena if we can. */ - if ((arenaList->last && arenaList->lastLimit != GC_THINGS_SIZE) || - NewGCArena(rt, arenaList)) { - - offset = arenaList->lastLimit; - if ((offset & GC_PAGE_MASK) == 0) { - /* - * Skip JSGCPageInfo record located at GC_PAGE_SIZE boundary. - */ - offset += PAGE_THING_GAP(nbytes); - } - JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); - arenaList->lastLimit = (uint16)(offset + nbytes); - a = arenaList->last; - firstPage = (uint8 *)FIRST_THING_PAGE(a); - thing = (JSGCThing *)(firstPage + offset); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - METER(++arenaList->stats.nthings); - METER(arenaList->stats.maxthings = - JS_MAX(arenaList->stats.nthings, - arenaList->stats.maxthings)); - -#ifdef JS_THREADSAFE - /* - * Refill the local free list by taking free things from the last - * arena. Prefer to order free things by ascending address in the - * (unscientific) hope of better cache locality. - */ - if (rt->gcMallocBytes >= rt->gcMaxMallocBytes || flbase[flindex]) - break; - METER(nfree = 0); - lastptr = &flbase[flindex]; - maxFreeThings = MAX_THREAD_LOCAL_THINGS; - for (offset = arenaList->lastLimit; - offset != GC_THINGS_SIZE && maxFreeThings-- != 0; - offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) - offset += PAGE_THING_GAP(nbytes); - JS_ASSERT(offset + nbytes <= GC_THINGS_SIZE); - tmpflagp = a->base + offset / sizeof(JSGCThing); - if (tmpflagp >= firstPage) - tmpflagp += GC_THINGS_SIZE; - - tmpthing = (JSGCThing *)(firstPage + offset); - tmpthing->flagp = tmpflagp; - *tmpflagp = GCF_FINAL; /* signifying that thing is free */ - - *lastptr = tmpthing; - lastptr = &tmpthing->next; - METER(++nfree); - } - arenaList->lastLimit = offset; - *lastptr = NULL; - METER(arenaList->stats.freelen += nfree); -#endif - break; - } - - /* Consider doing a "last ditch" GC unless already tried. */ - if (doGC) - goto fail; - rt->gcPoke = JS_TRUE; - doGC = JS_TRUE; - } - - /* We successfully allocated the thing. */ -#ifdef JS_THREADSAFE - success: -#endif - lrs = cx->localRootStack; - if (lrs) { - /* - * If we're in a local root scope, don't set newborn[type] at all, to - * avoid entraining garbage from it for an unbounded amount of time - * on this context. A caller will leave the local root scope and pop - * this reference, allowing thing to be GC'd if it has no other refs. - * See JS_EnterLocalRootScope and related APIs. - */ - if (js_PushLocalRoot(cx, lrs, (jsval) thing) < 0) { - /* - * When we fail for a thing allocated through the tail of the last - * arena, thing's flag byte is not initialized. So to prevent GC - * accessing the uninitialized flags during the finalization, we - * always mark the thing as final. See bug 337407. - */ - *flagp = GCF_FINAL; - goto fail; - } - } else { - /* - * No local root scope, so we're stuck with the old, fragile model of - * depending on a pigeon-hole newborn per type per context. - */ - cx->weakRoots.newborn[flags & GCF_TYPEMASK] = thing; - } - - /* We can't fail now, so update flags and rt->gc{,Private}Bytes. */ - *flagp = (uint8)flags; - - /* - * Clear thing before unlocking in case a GC run is about to scan it, - * finding it via newborn[]. - */ - thing->next = NULL; - thing->flagp = NULL; -#ifdef DEBUG_gchist - gchist[gchpos].lastDitch = doGC; - gchist[gchpos].freeList = rt->gcArenaList[flindex].freeList; - if (++gchpos == NGCHIST) - gchpos = 0; -#endif - METER(if (flags & GCF_LOCK) rt->gcStats.lockborn++); - METER(++rt->gcArenaList[flindex].stats.totalnew); -#ifdef JS_THREADSAFE - if (gcLocked) - JS_UNLOCK_GC(rt); -#endif - return thing; - -fail: -#ifdef JS_THREADSAFE - if (gcLocked) - JS_UNLOCK_GC(rt); -#endif - METER(rt->gcStats.fail++); - JS_ReportOutOfMemory(cx); - return NULL; -} - -JSBool -js_LockGCThing(JSContext *cx, void *thing) -{ - JSBool ok = js_LockGCThingRT(cx->runtime, thing); - if (!ok) - JS_ReportOutOfMemory(cx); - return ok; -} - -/* - * Deep GC-things can't be locked just by setting the GCF_LOCK bit, because - * their descendants must be marked by the GC. To find them during the mark - * phase, they are added to rt->gcLocksHash, which is created lazily. - * - * NB: we depend on the order of GC-thing type indexes here! - */ -#define GC_TYPE_IS_STRING(t) ((t) == GCX_STRING || \ - (t) >= GCX_EXTERNAL_STRING) -#define GC_TYPE_IS_XML(t) ((unsigned)((t) - GCX_NAMESPACE) <= \ - (unsigned)(GCX_XML - GCX_NAMESPACE)) -#define GC_TYPE_IS_DEEP(t) ((t) == GCX_OBJECT || GC_TYPE_IS_XML(t)) - -#define IS_DEEP_STRING(t,o) (GC_TYPE_IS_STRING(t) && \ - JSSTRING_IS_DEPENDENT((JSString *)(o))) - -#define GC_THING_IS_DEEP(t,o) (GC_TYPE_IS_DEEP(t) || IS_DEEP_STRING(t, o)) - -/* This is compatible with JSDHashEntryStub. */ -typedef struct JSGCLockHashEntry { - JSDHashEntryHdr hdr; - const JSGCThing *thing; - uint32 count; -} JSGCLockHashEntry; - -JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing) -{ - JSBool ok, deep; - uint8 *flagp; - uintN flags, lock, type; - JSGCLockHashEntry *lhe; - - ok = JS_TRUE; - if (!thing) - return ok; - - flagp = js_GetGCThingFlags(thing); - - JS_LOCK_GC(rt); - flags = *flagp; - lock = (flags & GCF_LOCK); - type = (flags & GCF_TYPEMASK); - deep = GC_THING_IS_DEEP(type, thing); - - /* - * Avoid adding a rt->gcLocksHash entry for shallow things until someone - * nests a lock -- then start such an entry with a count of 2, not 1. - */ - if (lock || deep) { - if (!rt->gcLocksHash) { - rt->gcLocksHash = - JS_NewDHashTable(JS_DHashGetStubOps(), NULL, - sizeof(JSGCLockHashEntry), - GC_ROOTS_SIZE); - if (!rt->gcLocksHash) { - ok = JS_FALSE; - goto done; - } - } else if (lock == 0) { -#ifdef DEBUG - JSDHashEntryHdr *hdr = - JS_DHashTableOperate(rt->gcLocksHash, thing, - JS_DHASH_LOOKUP); - JS_ASSERT(JS_DHASH_ENTRY_IS_FREE(hdr)); -#endif - } - - lhe = (JSGCLockHashEntry *) - JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_ADD); - if (!lhe) { - ok = JS_FALSE; - goto done; - } - if (!lhe->thing) { - lhe->thing = thing; - lhe->count = deep ? 1 : 2; - } else { - JS_ASSERT(lhe->count >= 1); - lhe->count++; - } - } - - *flagp = (uint8)(flags | GCF_LOCK); - METER(rt->gcStats.lock++); - ok = JS_TRUE; -done: - JS_UNLOCK_GC(rt); - return ok; -} - -JSBool -js_UnlockGCThingRT(JSRuntime *rt, void *thing) -{ - uint8 *flagp, flags; - JSGCLockHashEntry *lhe; - - if (!thing) - return JS_TRUE; - - flagp = js_GetGCThingFlags(thing); - JS_LOCK_GC(rt); - flags = *flagp; - - if (flags & GCF_LOCK) { - if (!rt->gcLocksHash || - (lhe = (JSGCLockHashEntry *) - JS_DHashTableOperate(rt->gcLocksHash, thing, - JS_DHASH_LOOKUP), - JS_DHASH_ENTRY_IS_FREE(&lhe->hdr))) { - /* Shallow GC-thing with an implicit lock count of 1. */ - JS_ASSERT(!GC_THING_IS_DEEP(flags & GCF_TYPEMASK, thing)); - } else { - /* Basis or nested unlock of a deep thing, or nested of shallow. */ - if (--lhe->count != 0) - goto out; - JS_DHashTableOperate(rt->gcLocksHash, thing, JS_DHASH_REMOVE); - } - *flagp = (uint8)(flags & ~GCF_LOCK); - } - - rt->gcPoke = JS_TRUE; -out: - METER(rt->gcStats.unlock++); - JS_UNLOCK_GC(rt); - return JS_TRUE; -} - -#ifdef GC_MARK_DEBUG - -#include -#include "jsprf.h" - -typedef struct GCMarkNode GCMarkNode; - -struct GCMarkNode { - void *thing; - const char *name; - GCMarkNode *next; - GCMarkNode *prev; -}; - -JS_FRIEND_DATA(FILE *) js_DumpGCHeap; -JS_EXPORT_DATA(void *) js_LiveThingToFind; - -#ifdef HAVE_XPCONNECT -#include "dump_xpc.h" -#endif - -static void -GetObjSlotName(JSScope *scope, JSObject *obj, uint32 slot, char *buf, - size_t bufsize) -{ - jsval nval; - JSScopeProperty *sprop; - JSClass *clasp; - uint32 key; - const char *slotname; - - if (!scope) { - JS_snprintf(buf, bufsize, "**UNKNOWN OBJECT MAP ENTRY**"); - return; - } - - sprop = SCOPE_LAST_PROP(scope); - while (sprop && sprop->slot != slot) - sprop = sprop->parent; - - if (!sprop) { - switch (slot) { - case JSSLOT_PROTO: - JS_snprintf(buf, bufsize, "__proto__"); - break; - case JSSLOT_PARENT: - JS_snprintf(buf, bufsize, "__parent__"); - break; - default: - slotname = NULL; - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (clasp->flags & JSCLASS_IS_GLOBAL) { - key = slot - JSSLOT_START(clasp); -#define JS_PROTO(name,code,init) \ - if ((code) == key) { slotname = js_##name##_str; goto found; } -#include "jsproto.tbl" -#undef JS_PROTO - } - found: - if (slotname) - JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname); - else - JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot); - break; - } - } else { - nval = ID_TO_VALUE(sprop->id); - if (JSVAL_IS_INT(nval)) { - JS_snprintf(buf, bufsize, "%ld", (long)JSVAL_TO_INT(nval)); - } else if (JSVAL_IS_STRING(nval)) { - JS_snprintf(buf, bufsize, "%s", - JS_GetStringBytes(JSVAL_TO_STRING(nval))); - } else { - JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**"); - } - } -} - -static const char * -gc_object_class_name(void* thing) -{ - uint8 *flagp = js_GetGCThingFlags(thing); - const char *className = ""; - static char depbuf[32]; - - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: { - JSObject *obj = (JSObject *)thing; - JSClass *clasp = JSVAL_TO_PRIVATE(obj->slots[JSSLOT_CLASS]); - className = clasp->name; -#ifdef HAVE_XPCONNECT - if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { - jsval privateValue = obj->slots[JSSLOT_PRIVATE]; - - JS_ASSERT(clasp->flags & JSCLASS_HAS_PRIVATE); - if (!JSVAL_IS_VOID(privateValue)) { - void *privateThing = JSVAL_TO_PRIVATE(privateValue); - const char *xpcClassName = GetXPCObjectClassName(privateThing); - - if (xpcClassName) - className = xpcClassName; - } - } -#endif - break; - } - - case GCX_STRING: - case GCX_MUTABLE_STRING: { - JSString *str = (JSString *)thing; - if (JSSTRING_IS_DEPENDENT(str)) { - JS_snprintf(depbuf, sizeof depbuf, "start:%u, length:%u", - JSSTRDEP_START(str), JSSTRDEP_LENGTH(str)); - className = depbuf; - } else { - className = "string"; - } - break; - } - - case GCX_DOUBLE: - className = "double"; - break; - } - - return className; -} - -static void -gc_dump_thing(JSContext *cx, JSGCThing *thing, FILE *fp) -{ - GCMarkNode *prev = (GCMarkNode *)cx->gcCurrentMarkNode; - GCMarkNode *next = NULL; - char *path = NULL; - - while (prev) { - next = prev; - prev = prev->prev; - } - while (next) { - uint8 nextFlags = *js_GetGCThingFlags(next->thing); - if ((nextFlags & GCF_TYPEMASK) == GCX_OBJECT) { - path = JS_sprintf_append(path, "%s(%s @ 0x%08p).", - next->name, - gc_object_class_name(next->thing), - (JSObject*)next->thing); - } else { - path = JS_sprintf_append(path, "%s(%s).", - next->name, - gc_object_class_name(next->thing)); - } - next = next->next; - } - if (!path) - return; - - fprintf(fp, "%08lx ", (long)thing); - switch (*js_GetGCThingFlags(thing) & GCF_TYPEMASK) { - case GCX_OBJECT: - { - JSObject *obj = (JSObject *)thing; - jsval privateValue = obj->slots[JSSLOT_PRIVATE]; - void *privateThing = JSVAL_IS_VOID(privateValue) - ? NULL - : JSVAL_TO_PRIVATE(privateValue); - const char *className = gc_object_class_name(thing); - fprintf(fp, "object %8p %s", privateThing, className); - break; - } -#if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - { - JSXMLNamespace *ns = (JSXMLNamespace *)thing; - fprintf(fp, "namespace %s:%s", - JS_GetStringBytes(ns->prefix), JS_GetStringBytes(ns->uri)); - break; - } - case GCX_QNAME: - { - JSXMLQName *qn = (JSXMLQName *)thing; - fprintf(fp, "qname %s(%s):%s", - JS_GetStringBytes(qn->prefix), JS_GetStringBytes(qn->uri), - JS_GetStringBytes(qn->localName)); - break; - } - case GCX_XML: - { - extern const char *js_xml_class_str[]; - JSXML *xml = (JSXML *)thing; - fprintf(fp, "xml %8p %s", xml, js_xml_class_str[xml->xml_class]); - break; - } -#endif - case GCX_DOUBLE: - fprintf(fp, "double %g", *(jsdouble *)thing); - break; - case GCX_PRIVATE: - fprintf(fp, "private %8p", (void *)thing); - break; - default: - fprintf(fp, "string %s", JS_GetStringBytes((JSString *)thing)); - break; - } - fprintf(fp, " via %s\n", path); - free(path); -} - -void -js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name) -{ - GCMarkNode markNode; - - if (!thing) - return; - - markNode.thing = thing; - markNode.name = name; - markNode.next = NULL; - markNode.prev = (GCMarkNode *)cx->gcCurrentMarkNode; - if (markNode.prev) - markNode.prev->next = &markNode; - cx->gcCurrentMarkNode = &markNode; - - if (thing == js_LiveThingToFind) { - /* - * Dump js_LiveThingToFind each time we reach it during the marking - * phase of GC to print all live references to the thing. - */ - gc_dump_thing(cx, thing, stderr); - } - - js_MarkGCThing(cx, thing); - - if (markNode.prev) - markNode.prev->next = NULL; - cx->gcCurrentMarkNode = markNode.prev; -} - -#endif /* !GC_MARK_DEBUG */ - -static void -gc_mark_atom_key_thing(void *thing, void *arg) -{ - JSContext *cx = (JSContext *) arg; - - GC_MARK(cx, thing, "atom"); -} - -void -js_MarkAtom(JSContext *cx, JSAtom *atom) -{ - jsval key; - - if (atom->flags & ATOM_MARK) - return; - atom->flags |= ATOM_MARK; - key = ATOM_KEY(atom); - if (JSVAL_IS_GCTHING(key)) { -#ifdef GC_MARK_DEBUG - char name[32]; - - if (JSVAL_IS_STRING(key)) { - JS_snprintf(name, sizeof name, "'%s'", - JS_GetStringBytes(JSVAL_TO_STRING(key))); - } else { - JS_snprintf(name, sizeof name, "<%x>", key); - } -#endif - GC_MARK(cx, JSVAL_TO_GCTHING(key), name); - } - if (atom->flags & ATOM_HIDDEN) - js_MarkAtom(cx, atom->entry.value); -} - -static void -AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp); - -static void -MarkGCThingChildren(JSContext *cx, void *thing, uint8 *flagp, - JSBool shouldCheckRecursion) -{ - JSRuntime *rt; - JSObject *obj; - jsval v, *vp, *end; - void *next_thing; - uint8 *next_flagp; - JSString *str; -#ifdef JS_GCMETER - uint32 tailCallNesting; -#endif -#ifdef GC_MARK_DEBUG - JSScope *scope; - char name[32]; -#endif - - /* - * With JS_GC_ASSUME_LOW_C_STACK defined the mark phase of GC always - * uses the non-recursive code that otherwise would be called only on - * a low C stack condition. - */ -#ifdef JS_GC_ASSUME_LOW_C_STACK -# define RECURSION_TOO_DEEP() shouldCheckRecursion -#else - int stackDummy; -# define RECURSION_TOO_DEEP() (shouldCheckRecursion && \ - !JS_CHECK_STACK_SIZE(cx, stackDummy)) -#endif - - rt = cx->runtime; - METER(tailCallNesting = 0); - METER(if (++rt->gcStats.cdepth > rt->gcStats.maxcdepth) - rt->gcStats.maxcdepth = rt->gcStats.cdepth); - -#ifndef GC_MARK_DEBUG - start: -#endif - JS_ASSERT(flagp); - JS_ASSERT(*flagp & GCF_MARK); /* the caller must already mark the thing */ - METER(if (++rt->gcStats.depth > rt->gcStats.maxdepth) - rt->gcStats.maxdepth = rt->gcStats.depth); -#ifdef GC_MARK_DEBUG - if (js_DumpGCHeap) - gc_dump_thing(cx, thing, js_DumpGCHeap); -#endif - - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - /* If obj->slots is null, obj must be a newborn. */ - obj = (JSObject *) thing; - vp = obj->slots; - if (!vp) - break; - - /* Mark slots if they are small enough to be GC-allocated. */ - if ((vp[-1] + 1) * sizeof(jsval) <= GC_NBYTES_MAX) - GC_MARK(cx, vp - 1, "slots"); - - /* Set up local variables to loop over unmarked things. */ - end = vp + ((obj->map->ops->mark) - ? obj->map->ops->mark(cx, obj, NULL) - : JS_MIN(obj->map->freeslot, obj->map->nslots)); - thing = NULL; - flagp = NULL; -#ifdef GC_MARK_DEBUG - scope = OBJ_IS_NATIVE(obj) ? OBJ_SCOPE(obj) : NULL; -#endif - for (; vp != end; ++vp) { - v = *vp; - if (!JSVAL_IS_GCTHING(v) || v == JSVAL_NULL) - continue; - next_thing = JSVAL_TO_GCTHING(v); - if (next_thing == thing) - continue; - next_flagp = js_GetGCThingFlags(next_thing); - if (*next_flagp & GCF_MARK) - continue; - JS_ASSERT(*next_flagp != GCF_FINAL); - if (thing) { -#ifdef GC_MARK_DEBUG - GC_MARK(cx, thing, name); -#else - *flagp |= GCF_MARK; - MarkGCThingChildren(cx, thing, flagp, JS_TRUE); -#endif - if (*next_flagp & GCF_MARK) { - /* - * This happens when recursive MarkGCThingChildren marks - * the thing with flags referred by *next_flagp. - */ - thing = NULL; - continue; - } - } -#ifdef GC_MARK_DEBUG - GetObjSlotName(scope, obj, vp - obj->slots, name, sizeof name); -#endif - thing = next_thing; - flagp = next_flagp; - } - if (thing) { - /* - * thing came from the last unmarked GC-thing slot and we - * can optimize tail recursion. - * - * Since we already know that there is enough C stack space, - * we clear shouldCheckRecursion to avoid extra checking in - * RECURSION_TOO_DEEP. - */ - shouldCheckRecursion = JS_FALSE; - goto on_tail_recursion; - } - break; - -#ifdef DEBUG - case GCX_STRING: - str = (JSString *)thing; - JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); - break; -#endif - - case GCX_MUTABLE_STRING: - str = (JSString *)thing; - if (!JSSTRING_IS_DEPENDENT(str)) - break; - thing = JSSTRDEP_BASE(str); - flagp = js_GetGCThingFlags(thing); - if (*flagp & GCF_MARK) - break; -#ifdef GC_MARK_DEBUG - strcpy(name, "base"); -#endif - /* Fallthrough to code to deal with the tail recursion. */ - - on_tail_recursion: -#ifdef GC_MARK_DEBUG - /* - * Do not eliminate C recursion when debugging to allow - * js_MarkNamedGCThing to build a full dump of live GC - * things. - */ - GC_MARK(cx, thing, name); - break; -#else - /* Eliminate tail recursion for the last unmarked child. */ - JS_ASSERT(*flagp != GCF_FINAL); - METER(++tailCallNesting); - *flagp |= GCF_MARK; - goto start; -#endif - -#if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXMLNamespace(cx, (JSXMLNamespace *)thing); - break; - - case GCX_QNAME: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXMLQName(cx, (JSXMLQName *)thing); - break; - - case GCX_XML: - if (RECURSION_TOO_DEEP()) - goto add_to_unscanned_bag; - js_MarkXML(cx, (JSXML *)thing); - break; -#endif - add_to_unscanned_bag: - AddThingToUnscannedBag(cx->runtime, thing, flagp); - break; - } - -#undef RECURSION_TOO_DEEP - - METER(rt->gcStats.depth -= 1 + tailCallNesting); - METER(rt->gcStats.cdepth--); -} - -/* - * Avoid using PAGE_THING_GAP inside this macro to optimize the - * thingsPerUnscannedChunk calculation when thingSize is a power of two. - */ -#define GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap) \ - JS_BEGIN_MACRO \ - if (0 == ((thingSize) & ((thingSize) - 1))) { \ - pageGap = (thingSize); \ - thingsPerUnscannedChunk = ((GC_PAGE_SIZE / (thingSize)) \ - + JS_BITS_PER_WORD - 1) \ - >> JS_BITS_PER_WORD_LOG2; \ - } else { \ - pageGap = GC_PAGE_SIZE % (thingSize); \ - thingsPerUnscannedChunk = JS_HOWMANY(GC_PAGE_SIZE / (thingSize), \ - JS_BITS_PER_WORD); \ - } \ - JS_END_MACRO - -static void -AddThingToUnscannedBag(JSRuntime *rt, void *thing, uint8 *flagp) -{ - JSGCPageInfo *pi; - JSGCArena *arena; - size_t thingSize; - size_t thingsPerUnscannedChunk; - size_t pageGap; - size_t chunkIndex; - jsuword bit; - - /* Things from delayed scanning bag are marked as GCF_MARK | GCF_FINAL. */ - JS_ASSERT((*flagp & (GCF_MARK | GCF_FINAL)) == GCF_MARK); - *flagp |= GCF_FINAL; - - METER(rt->gcStats.unscanned++); -#ifdef DEBUG - ++rt->gcUnscannedBagSize; - METER(if (rt->gcUnscannedBagSize > rt->gcStats.maxunscanned) - rt->gcStats.maxunscanned = rt->gcUnscannedBagSize); -#endif - - pi = THING_TO_PAGE(thing); - arena = PAGE_TO_ARENA(pi); - thingSize = arena->list->thingSize; - GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); - chunkIndex = (((jsuword)thing & GC_PAGE_MASK) - pageGap) / - (thingSize * thingsPerUnscannedChunk); - JS_ASSERT(chunkIndex < JS_BITS_PER_WORD); - bit = (jsuword)1 << chunkIndex; - if (pi->unscannedBitmap != 0) { - JS_ASSERT(rt->gcUnscannedArenaStackTop); - if (thingsPerUnscannedChunk != 1) { - if (pi->unscannedBitmap & bit) { - /* Chunk already contains things to scan later. */ - return; - } - } else { - /* - * The chunk must not contain things to scan later if there is - * only one thing per chunk. - */ - JS_ASSERT(!(pi->unscannedBitmap & bit)); - } - pi->unscannedBitmap |= bit; - JS_ASSERT(arena->unscannedPages & ((size_t)1 << PAGE_INDEX(pi))); - } else { - /* - * The thing is the first unscanned thing in the page, set the bit - * corresponding to this page arena->unscannedPages. - */ - pi->unscannedBitmap = bit; - JS_ASSERT(PAGE_INDEX(pi) < JS_BITS_PER_WORD); - bit = (jsuword)1 << PAGE_INDEX(pi); - JS_ASSERT(!(arena->unscannedPages & bit)); - if (arena->unscannedPages != 0) { - arena->unscannedPages |= bit; - JS_ASSERT(arena->prevUnscanned); - JS_ASSERT(rt->gcUnscannedArenaStackTop); - } else { - /* - * The thing is the first unscanned thing in the whole arena, push - * the arena on the stack of unscanned arenas unless the arena - * has already been pushed. We detect that through prevUnscanned - * field which is NULL only for not yet pushed arenas. To ensure - * that prevUnscanned != NULL even when the stack contains one - * element, we make prevUnscanned for the arena at the bottom - * to point to itself. - * - * See comments in ScanDelayedChildren. - */ - arena->unscannedPages = bit; - if (!arena->prevUnscanned) { - if (!rt->gcUnscannedArenaStackTop) { - /* Stack was empty, mark the arena as bottom element. */ - arena->prevUnscanned = arena; - } else { - JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); - arena->prevUnscanned = rt->gcUnscannedArenaStackTop; - } - rt->gcUnscannedArenaStackTop = arena; - } - } - } - JS_ASSERT(rt->gcUnscannedArenaStackTop); -} - -static void -ScanDelayedChildren(JSContext *cx) -{ - JSRuntime *rt; - JSGCArena *arena; - size_t thingSize; - size_t thingsPerUnscannedChunk; - size_t pageGap; - size_t pageIndex; - JSGCPageInfo *pi; - size_t chunkIndex; - size_t thingOffset, thingLimit; - JSGCThing *thing; - uint8 *flagp; - JSGCArena *prevArena; - - rt = cx->runtime; - arena = rt->gcUnscannedArenaStackTop; - if (!arena) { - JS_ASSERT(rt->gcUnscannedBagSize == 0); - return; - } - - init_size: - thingSize = arena->list->thingSize; - GET_GAP_AND_CHUNK_SPAN(thingSize, thingsPerUnscannedChunk, pageGap); - for (;;) { - /* - * The following assert verifies that the current arena belongs to - * the unscan stack since AddThingToUnscannedBag ensures that even - * for stack's bottom prevUnscanned != NULL but rather points to self. - */ - JS_ASSERT(arena->prevUnscanned); - JS_ASSERT(rt->gcUnscannedArenaStackTop->prevUnscanned); - while (arena->unscannedPages != 0) { - pageIndex = JS_FLOOR_LOG2W(arena->unscannedPages); - JS_ASSERT(pageIndex < GC_PAGE_COUNT); - pi = (JSGCPageInfo *)(FIRST_THING_PAGE(arena) + - pageIndex * GC_PAGE_SIZE); - JS_ASSERT(pi->unscannedBitmap); - chunkIndex = JS_FLOOR_LOG2W(pi->unscannedBitmap); - pi->unscannedBitmap &= ~((jsuword)1 << chunkIndex); - if (pi->unscannedBitmap == 0) - arena->unscannedPages &= ~((jsuword)1 << pageIndex); - thingOffset = (pageGap - + chunkIndex * thingsPerUnscannedChunk * thingSize); - JS_ASSERT(thingOffset >= sizeof(JSGCPageInfo)); - thingLimit = thingOffset + thingsPerUnscannedChunk * thingSize; - if (thingsPerUnscannedChunk != 1) { - /* - * thingLimit can go beyond the last allocated thing for the - * last chunk as the real limit can be inside the chunk. - */ - if (arena->list->last == arena && - arena->list->lastLimit < (pageIndex * GC_PAGE_SIZE + - thingLimit)) { - thingLimit = (arena->list->lastLimit - - pageIndex * GC_PAGE_SIZE); - } else if (thingLimit > GC_PAGE_SIZE) { - thingLimit = GC_PAGE_SIZE; - } - JS_ASSERT(thingLimit > thingOffset); - } - JS_ASSERT(arena->list->last != arena || - arena->list->lastLimit >= (pageIndex * GC_PAGE_SIZE + - thingLimit)); - JS_ASSERT(thingLimit <= GC_PAGE_SIZE); - - for (; thingOffset != thingLimit; thingOffset += thingSize) { - /* - * XXX: inline js_GetGCThingFlags() to use already available - * pi. - */ - thing = (void *)((jsuword)pi + thingOffset); - flagp = js_GetGCThingFlags(thing); - if (thingsPerUnscannedChunk != 1) { - /* - * Skip free or already scanned things that share the chunk - * with unscanned ones. - */ - if ((*flagp & (GCF_MARK|GCF_FINAL)) != (GCF_MARK|GCF_FINAL)) - continue; - } - JS_ASSERT((*flagp & (GCF_MARK|GCF_FINAL)) - == (GCF_MARK|GCF_FINAL)); - *flagp &= ~GCF_FINAL; -#ifdef DEBUG - JS_ASSERT(rt->gcUnscannedBagSize != 0); - --rt->gcUnscannedBagSize; - - /* - * Check that GC thing type is consistent with the type of - * things that can be put to the unscanned bag. - */ - switch (*flagp & GCF_TYPEMASK) { - case GCX_OBJECT: -# if JS_HAS_XML_SUPPORT - case GCX_NAMESPACE: - case GCX_QNAME: - case GCX_XML: -# endif - break; - default: - JS_ASSERT(0); - } -#endif - MarkGCThingChildren(cx, thing, flagp, JS_FALSE); - } - } - /* - * We finished scanning of the arena but we can only pop it from - * the stack if the arena is the stack's top. - * - * When MarkGCThingChildren from the above calls - * AddThingToUnscannedBag and the latter pushes new arenas to the - * stack, we have to skip popping of this arena until it becomes - * the top of the stack again. - */ - if (arena == rt->gcUnscannedArenaStackTop) { - prevArena = arena->prevUnscanned; - arena->prevUnscanned = NULL; - if (arena == prevArena) { - /* - * prevUnscanned points to itself and we reached the bottom - * of the stack. - */ - break; - } - rt->gcUnscannedArenaStackTop = arena = prevArena; - } else { - arena = rt->gcUnscannedArenaStackTop; - } - if (arena->list->thingSize != thingSize) - goto init_size; - } - JS_ASSERT(rt->gcUnscannedArenaStackTop); - JS_ASSERT(!rt->gcUnscannedArenaStackTop->prevUnscanned); - rt->gcUnscannedArenaStackTop = NULL; - JS_ASSERT(rt->gcUnscannedBagSize == 0); -} - -void -js_MarkGCThing(JSContext *cx, void *thing) -{ - uint8 *flagp; - - if (!thing) - return; - - flagp = js_GetGCThingFlags(thing); - JS_ASSERT(*flagp != GCF_FINAL); - if (*flagp & GCF_MARK) - return; - *flagp |= GCF_MARK; - - if (!cx->insideGCMarkCallback) { - MarkGCThingChildren(cx, thing, flagp, JS_TRUE); - } else { - /* - * For API compatibility we allow for the callback to assume that - * after it calls js_MarkGCThing for the last time, the callback - * can start to finalize its own objects that are only referenced - * by unmarked GC things. - * - * Since we do not know which call from inside the callback is the - * last, we ensure that the unscanned bag is always empty when we - * return to the callback and all marked things are scanned. - * - * As an optimization we do not check for the stack size here and - * pass JS_FALSE as the last argument to MarkGCThingChildren. - * Otherwise with low C stack the thing would be pushed to the bag - * just to be feed to MarkGCThingChildren from inside - * ScanDelayedChildren. - */ - cx->insideGCMarkCallback = JS_FALSE; - MarkGCThingChildren(cx, thing, flagp, JS_FALSE); - ScanDelayedChildren(cx); - cx->insideGCMarkCallback = JS_TRUE; - } -} - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -gc_root_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) -{ - JSGCRootHashEntry *rhe = (JSGCRootHashEntry *)hdr; - jsval *rp = (jsval *)rhe->root; - jsval v = *rp; - - /* Ignore null object and scalar values. */ - if (!JSVAL_IS_NULL(v) && JSVAL_IS_GCTHING(v)) { - JSContext *cx = (JSContext *)arg; -#ifdef DEBUG - JSBool root_points_to_gcArenaList = JS_FALSE; - jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); - uintN i; - JSGCArenaList *arenaList; - JSGCArena *a; - size_t limit; - - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &cx->runtime->gcArenaList[i]; - limit = arenaList->lastLimit; - for (a = arenaList->last; a; a = a->prev) { - if (thing - FIRST_THING_PAGE(a) < limit) { - root_points_to_gcArenaList = JS_TRUE; - break; - } - limit = GC_THINGS_SIZE; - } - } - if (!root_points_to_gcArenaList && rhe->name) { - fprintf(stderr, -"JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" -"invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n" -"The root's name is \"%s\".\n", - rhe->name); - } - JS_ASSERT(root_points_to_gcArenaList); -#endif - - GC_MARK(cx, JSVAL_TO_GCTHING(v), rhe->name ? rhe->name : "root"); - } - return JS_DHASH_NEXT; -} - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -gc_lock_marker(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, void *arg) -{ - JSGCLockHashEntry *lhe = (JSGCLockHashEntry *)hdr; - void *thing = (void *)lhe->thing; - JSContext *cx = (JSContext *)arg; - - GC_MARK(cx, thing, "locked object"); - return JS_DHASH_NEXT; -} - -#define GC_MARK_JSVALS(cx, len, vec, name) \ - JS_BEGIN_MACRO \ - jsval _v, *_vp, *_end; \ - \ - for (_vp = vec, _end = _vp + len; _vp < _end; _vp++) { \ - _v = *_vp; \ - if (JSVAL_IS_GCTHING(_v)) \ - GC_MARK(cx, JSVAL_TO_GCTHING(_v), name); \ - } \ - JS_END_MACRO - -void -js_MarkStackFrame(JSContext *cx, JSStackFrame *fp) -{ - uintN depth, nslots; - - if (fp->callobj) - GC_MARK(cx, fp->callobj, "call object"); - if (fp->argsobj) - GC_MARK(cx, fp->argsobj, "arguments object"); - if (fp->varobj) - GC_MARK(cx, fp->varobj, "variables object"); - if (fp->script) { - js_MarkScript(cx, fp->script); - if (fp->spbase) { - /* - * Don't mark what has not been pushed yet, or what has been - * popped already. - */ - depth = fp->script->depth; - nslots = (JS_UPTRDIFF(fp->sp, fp->spbase) - < depth * sizeof(jsval)) - ? (uintN)(fp->sp - fp->spbase) - : depth; - GC_MARK_JSVALS(cx, nslots, fp->spbase, "operand"); - } - } - - /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */ - JS_ASSERT(JSVAL_IS_OBJECT((jsval)fp->thisp) || - (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags))); - if (JSVAL_IS_GCTHING((jsval)fp->thisp)) - GC_MARK(cx, JSVAL_TO_GCTHING((jsval)fp->thisp), "this"); - - /* - * Mark fp->argv, even though in the common case it will be marked via our - * caller's frame, or via a JSStackHeader if fp was pushed by an external - * invocation. - * - * The hard case is when there is not enough contiguous space in the stack - * arena for actual, missing formal, and local root (JSFunctionSpec.extra) - * slots. In this case, fp->argv points to new space in a new arena, and - * marking the caller's operand stack, or an external caller's allocated - * stack tracked by a JSStackHeader, will not mark all the values stored - * and addressable via fp->argv. - * - * So in summary, solely for the hard case of moving argv due to missing - * formals and extra roots, we must mark actuals, missing formals, and any - * local roots arrayed at fp->argv here. - * - * It would be good to avoid redundant marking of the same reference, in - * the case where fp->argv does point into caller-allocated space tracked - * by fp->down->spbase or cx->stackHeaders. This would allow callbacks - * such as the forthcoming rt->gcThingCallback (bug 333078) to compute JS - * reference counts. So this comment deserves a FIXME bug to cite. - */ - if (fp->argv) { - nslots = fp->argc; - if (fp->fun) { - if (fp->fun->nargs > nslots) - nslots = fp->fun->nargs; - if (!FUN_INTERPRETED(fp->fun)) - nslots += fp->fun->u.n.extra; - } - GC_MARK_JSVALS(cx, nslots + 2, fp->argv - 2, "arg"); - } - if (JSVAL_IS_GCTHING(fp->rval)) - GC_MARK(cx, JSVAL_TO_GCTHING(fp->rval), "rval"); - if (fp->vars) - GC_MARK_JSVALS(cx, fp->nvars, fp->vars, "var"); - GC_MARK(cx, fp->scopeChain, "scope chain"); - if (fp->sharpArray) - GC_MARK(cx, fp->sharpArray, "sharp array"); - - if (fp->xmlNamespace) - GC_MARK(cx, fp->xmlNamespace, "xmlNamespace"); -} - -static void -MarkWeakRoots(JSContext *cx, JSWeakRoots *wr) -{ - uintN i; - void *thing; - - for (i = 0; i < GCX_NTYPES; i++) - GC_MARK(cx, wr->newborn[i], gc_typenames[i]); - if (wr->lastAtom) - GC_MARK_ATOM(cx, wr->lastAtom); - if (JSVAL_IS_GCTHING(wr->lastInternalResult)) { - thing = JSVAL_TO_GCTHING(wr->lastInternalResult); - if (thing) - GC_MARK(cx, thing, "lastInternalResult"); - } -} - -/* - * When gckind is GC_LAST_DITCH, it indicates a call from js_NewGCThing with - * rt->gcLock already held and when the lock should be kept on return. - */ -void -js_GC(JSContext *cx, JSGCInvocationKind gckind) -{ - JSRuntime *rt; - JSBool keepAtoms; - uintN i, type; - JSContext *iter, *acx; -#if JS_HAS_GENERATORS - JSGenerator **genTodoTail; -#endif - JSStackFrame *fp, *chain; - JSStackHeader *sh; - JSTempValueRooter *tvr; - size_t nbytes, limit, offset; - JSGCArena *a, **ap; - uint8 flags, *flagp, *firstPage; - JSGCThing *thing, *freeList; - JSGCArenaList *arenaList; - GCFinalizeOp finalizer; - JSBool allClear; -#ifdef JS_THREADSAFE - uint32 requestDebit; -#endif - - rt = cx->runtime; -#ifdef JS_THREADSAFE - /* Avoid deadlock. */ - JS_ASSERT(!JS_IS_RUNTIME_LOCKED(rt)); -#endif - - if (gckind == GC_LAST_DITCH) { - /* The last ditch GC preserves all atoms and weak roots. */ - keepAtoms = JS_TRUE; - } else { - JS_CLEAR_WEAK_ROOTS(&cx->weakRoots); - rt->gcPoke = JS_TRUE; - - /* Keep atoms when a suspended compile is running on another context. */ - keepAtoms = (rt->gcKeepAtoms != 0); - } - - /* - * Don't collect garbage if the runtime isn't up, and cx is not the last - * context in the runtime. The last context must force a GC, and nothing - * should suppress that final collection or there may be shutdown leaks, - * or runtime bloat until the next context is created. - */ - if (rt->state != JSRTS_UP && gckind != GC_LAST_CONTEXT) - return; - - restart_after_callback: - /* - * Let the API user decide to defer a GC if it wants to (unless this - * is the last context). Invoke the callback regardless. - */ - if (rt->gcCallback && - !rt->gcCallback(cx, JSGC_BEGIN) && - gckind != GC_LAST_CONTEXT) { - return; - } - - /* Lock out other GC allocator and collector invocations. */ - if (gckind != GC_LAST_DITCH) - JS_LOCK_GC(rt); - - /* Do nothing if no mutator has executed since the last GC. */ - if (!rt->gcPoke) { - METER(rt->gcStats.nopoke++); - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); - return; - } - METER(rt->gcStats.poke++); - rt->gcPoke = JS_FALSE; - -#ifdef JS_THREADSAFE - JS_ASSERT(cx->thread->id == js_CurrentThreadId()); - - /* Bump gcLevel and return rather than nest on this thread. */ - if (rt->gcThread == cx->thread) { - JS_ASSERT(rt->gcLevel > 0); - rt->gcLevel++; - METER(if (rt->gcLevel > rt->gcStats.maxlevel) - rt->gcStats.maxlevel = rt->gcLevel); - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); - return; - } - - /* - * If we're in one or more requests (possibly on more than one context) - * running on the current thread, indicate, temporarily, that all these - * requests are inactive. If cx->thread is NULL, then cx is not using - * the request model, and does not contribute to rt->requestCount. - */ - requestDebit = 0; - if (cx->thread) { - JSCList *head, *link; - - /* - * Check all contexts on cx->thread->contextList for active requests, - * counting each such context against requestDebit. - */ - head = &cx->thread->contextList; - for (link = head->next; link != head; link = link->next) { - acx = CX_FROM_THREAD_LINKS(link); - JS_ASSERT(acx->thread == cx->thread); - if (acx->requestDepth) - requestDebit++; - } - } else { - /* - * We assert, but check anyway, in case someone is misusing the API. - * Avoiding the loop over all of rt's contexts is a win in the event - * that the GC runs only on request-less contexts with null threads, - * in a special thread such as might be used by the UI/DOM/Layout - * "mozilla" or "main" thread in Mozilla-the-browser. - */ - JS_ASSERT(cx->requestDepth == 0); - if (cx->requestDepth) - requestDebit = 1; - } - if (requestDebit) { - JS_ASSERT(requestDebit <= rt->requestCount); - rt->requestCount -= requestDebit; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - } - - /* If another thread is already in GC, don't attempt GC; wait instead. */ - if (rt->gcLevel > 0) { - /* Bump gcLevel to restart the current GC, so it finds new garbage. */ - rt->gcLevel++; - METER(if (rt->gcLevel > rt->gcStats.maxlevel) - rt->gcStats.maxlevel = rt->gcLevel); - - /* Wait for the other thread to finish, then resume our request. */ - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - if (requestDebit) - rt->requestCount += requestDebit; - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); - return; - } - - /* No other thread is in GC, so indicate that we're now in GC. */ - rt->gcLevel = 1; - rt->gcThread = cx->thread; - - /* Wait for all other requests to finish. */ - while (rt->requestCount > 0) - JS_AWAIT_REQUEST_DONE(rt); - -#else /* !JS_THREADSAFE */ - - /* Bump gcLevel and return rather than nest; the outer gc will restart. */ - rt->gcLevel++; - METER(if (rt->gcLevel > rt->gcStats.maxlevel) - rt->gcStats.maxlevel = rt->gcLevel); - if (rt->gcLevel > 1) - return; - -#endif /* !JS_THREADSAFE */ - - /* - * Set rt->gcRunning here within the GC lock, and after waiting for any - * active requests to end, so that new requests that try to JS_AddRoot, - * JS_RemoveRoot, or JS_RemoveRootRT block in JS_BeginRequest waiting for - * rt->gcLevel to drop to zero, while request-less calls to the *Root* - * APIs block in js_AddRoot or js_RemoveRoot (see above in this file), - * waiting for GC to finish. - */ - rt->gcRunning = JS_TRUE; - JS_UNLOCK_GC(rt); - - /* Reset malloc counter. */ - rt->gcMallocBytes = 0; - - /* Drop atoms held by the property cache, and clear property weak links. */ - js_DisablePropertyCache(cx); - js_FlushPropertyCache(cx); -#ifdef DEBUG_scopemeters - { extern void js_DumpScopeMeters(JSRuntime *rt); - js_DumpScopeMeters(rt); - } -#endif - -#ifdef JS_THREADSAFE - /* - * Set all thread local freelists to NULL. We may visit a thread's - * freelist more than once. To avoid redundant clearing we unroll the - * current thread's step. - * - * Also, in case a JSScript wrapped within an object was finalized, we - * null acx->thread->gsnCache.script and finish the cache's hashtable. - * Note that js_DestroyScript, called from script_finalize, will have - * already cleared cx->thread->gsnCache above during finalization, so we - * don't have to here. - */ - memset(cx->thread->gcFreeLists, 0, sizeof cx->thread->gcFreeLists); - iter = NULL; - while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)) != NULL) { - if (!acx->thread || acx->thread == cx->thread) - continue; - memset(acx->thread->gcFreeLists, 0, sizeof acx->thread->gcFreeLists); - GSN_CACHE_CLEAR(&acx->thread->gsnCache); - } -#else - /* The thread-unsafe case just has to clear the runtime's GSN cache. */ - GSN_CACHE_CLEAR(&rt->gsnCache); -#endif - -restart: - rt->gcNumber++; - JS_ASSERT(!rt->gcUnscannedArenaStackTop); - JS_ASSERT(rt->gcUnscannedBagSize == 0); - - /* - * Mark phase. - */ - JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_marker, cx); - if (rt->gcLocksHash) - JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_marker, cx); - js_MarkAtomState(&rt->atomState, keepAtoms, gc_mark_atom_key_thing, cx); - js_MarkWatchPoints(cx); - js_MarkScriptFilenames(rt, keepAtoms); - js_MarkNativeIteratorStates(cx); - -#if JS_HAS_GENERATORS - genTodoTail = MarkScheduledGenerators(cx); - JS_ASSERT(!*genTodoTail); -#endif - - iter = NULL; - while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL) { - /* - * Iterate frame chain and dormant chains. Temporarily tack current - * frame onto the head of the dormant list to ease iteration. - * - * (NB: see comment on this whole "dormant" thing in js_Execute.) - */ - chain = acx->fp; - if (chain) { - JS_ASSERT(!chain->dormantNext); - chain->dormantNext = acx->dormantFrameChain; - } else { - chain = acx->dormantFrameChain; - } - - for (fp = chain; fp; fp = chain = chain->dormantNext) { - do { - js_MarkStackFrame(cx, fp); - } while ((fp = fp->down) != NULL); - } - - /* Cleanup temporary "dormant" linkage. */ - if (acx->fp) - acx->fp->dormantNext = NULL; - - /* Mark other roots-by-definition in acx. */ - GC_MARK(cx, acx->globalObject, "global object"); - MarkWeakRoots(cx, &acx->weakRoots); - if (acx->throwing) { - if (JSVAL_IS_GCTHING(acx->exception)) - GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception"); - } else { - /* Avoid keeping GC-ed junk stored in JSContext.exception. */ - acx->exception = JSVAL_NULL; - } -#if JS_HAS_LVALUE_RETURN - if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) - GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2"); -#endif - - for (sh = acx->stackHeaders; sh; sh = sh->down) { - METER(rt->gcStats.stackseg++); - METER(rt->gcStats.segslots += sh->nslots); - GC_MARK_JSVALS(cx, sh->nslots, JS_STACK_SEGMENT(sh), "stack"); - } - - if (acx->localRootStack) - js_MarkLocalRoots(cx, acx->localRootStack); - - for (tvr = acx->tempValueRooters; tvr; tvr = tvr->down) { - switch (tvr->count) { - case JSTVU_SINGLE: - if (JSVAL_IS_GCTHING(tvr->u.value)) { - GC_MARK(cx, JSVAL_TO_GCTHING(tvr->u.value), - "tvr->u.value"); - } - break; - case JSTVU_MARKER: - tvr->u.marker(cx, tvr); - break; - case JSTVU_SPROP: - MARK_SCOPE_PROPERTY(cx, tvr->u.sprop); - break; - case JSTVU_WEAK_ROOTS: - MarkWeakRoots(cx, tvr->u.weakRoots); - break; - default: - JS_ASSERT(tvr->count >= 0); - GC_MARK_JSVALS(cx, tvr->count, tvr->u.array, "tvr->u.array"); - } - } - - if (acx->sharpObjectMap.depth > 0) - js_GCMarkSharpMap(cx, &acx->sharpObjectMap); - } - -#ifdef DUMP_CALL_TABLE - js_DumpCallTable(cx); -#endif - - /* - * Mark children of things that caused too deep recursion during above - * marking phase. - */ - ScanDelayedChildren(cx); - -#if JS_HAS_GENERATORS - /* - * Close phase: search and mark part. See comments in - * FindAndMarkObjectsToClose for details. - */ - FindAndMarkObjectsToClose(cx, gckind, genTodoTail); - - /* - * Mark children of things that caused too deep recursion during the - * just-completed marking part of the close phase. - */ - ScanDelayedChildren(cx); -#endif - - JS_ASSERT(!cx->insideGCMarkCallback); - if (rt->gcCallback) { - cx->insideGCMarkCallback = JS_TRUE; - (void) rt->gcCallback(cx, JSGC_MARK_END); - JS_ASSERT(cx->insideGCMarkCallback); - cx->insideGCMarkCallback = JS_FALSE; - } - JS_ASSERT(rt->gcUnscannedBagSize == 0); - - /* Finalize iterator states before the objects they iterate over. */ - CloseIteratorStates(cx); - - /* - * Sweep phase. - * - * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set - * so that any attempt to allocate a GC-thing from a finalizer will fail, - * rather than nest badly and leave the unmarked newborn to be swept. - * - * Finalize smaller objects before larger, to guarantee finalization of - * GC-allocated obj->slots after obj. See FreeSlots in jsobj.c. - */ - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - nbytes = GC_FREELIST_NBYTES(i); - limit = arenaList->lastLimit; - for (a = arenaList->last; a; a = a->prev) { - JS_ASSERT(!a->prevUnscanned); - JS_ASSERT(a->unscannedPages == 0); - firstPage = (uint8 *) FIRST_THING_PAGE(a); - for (offset = 0; offset != limit; offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) { - JS_ASSERT(((JSGCPageInfo *)(firstPage + offset))-> - unscannedBitmap == 0); - offset += PAGE_THING_GAP(nbytes); - } - JS_ASSERT(offset < limit); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - flags = *flagp; - if (flags & GCF_MARK) { - *flagp &= ~GCF_MARK; - } else if (!(flags & (GCF_LOCK | GCF_FINAL))) { - /* Call the finalizer with GCF_FINAL ORed into flags. */ - type = flags & GCF_TYPEMASK; - finalizer = gc_finalizers[type]; - if (finalizer) { - thing = (JSGCThing *)(firstPage + offset); - *flagp = (uint8)(flags | GCF_FINAL); - if (type >= GCX_EXTERNAL_STRING) - js_PurgeDeflatedStringCache(rt, (JSString *)thing); - finalizer(cx, thing); - } - - /* Set flags to GCF_FINAL, signifying that thing is free. */ - *flagp = GCF_FINAL; - } - } - limit = GC_THINGS_SIZE; - } - } - - /* - * Sweep the runtime's property tree after finalizing objects, in case any - * had watchpoints referencing tree nodes. Then sweep atoms, which may be - * referenced from dead property ids. - */ - js_SweepScopeProperties(rt); - js_SweepAtomState(&rt->atomState); - - /* - * Sweep script filenames after sweeping functions in the generic loop - * above. In this way when a scripted function's finalizer destroys the - * script and calls rt->destroyScriptHook, the hook can still access the - * script's filename. See bug 323267. - */ - js_SweepScriptFilenames(rt); - - /* - * Free phase. - * Free any unused arenas and rebuild the JSGCThing freelist. - */ - for (i = 0; i < GC_NUM_FREELISTS; i++) { - arenaList = &rt->gcArenaList[i]; - ap = &arenaList->last; - a = *ap; - if (!a) - continue; - - allClear = JS_TRUE; - arenaList->freeList = NULL; - freeList = NULL; - METER(arenaList->stats.nthings = 0); - METER(arenaList->stats.freelen = 0); - - nbytes = GC_FREELIST_NBYTES(i); - limit = arenaList->lastLimit; - do { - METER(size_t nfree = 0); - firstPage = (uint8 *) FIRST_THING_PAGE(a); - for (offset = 0; offset != limit; offset += nbytes) { - if ((offset & GC_PAGE_MASK) == 0) - offset += PAGE_THING_GAP(nbytes); - JS_ASSERT(offset < limit); - flagp = a->base + offset / sizeof(JSGCThing); - if (flagp >= firstPage) - flagp += GC_THINGS_SIZE; - - if (*flagp != GCF_FINAL) { - allClear = JS_FALSE; - METER(++arenaList->stats.nthings); - } else { - thing = (JSGCThing *)(firstPage + offset); - thing->flagp = flagp; - thing->next = freeList; - freeList = thing; - METER(++nfree); - } - } - if (allClear) { - /* - * Forget just assembled free list head for the arena - * and destroy the arena itself. - */ - freeList = arenaList->freeList; - DestroyGCArena(rt, arenaList, ap); - } else { - allClear = JS_TRUE; - arenaList->freeList = freeList; - ap = &a->prev; - METER(arenaList->stats.freelen += nfree); - METER(arenaList->stats.totalfreelen += nfree); - METER(++arenaList->stats.totalarenas); - } - limit = GC_THINGS_SIZE; - } while ((a = *ap) != NULL); - } - - if (rt->gcCallback) - (void) rt->gcCallback(cx, JSGC_FINALIZE_END); -#ifdef DEBUG_srcnotesize - { extern void DumpSrcNoteSizeHist(); - DumpSrcNoteSizeHist(); - printf("GC HEAP SIZE %lu (%lu)\n", - (unsigned long)rt->gcBytes, (unsigned long)rt->gcPrivateBytes); - } -#endif - - JS_LOCK_GC(rt); - - /* - * We want to restart GC if js_GC was called recursively or if any of the - * finalizers called js_RemoveRoot or js_UnlockGCThingRT. - */ - if (rt->gcLevel > 1 || rt->gcPoke) { - rt->gcLevel = 1; - rt->gcPoke = JS_FALSE; - JS_UNLOCK_GC(rt); - goto restart; - } - js_EnablePropertyCache(cx); - rt->gcLevel = 0; - rt->gcLastBytes = rt->gcBytes; - rt->gcRunning = JS_FALSE; - -#ifdef JS_THREADSAFE - /* If we were invoked during a request, pay back the temporary debit. */ - if (requestDebit) - rt->requestCount += requestDebit; - rt->gcThread = NULL; - JS_NOTIFY_GC_DONE(rt); - - /* - * Unlock unless we have GC_LAST_DITCH which requires locked GC on return. - */ - if (gckind != GC_LAST_DITCH) - JS_UNLOCK_GC(rt); -#endif - - /* Execute JSGC_END callback outside the lock. */ - if (rt->gcCallback) { - JSWeakRoots savedWeakRoots; - JSTempValueRooter tvr; - - if (gckind == GC_LAST_DITCH) { - /* - * We allow JSGC_END implementation to force a full GC or allocate - * new GC things. Thus we must protect the weak roots from GC or - * overwrites. - */ - savedWeakRoots = cx->weakRoots; - JS_PUSH_TEMP_ROOT_WEAK_COPY(cx, &savedWeakRoots, &tvr); - JS_KEEP_ATOMS(rt); - JS_UNLOCK_GC(rt); - } - - (void) rt->gcCallback(cx, JSGC_END); - - if (gckind == GC_LAST_DITCH) { - JS_LOCK_GC(rt); - JS_UNKEEP_ATOMS(rt); - JS_POP_TEMP_ROOT(cx, &tvr); - } else if (gckind == GC_LAST_CONTEXT && rt->gcPoke) { - /* - * On shutdown iterate until JSGC_END callback stops creating - * garbage. - */ - goto restart_after_callback; - } - } -} - -void -js_UpdateMallocCounter(JSContext *cx, size_t nbytes) -{ - uint32 *pbytes, bytes; - -#ifdef JS_THREADSAFE - pbytes = &cx->thread->gcMallocBytes; -#else - pbytes = &cx->runtime->gcMallocBytes; -#endif - bytes = *pbytes; - *pbytes = ((uint32)-1 - bytes <= nbytes) ? (uint32)-1 : bytes + nbytes; -} diff --git a/spidermonkey/libjs/jsgc.h b/spidermonkey/libjs/jsgc.h deleted file mode 100644 index ec623a1..0000000 --- a/spidermonkey/libjs/jsgc.h +++ /dev/null @@ -1,368 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsgc_h___ -#define jsgc_h___ -/* - * JS Garbage Collector. - */ -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsdhash.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* GC thing type indexes. */ -#define GCX_OBJECT 0 /* JSObject */ -#define GCX_STRING 1 /* JSString */ -#define GCX_DOUBLE 2 /* jsdouble */ -#define GCX_MUTABLE_STRING 3 /* JSString that's mutable -- - single-threaded only! */ -#define GCX_PRIVATE 4 /* private (unscanned) data */ -#define GCX_NAMESPACE 5 /* JSXMLNamespace */ -#define GCX_QNAME 6 /* JSXMLQName */ -#define GCX_XML 7 /* JSXML */ -#define GCX_EXTERNAL_STRING 8 /* JSString w/ external chars */ - -#define GCX_NTYPES_LOG2 4 /* type index bits */ -#define GCX_NTYPES JS_BIT(GCX_NTYPES_LOG2) - -/* GC flag definitions, must fit in 8 bits (type index goes in the low bits). */ -#define GCF_TYPEMASK JS_BITMASK(GCX_NTYPES_LOG2) -#define GCF_MARK JS_BIT(GCX_NTYPES_LOG2) -#define GCF_FINAL JS_BIT(GCX_NTYPES_LOG2 + 1) -#define GCF_SYSTEM JS_BIT(GCX_NTYPES_LOG2 + 2) -#define GCF_LOCKSHIFT (GCX_NTYPES_LOG2 + 3) /* lock bit shift */ -#define GCF_LOCK JS_BIT(GCF_LOCKSHIFT) /* lock request bit in API */ - -/* Pseudo-flag that modifies GCX_STRING to make GCX_MUTABLE_STRING. */ -#define GCF_MUTABLE 2 - -#if (GCX_STRING | GCF_MUTABLE) != GCX_MUTABLE_STRING -# error "mutable string type index botch!" -#endif - -extern uint8 * -js_GetGCThingFlags(void *thing); - -/* - * The sole purpose of the function is to preserve public API compatibility - * in JS_GetStringBytes which takes only single JSString* argument. - */ -JSRuntime* -js_GetGCStringRuntime(JSString *str); - -#if 1 -/* - * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles - * loading oldval. XXX remove implied force, fix jsinterp.c's "second arg - * ignored", etc. - */ -#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JS_TRUE) -#else -#define GC_POKE(cx, oldval) ((cx)->runtime->gcPoke = JSVAL_IS_GCTHING(oldval)) -#endif - -extern intN -js_ChangeExternalStringFinalizer(JSStringFinalizeOp oldop, - JSStringFinalizeOp newop); - -extern JSBool -js_InitGC(JSRuntime *rt, uint32 maxbytes); - -extern void -js_FinishGC(JSRuntime *rt); - -extern JSBool -js_AddRoot(JSContext *cx, void *rp, const char *name); - -extern JSBool -js_AddRootRT(JSRuntime *rt, void *rp, const char *name); - -extern JSBool -js_RemoveRoot(JSRuntime *rt, void *rp); - -#ifdef DEBUG -extern void -js_DumpNamedRoots(JSRuntime *rt, - void (*dump)(const char *name, void *rp, void *data), - void *data); -#endif - -extern uint32 -js_MapGCRoots(JSRuntime *rt, JSGCRootMapFun map, void *data); - -/* Table of pointers with count valid members. */ -typedef struct JSPtrTable { - size_t count; - void **array; -} JSPtrTable; - -extern JSBool -js_RegisterCloseableIterator(JSContext *cx, JSObject *obj); - -#if JS_HAS_GENERATORS - -/* - * Runtime state to support generators' close hooks. - */ -typedef struct JSGCCloseState { - /* - * Singly linked list of generators that are reachable from GC roots or - * were created after the last GC. - */ - JSGenerator *reachableList; - - /* - * Head of the queue of generators that have already become unreachable but - * whose close hooks are not yet run. - */ - JSGenerator *todoQueue; - -#ifndef JS_THREADSAFE - /* - * Flag indicating that the current thread is excuting a close hook for - * single thread case. - */ - JSBool runningCloseHook; -#endif -} JSGCCloseState; - -extern void -js_RegisterGenerator(JSContext *cx, JSGenerator *gen); - -extern JSBool -js_RunCloseHooks(JSContext *cx); - -#endif - -/* - * The private JSGCThing struct, which describes a gcFreeList element. - */ -struct JSGCThing { - JSGCThing *next; - uint8 *flagp; -}; - -#define GC_NBYTES_MAX (10 * sizeof(JSGCThing)) -#define GC_NUM_FREELISTS (GC_NBYTES_MAX / sizeof(JSGCThing)) -#define GC_FREELIST_NBYTES(i) (((i) + 1) * sizeof(JSGCThing)) -#define GC_FREELIST_INDEX(n) (((n) / sizeof(JSGCThing)) - 1) - -extern void * -js_NewGCThing(JSContext *cx, uintN flags, size_t nbytes); - -extern JSBool -js_LockGCThing(JSContext *cx, void *thing); - -extern JSBool -js_LockGCThingRT(JSRuntime *rt, void *thing); - -extern JSBool -js_UnlockGCThingRT(JSRuntime *rt, void *thing); - -extern JSBool -js_IsAboutToBeFinalized(JSContext *cx, void *thing); - -extern void -js_MarkAtom(JSContext *cx, JSAtom *atom); - -/* We avoid a large number of unnecessary calls by doing the flag check first */ -#define GC_MARK_ATOM(cx, atom) \ - JS_BEGIN_MACRO \ - if (!((atom)->flags & ATOM_MARK)) \ - js_MarkAtom(cx, atom); \ - JS_END_MACRO - -/* - * Always use GC_MARK macro and never call js_MarkGCThing directly so - * when GC_MARK_DEBUG is defined the dump of live GC things does not miss - * a thing. - */ -extern void -js_MarkGCThing(JSContext *cx, void *thing); - -#ifdef GC_MARK_DEBUG - -# define GC_MARK(cx, thing, name) js_MarkNamedGCThing(cx, thing, name) - -extern void -js_MarkNamedGCThing(JSContext *cx, void *thing, const char *name); - -extern JS_FRIEND_DATA(FILE *) js_DumpGCHeap; -JS_EXTERN_DATA(void *) js_LiveThingToFind; - -#else - -# define GC_MARK(cx, thing, name) js_MarkGCThing(cx, thing) - -#endif - -extern void -js_MarkStackFrame(JSContext *cx, JSStackFrame *fp); - -/* - * Kinds of js_GC invocation. - */ -typedef enum JSGCInvocationKind { - /* Normal invocation. */ - GC_NORMAL, - - /* - * Called from js_DestroyContext for last JSContext in a JSRuntime, when - * it is imperative that rt->gcPoke gets cleared early in js_GC. - */ - GC_LAST_CONTEXT, - - /* - * Called from js_NewGCThing as a last-ditch GC attempt. See comments - * before js_GC definition for details. - */ - GC_LAST_DITCH -} JSGCInvocationKind; - -extern void -js_GC(JSContext *cx, JSGCInvocationKind gckind); - -/* Call this after succesful malloc of memory for GC-related things. */ -extern void -js_UpdateMallocCounter(JSContext *cx, size_t nbytes); - -#ifdef DEBUG_notme -#define JS_GCMETER 1 -#endif - -#ifdef JS_GCMETER - -typedef struct JSGCStats { -#ifdef JS_THREADSAFE - uint32 localalloc; /* number of succeeded allocations from local lists */ -#endif - uint32 alloc; /* number of allocation attempts */ - uint32 retry; /* allocation attempt retries after running the GC */ - uint32 retryhalt; /* allocation retries halted by the branch callback */ - uint32 fail; /* allocation failures */ - uint32 finalfail; /* finalizer calls allocator failures */ - uint32 lockborn; /* things born locked */ - uint32 lock; /* valid lock calls */ - uint32 unlock; /* valid unlock calls */ - uint32 depth; /* mark tail recursion depth */ - uint32 maxdepth; /* maximum mark tail recursion depth */ - uint32 cdepth; /* mark recursion depth of C functions */ - uint32 maxcdepth; /* maximum mark recursion depth of C functions */ - uint32 unscanned; /* mark C stack overflows or number of times - GC things were put in unscanned bag */ -#ifdef DEBUG - uint32 maxunscanned; /* maximum size of unscanned bag */ -#endif - uint32 maxlevel; /* maximum GC nesting (indirect recursion) level */ - uint32 poke; /* number of potentially useful GC calls */ - uint32 nopoke; /* useless GC calls where js_PokeGC was not set */ - uint32 afree; /* thing arenas freed so far */ - uint32 stackseg; /* total extraordinary stack segments scanned */ - uint32 segslots; /* total stack segment jsval slots scanned */ - uint32 nclose; /* number of objects with close hooks */ - uint32 maxnclose; /* max number of objects with close hooks */ - uint32 closelater; /* number of close hooks scheduled to run */ - uint32 maxcloselater; /* max number of close hooks scheduled to run */ -} JSGCStats; - -extern JS_FRIEND_API(void) -js_DumpGCStats(JSRuntime *rt, FILE *fp); - -#endif /* JS_GCMETER */ - -typedef struct JSGCArena JSGCArena; -typedef struct JSGCArenaList JSGCArenaList; - -#ifdef JS_GCMETER -typedef struct JSGCArenaStats JSGCArenaStats; - -struct JSGCArenaStats { - uint32 narenas; /* number of arena in list */ - uint32 maxarenas; /* maximun number of allocated arenas */ - uint32 nthings; /* number of allocates JSGCThing */ - uint32 maxthings; /* maximum number number of allocates JSGCThing */ - uint32 totalnew; /* number of succeeded calls to js_NewGCThing */ - uint32 freelen; /* freeList lengths */ - uint32 recycle; /* number of things recycled through freeList */ - uint32 totalarenas; /* total number of arenas with live things that - GC scanned so far */ - uint32 totalfreelen; /* total number of things that GC put to free - list so far */ -}; -#endif - -struct JSGCArenaList { - JSGCArena *last; /* last allocated GC arena */ - uint16 lastLimit; /* end offset of allocated so far things in - the last arena */ - uint16 thingSize; /* size of things to allocate on this list */ - JSGCThing *freeList; /* list of free GC things */ -#ifdef JS_GCMETER - JSGCArenaStats stats; -#endif -}; - -typedef struct JSWeakRoots { - /* Most recently created things by type, members of the GC's root set. */ - JSGCThing *newborn[GCX_NTYPES]; - - /* Atom root for the last-looked-up atom on this context. */ - JSAtom *lastAtom; - - /* Root for the result of the most recent js_InternalInvoke call. */ - jsval lastInternalResult; -} JSWeakRoots; - -JS_STATIC_ASSERT(JSVAL_NULL == 0); -#define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots))) - -#ifdef DEBUG_notme -#define TOO_MUCH_GC 1 -#endif - -#ifdef WAY_TOO_MUCH_GC -#define TOO_MUCH_GC 1 -#endif - -JS_END_EXTERN_C - -#endif /* jsgc_h___ */ diff --git a/spidermonkey/libjs/jshash.c b/spidermonkey/libjs/jshash.c deleted file mode 100644 index 8e25517..0000000 --- a/spidermonkey/libjs/jshash.c +++ /dev/null @@ -1,483 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * PR hash table package. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ - -/* Compute the number of buckets in ht */ -#define NBUCKETS(ht) JS_BIT(JS_HASH_BITS - (ht)->shift) - -/* The smallest table has 16 buckets */ -#define MINBUCKETSLOG2 4 -#define MINBUCKETS JS_BIT(MINBUCKETSLOG2) - -/* Compute the maximum entries given n buckets that we will tolerate, ~90% */ -#define OVERLOADED(n) ((n) - ((n) >> 3)) - -/* Compute the number of entries below which we shrink the table by half */ -#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0) - -/* -** Stubs for default hash allocator ops. -*/ -static void * -DefaultAllocTable(void *pool, size_t size) -{ - return malloc(size); -} - -static void -DefaultFreeTable(void *pool, void *item) -{ - free(item); -} - -static JSHashEntry * -DefaultAllocEntry(void *pool, const void *key) -{ - return (JSHashEntry*) malloc(sizeof(JSHashEntry)); -} - -static void -DefaultFreeEntry(void *pool, JSHashEntry *he, uintN flag) -{ - if (flag == HT_FREE_ENTRY) - free(he); -} - -static JSHashAllocOps defaultHashAllocOps = { - DefaultAllocTable, DefaultFreeTable, - DefaultAllocEntry, DefaultFreeEntry -}; - -JS_PUBLIC_API(JSHashTable *) -JS_NewHashTable(uint32 n, JSHashFunction keyHash, - JSHashComparator keyCompare, JSHashComparator valueCompare, - JSHashAllocOps *allocOps, void *allocPriv) -{ - JSHashTable *ht; - size_t nb; - - if (n <= MINBUCKETS) { - n = MINBUCKETSLOG2; - } else { - n = JS_CeilingLog2(n); - if ((int32)n < 0) - return NULL; - } - - if (!allocOps) allocOps = &defaultHashAllocOps; - - ht = (JSHashTable*) allocOps->allocTable(allocPriv, sizeof *ht); - if (!ht) - return NULL; - memset(ht, 0, sizeof *ht); - ht->shift = JS_HASH_BITS - n; - n = JS_BIT(n); - nb = n * sizeof(JSHashEntry *); - ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); - if (!ht->buckets) { - allocOps->freeTable(allocPriv, ht); - return NULL; - } - memset(ht->buckets, 0, nb); - - ht->keyHash = keyHash; - ht->keyCompare = keyCompare; - ht->valueCompare = valueCompare; - ht->allocOps = allocOps; - ht->allocPriv = allocPriv; - return ht; -} - -JS_PUBLIC_API(void) -JS_HashTableDestroy(JSHashTable *ht) -{ - uint32 i, n; - JSHashEntry *he, **hep; - JSHashAllocOps *allocOps = ht->allocOps; - void *allocPriv = ht->allocPriv; - - n = NBUCKETS(ht); - for (i = 0; i < n; i++) { - hep = &ht->buckets[i]; - while ((he = *hep) != NULL) { - *hep = he->next; - allocOps->freeEntry(allocPriv, he, HT_FREE_ENTRY); - } - } -#ifdef DEBUG - memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); -#endif - allocOps->freeTable(allocPriv, ht->buckets); -#ifdef DEBUG - memset(ht, 0xDB, sizeof *ht); -#endif - allocOps->freeTable(allocPriv, ht); -} - -/* - * Multiplicative hash, from Knuth 6.4. - */ -#define BUCKET_HEAD(ht, keyHash) \ - (&(ht)->buckets[((keyHash) * JS_GOLDEN_RATIO) >> (ht)->shift]) - -JS_PUBLIC_API(JSHashEntry **) -JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key) -{ - JSHashEntry *he, **hep, **hep0; - -#ifdef HASHMETER - ht->nlookups++; -#endif - hep = hep0 = BUCKET_HEAD(ht, keyHash); - while ((he = *hep) != NULL) { - if (he->keyHash == keyHash && ht->keyCompare(key, he->key)) { - /* Move to front of chain if not already there */ - if (hep != hep0) { - *hep = he->next; - he->next = *hep0; - *hep0 = he; - } - return hep0; - } - hep = &he->next; -#ifdef HASHMETER - ht->nsteps++; -#endif - } - return hep; -} - -static JSBool -Resize(JSHashTable *ht, uint32 newshift) -{ - size_t nb, nentries, i; - JSHashEntry **oldbuckets, *he, *next, **hep; -#ifdef DEBUG - size_t nold = NBUCKETS(ht); -#endif - - JS_ASSERT(newshift < JS_HASH_BITS); - - nb = (size_t)1 << (JS_HASH_BITS - newshift); - - /* Integer overflow protection. */ - if (nb > (size_t)-1 / sizeof(JSHashEntry*)) - return JS_FALSE; - nb *= sizeof(JSHashEntry*); - - oldbuckets = ht->buckets; - ht->buckets = (JSHashEntry**)ht->allocOps->allocTable(ht->allocPriv, nb); - if (!ht->buckets) { - ht->buckets = oldbuckets; - return JS_FALSE; - } - memset(ht->buckets, 0, nb); - - ht->shift = newshift; - nentries = ht->nentries; - - for (i = 0; nentries != 0; i++) { - for (he = oldbuckets[i]; he; he = next) { - JS_ASSERT(nentries != 0); - --nentries; - next = he->next; - hep = BUCKET_HEAD(ht, he->keyHash); - - /* - * Since he comes from the old table, it must be unique and we - * simply add it to the head of bucket chain without chain lookup. - */ - he->next = *hep; - *hep = he; - } - } -#ifdef DEBUG - memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); -#endif - ht->allocOps->freeTable(ht->allocPriv, oldbuckets); - return JS_TRUE; -} - -JS_PUBLIC_API(JSHashEntry *) -JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, - JSHashNumber keyHash, const void *key, void *value) -{ - uint32 n; - JSHashEntry *he; - - /* Grow the table if it is overloaded */ - n = NBUCKETS(ht); - if (ht->nentries >= OVERLOADED(n)) { - if (!Resize(ht, ht->shift - 1)) - return NULL; -#ifdef HASHMETER - ht->ngrows++; -#endif - hep = JS_HashTableRawLookup(ht, keyHash, key); - } - - /* Make a new key value entry */ - he = ht->allocOps->allocEntry(ht->allocPriv, key); - if (!he) - return NULL; - he->keyHash = keyHash; - he->key = key; - he->value = value; - he->next = *hep; - *hep = he; - ht->nentries++; - return he; -} - -JS_PUBLIC_API(JSHashEntry *) -JS_HashTableAdd(JSHashTable *ht, const void *key, void *value) -{ - JSHashNumber keyHash; - JSHashEntry *he, **hep; - - keyHash = ht->keyHash(key); - hep = JS_HashTableRawLookup(ht, keyHash, key); - if ((he = *hep) != NULL) { - /* Hit; see if values match */ - if (ht->valueCompare(he->value, value)) { - /* key,value pair is already present in table */ - return he; - } - if (he->value) - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_VALUE); - he->value = value; - return he; - } - return JS_HashTableRawAdd(ht, hep, keyHash, key, value); -} - -JS_PUBLIC_API(void) -JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he) -{ - uint32 n; - - *hep = he->next; - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); - - /* Shrink table if it's underloaded */ - n = NBUCKETS(ht); - if (--ht->nentries < UNDERLOADED(n)) { - Resize(ht, ht->shift + 1); -#ifdef HASHMETER - ht->nshrinks++; -#endif - } -} - -JS_PUBLIC_API(JSBool) -JS_HashTableRemove(JSHashTable *ht, const void *key) -{ - JSHashNumber keyHash; - JSHashEntry *he, **hep; - - keyHash = ht->keyHash(key); - hep = JS_HashTableRawLookup(ht, keyHash, key); - if ((he = *hep) == NULL) - return JS_FALSE; - - /* Hit; remove element */ - JS_HashTableRawRemove(ht, hep, he); - return JS_TRUE; -} - -JS_PUBLIC_API(void *) -JS_HashTableLookup(JSHashTable *ht, const void *key) -{ - JSHashNumber keyHash; - JSHashEntry *he, **hep; - - keyHash = ht->keyHash(key); - hep = JS_HashTableRawLookup(ht, keyHash, key); - if ((he = *hep) != NULL) { - return he->value; - } - return NULL; -} - -/* -** Iterate over the entries in the hash table calling func for each -** entry found. Stop if "f" says to (return value & JS_ENUMERATE_STOP). -** Return a count of the number of elements scanned. -*/ -JS_PUBLIC_API(int) -JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg) -{ - JSHashEntry *he, **hep, **bucket; - uint32 nlimit, n, nbuckets, newlog2; - int rv; - - nlimit = ht->nentries; - n = 0; - for (bucket = ht->buckets; n != nlimit; ++bucket) { - hep = bucket; - while ((he = *hep) != NULL) { - JS_ASSERT(n < nlimit); - rv = f(he, n, arg); - n++; - if (rv & HT_ENUMERATE_REMOVE) { - *hep = he->next; - ht->allocOps->freeEntry(ht->allocPriv, he, HT_FREE_ENTRY); - --ht->nentries; - } else { - hep = &he->next; - } - if (rv & HT_ENUMERATE_STOP) { - goto out; - } - } - } - -out: - /* Shrink table if removal of entries made it underloaded */ - if (ht->nentries != nlimit) { - JS_ASSERT(ht->nentries < nlimit); - nbuckets = NBUCKETS(ht); - if (MINBUCKETS < nbuckets && ht->nentries < UNDERLOADED(nbuckets)) { - newlog2 = JS_CeilingLog2(ht->nentries); - if (newlog2 < MINBUCKETSLOG2) - newlog2 = MINBUCKETSLOG2; - - /* Check that we really shrink the table. */ - JS_ASSERT(JS_HASH_BITS - ht->shift > newlog2); - Resize(ht, JS_HASH_BITS - newlog2); - } - } - return (int)n; -} - -#ifdef HASHMETER -#include -#include - -JS_PUBLIC_API(void) -JS_HashTableDumpMeter(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) -{ - double sqsum, mean, variance, sigma; - uint32 nchains, nbuckets, nentries; - uint32 i, n, maxChain, maxChainLen; - JSHashEntry *he; - - sqsum = 0; - nchains = 0; - maxChainLen = 0; - nbuckets = NBUCKETS(ht); - for (i = 0; i < nbuckets; i++) { - he = ht->buckets[i]; - if (!he) - continue; - nchains++; - for (n = 0; he; he = he->next) - n++; - sqsum += n * n; - if (n > maxChainLen) { - maxChainLen = n; - maxChain = i; - } - } - nentries = ht->nentries; - mean = (double)nentries / nchains; - variance = nchains * sqsum - nentries * nentries; - if (variance < 0 || nchains == 1) - variance = 0; - else - variance /= nchains * (nchains - 1); - sigma = sqrt(variance); - - fprintf(fp, "\nHash table statistics:\n"); - fprintf(fp, " number of lookups: %u\n", ht->nlookups); - fprintf(fp, " number of entries: %u\n", ht->nentries); - fprintf(fp, " number of grows: %u\n", ht->ngrows); - fprintf(fp, " number of shrinks: %u\n", ht->nshrinks); - fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps - / ht->nlookups); - fprintf(fp, "mean hash chain length: %g\n", mean); - fprintf(fp, " standard deviation: %g\n", sigma); - fprintf(fp, " max hash chain length: %u\n", maxChainLen); - fprintf(fp, " max hash chain: [%u]\n", maxChain); - - for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++) - if (dump(he, i, fp) != HT_ENUMERATE_NEXT) - break; -} -#endif /* HASHMETER */ - -JS_PUBLIC_API(int) -JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp) -{ - int count; - - count = JS_HashTableEnumerateEntries(ht, dump, fp); -#ifdef HASHMETER - JS_HashTableDumpMeter(ht, dump, fp); -#endif - return count; -} - -JS_PUBLIC_API(JSHashNumber) -JS_HashString(const void *key) -{ - JSHashNumber h; - const unsigned char *s; - - h = 0; - for (s = (const unsigned char *)key; *s; s++) - h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; - return h; -} - -JS_PUBLIC_API(int) -JS_CompareValues(const void *v1, const void *v2) -{ - return v1 == v2; -} diff --git a/spidermonkey/libjs/jshash.h b/spidermonkey/libjs/jshash.h deleted file mode 100644 index 2a125e1..0000000 --- a/spidermonkey/libjs/jshash.h +++ /dev/null @@ -1,151 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jshash_h___ -#define jshash_h___ -/* - * API to portable hash table code. - */ -#include -#include -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -typedef uint32 JSHashNumber; -typedef struct JSHashEntry JSHashEntry; -typedef struct JSHashTable JSHashTable; - -#define JS_HASH_BITS 32 -#define JS_GOLDEN_RATIO 0x9E3779B9U - -typedef JSHashNumber (* JS_DLL_CALLBACK JSHashFunction)(const void *key); -typedef intN (* JS_DLL_CALLBACK JSHashComparator)(const void *v1, const void *v2); -typedef intN (* JS_DLL_CALLBACK JSHashEnumerator)(JSHashEntry *he, intN i, void *arg); - -/* Flag bits in JSHashEnumerator's return value */ -#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */ -#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */ -#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */ - -typedef struct JSHashAllocOps { - void * (*allocTable)(void *pool, size_t size); - void (*freeTable)(void *pool, void *item); - JSHashEntry * (*allocEntry)(void *pool, const void *key); - void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag); -} JSHashAllocOps; - -#define HT_FREE_VALUE 0 /* just free the entry's value */ -#define HT_FREE_ENTRY 1 /* free value and entire entry */ - -struct JSHashEntry { - JSHashEntry *next; /* hash chain linkage */ - JSHashNumber keyHash; /* key hash function result */ - const void *key; /* ptr to opaque key */ - void *value; /* ptr to opaque value */ -}; - -struct JSHashTable { - JSHashEntry **buckets; /* vector of hash buckets */ - uint32 nentries; /* number of entries in table */ - uint32 shift; /* multiplicative hash shift */ - JSHashFunction keyHash; /* key hash function */ - JSHashComparator keyCompare; /* key comparison function */ - JSHashComparator valueCompare; /* value comparison function */ - JSHashAllocOps *allocOps; /* allocation operations */ - void *allocPriv; /* allocation private data */ -#ifdef HASHMETER - uint32 nlookups; /* total number of lookups */ - uint32 nsteps; /* number of hash chains traversed */ - uint32 ngrows; /* number of table expansions */ - uint32 nshrinks; /* number of table contractions */ -#endif -}; - -/* - * Create a new hash table. - * If allocOps is null, use default allocator ops built on top of malloc(). - */ -extern JS_PUBLIC_API(JSHashTable *) -JS_NewHashTable(uint32 n, JSHashFunction keyHash, - JSHashComparator keyCompare, JSHashComparator valueCompare, - JSHashAllocOps *allocOps, void *allocPriv); - -extern JS_PUBLIC_API(void) -JS_HashTableDestroy(JSHashTable *ht); - -/* Low level access methods */ -extern JS_PUBLIC_API(JSHashEntry **) -JS_HashTableRawLookup(JSHashTable *ht, JSHashNumber keyHash, const void *key); - -extern JS_PUBLIC_API(JSHashEntry *) -JS_HashTableRawAdd(JSHashTable *ht, JSHashEntry **hep, JSHashNumber keyHash, - const void *key, void *value); - -extern JS_PUBLIC_API(void) -JS_HashTableRawRemove(JSHashTable *ht, JSHashEntry **hep, JSHashEntry *he); - -/* Higher level access methods */ -extern JS_PUBLIC_API(JSHashEntry *) -JS_HashTableAdd(JSHashTable *ht, const void *key, void *value); - -extern JS_PUBLIC_API(JSBool) -JS_HashTableRemove(JSHashTable *ht, const void *key); - -extern JS_PUBLIC_API(intN) -JS_HashTableEnumerateEntries(JSHashTable *ht, JSHashEnumerator f, void *arg); - -extern JS_PUBLIC_API(void *) -JS_HashTableLookup(JSHashTable *ht, const void *key); - -extern JS_PUBLIC_API(intN) -JS_HashTableDump(JSHashTable *ht, JSHashEnumerator dump, FILE *fp); - -/* General-purpose C string hash function. */ -extern JS_PUBLIC_API(JSHashNumber) -JS_HashString(const void *key); - -/* Stub function just returns v1 == v2 */ -extern JS_PUBLIC_API(intN) -JS_CompareValues(const void *v1, const void *v2); - -JS_END_EXTERN_C - -#endif /* jshash_h___ */ diff --git a/spidermonkey/libjs/jsinterp.c b/spidermonkey/libjs/jsinterp.c deleted file mode 100644 index c8c1204..0000000 --- a/spidermonkey/libjs/jsinterp.c +++ /dev/null @@ -1,6216 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JavaScript bytecode interpreter. - */ -#include "jsstddef.h" -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#ifdef DEBUG -#define ASSERT_CACHE_IS_EMPTY(cache) \ - JS_BEGIN_MACRO \ - JSPropertyCacheEntry *end_, *pce_, entry_; \ - JSPropertyCache *cache_ = (cache); \ - JS_ASSERT(cache_->empty); \ - end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ - for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ - PCE_LOAD(cache_, pce_, entry_); \ - JS_ASSERT(!PCE_OBJECT(entry_)); \ - JS_ASSERT(!PCE_PROPERTY(entry_)); \ - } \ - JS_END_MACRO -#else -#define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) -#endif - -void -js_FlushPropertyCache(JSContext *cx) -{ - JSPropertyCache *cache; - - cache = &cx->runtime->propertyCache; - if (cache->empty) { - ASSERT_CACHE_IS_EMPTY(cache); - return; - } - memset(cache->table, 0, sizeof cache->table); - cache->empty = JS_TRUE; -#ifdef JS_PROPERTY_CACHE_METERING - cache->flushes++; -#endif -} - -void -js_DisablePropertyCache(JSContext *cx) -{ - JS_ASSERT(!cx->runtime->propertyCache.disabled); - cx->runtime->propertyCache.disabled = JS_TRUE; -} - -void -js_EnablePropertyCache(JSContext *cx) -{ - JS_ASSERT(cx->runtime->propertyCache.disabled); - ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); - cx->runtime->propertyCache.disabled = JS_FALSE; -} - -/* - * Stack macros and functions. These all use a local variable, jsval *sp, to - * point to the next free stack slot. SAVE_SP must be called before any call - * to a function that may invoke the interpreter. RESTORE_SP must be called - * only after return from js_Invoke, because only js_Invoke changes fp->sp. - */ -#define PUSH(v) (*sp++ = (v)) -#define POP() (*--sp) -#ifdef DEBUG -#define SAVE_SP(fp) \ - (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ - (fp)->sp = sp) -#else -#define SAVE_SP(fp) ((fp)->sp = sp) -#endif -#define RESTORE_SP(fp) (sp = (fp)->sp) - -/* - * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their - * homes in fp, when calling out of the interpreter loop or threaded code. - * RESTORE_SP_AND_PC copies the other way, to update registers after a call - * to a subroutine that interprets a piece of the current script. - */ -#define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc) -#define RESTORE_SP_AND_PC(fp) (RESTORE_SP(fp), pc = (fp)->pc) - -/* - * Push the generating bytecode's pc onto the parallel pc stack that runs - * depth slots below the operands. - * - * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See - * js_Interpret for these local variables' declarations and uses. - */ -#define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) -#define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) -#define POP_OPND() POP() -#define FETCH_OPND(n) (sp[n]) - -/* - * Push the jsdouble d using sp, depth, and pc from the lexical environment. - * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space - * for it and push a reference. - */ -#define STORE_NUMBER(cx, n, d) \ - JS_BEGIN_MACRO \ - jsint i_; \ - jsval v_; \ - \ - if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ - v_ = INT_TO_JSVAL(i_); \ - } else { \ - ok = js_NewDoubleValue(cx, d, &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define STORE_INT(cx, n, i) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - if (INT_FITS_IN_JSVAL(i)) { \ - v_ = INT_TO_JSVAL(i); \ - } else { \ - ok = js_NewDoubleValue(cx, (jsdouble)(i), &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define STORE_UINT(cx, n, u) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - if ((u) <= JSVAL_INT_MAX) { \ - v_ = INT_TO_JSVAL(u); \ - } else { \ - ok = js_NewDoubleValue(cx, (jsdouble)(u), &v_); \ - if (!ok) \ - goto out; \ - } \ - STORE_OPND(n, v_); \ - JS_END_MACRO - -#define FETCH_NUMBER(cx, n, d) \ - JS_BEGIN_MACRO \ - jsval v_; \ - \ - v_ = FETCH_OPND(n); \ - VALUE_TO_NUMBER(cx, v_, d); \ - JS_END_MACRO - -#define FETCH_INT(cx, n, i) \ - JS_BEGIN_MACRO \ - jsval v_ = FETCH_OPND(n); \ - if (JSVAL_IS_INT(v_)) { \ - i = JSVAL_TO_INT(v_); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToECMAInt32(cx, v_, &i); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define FETCH_UINT(cx, n, ui) \ - JS_BEGIN_MACRO \ - jsval v_ = FETCH_OPND(n); \ - jsint i_; \ - if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ - ui = (uint32) i_; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToECMAUint32(cx, v_, &ui); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -/* - * Optimized conversion macros that test for the desired type in v before - * homing sp and calling a conversion function. - */ -#define VALUE_TO_NUMBER(cx, v, d) \ - JS_BEGIN_MACRO \ - if (JSVAL_IS_INT(v)) { \ - d = (jsdouble)JSVAL_TO_INT(v); \ - } else if (JSVAL_IS_DOUBLE(v)) { \ - d = *JSVAL_TO_DOUBLE(v); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToNumber(cx, v, &d); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define POP_BOOLEAN(cx, v, b) \ - JS_BEGIN_MACRO \ - v = FETCH_OPND(-1); \ - if (v == JSVAL_NULL) { \ - b = JS_FALSE; \ - } else if (JSVAL_IS_BOOLEAN(v)) { \ - b = JSVAL_TO_BOOLEAN(v); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_ValueToBoolean(cx, v, &b); \ - if (!ok) \ - goto out; \ - } \ - sp--; \ - JS_END_MACRO - -/* - * Convert a primitive string, number or boolean to a corresponding object. - * v must not be an object, null or undefined when using this macro. - */ -#define PRIMITIVE_TO_OBJECT(cx, v, obj) \ - JS_BEGIN_MACRO \ - SAVE_SP(fp); \ - if (JSVAL_IS_STRING(v)) { \ - obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); \ - } else if (JSVAL_IS_INT(v)) { \ - obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); \ - } else if (JSVAL_IS_DOUBLE(v)) { \ - obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); \ - } else { \ - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); \ - obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); \ - } \ - JS_END_MACRO - -#define VALUE_TO_OBJECT(cx, v, obj) \ - JS_BEGIN_MACRO \ - if (!JSVAL_IS_PRIMITIVE(v)) { \ - obj = JSVAL_TO_OBJECT(v); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - obj = js_ValueToNonNullObject(cx, v); \ - if (!obj) { \ - ok = JS_FALSE; \ - goto out; \ - } \ - } \ - JS_END_MACRO - -#define FETCH_OBJECT(cx, n, v, obj) \ - JS_BEGIN_MACRO \ - v = FETCH_OPND(n); \ - VALUE_TO_OBJECT(cx, v, obj); \ - STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ - JS_END_MACRO - -#define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ - JS_BEGIN_MACRO \ - if (JSVAL_IS_PRIMITIVE(v)) { \ - *vp = v; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -JS_FRIEND_API(jsval *) -js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) -{ - jsval *sp; - - if (markp) - *markp = JS_ARENA_MARK(&cx->stackPool); - JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); - if (!sp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, - (cx->fp && cx->fp->fun) - ? JS_GetFunctionName(cx->fp->fun) - : "script"); - } - return sp; -} - -JS_FRIEND_API(void) -js_FreeRawStack(JSContext *cx, void *mark) -{ - JS_ARENA_RELEASE(&cx->stackPool, mark); -} - -JS_FRIEND_API(jsval *) -js_AllocStack(JSContext *cx, uintN nslots, void **markp) -{ - jsval *sp, *vp, *end; - JSArena *a; - JSStackHeader *sh; - JSStackFrame *fp; - - /* Callers don't check for zero nslots: we do to avoid empty segments. */ - if (nslots == 0) { - *markp = NULL; - return JS_ARENA_MARK(&cx->stackPool); - } - - /* Allocate 2 extra slots for the stack segment header we'll likely need. */ - sp = js_AllocRawStack(cx, 2 + nslots, markp); - if (!sp) - return NULL; - - /* Try to avoid another header if we can piggyback on the last segment. */ - a = cx->stackPool.current; - sh = cx->stackHeaders; - if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) { - /* Extend the last stack segment, give back the 2 header slots. */ - sh->nslots += nslots; - a->avail -= 2 * sizeof(jsval); - } else { - /* - * Need a new stack segment, so we must initialize unused slots in the - * current frame. See js_GC, just before marking the "operand" jsvals, - * where we scan from fp->spbase to fp->sp or through fp->script->depth - * (whichever covers fewer slots). - */ - fp = cx->fp; - if (fp && fp->script && fp->spbase) { -#ifdef DEBUG - jsuword depthdiff = fp->script->depth * sizeof(jsval); - JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); - JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff); -#endif - end = fp->spbase + fp->script->depth; - for (vp = fp->sp; vp < end; vp++) - *vp = JSVAL_VOID; - } - - /* Allocate and push a stack segment header from the 2 extra slots. */ - sh = (JSStackHeader *)sp; - sh->nslots = nslots; - sh->down = cx->stackHeaders; - cx->stackHeaders = sh; - sp += 2; - } - - /* - * Store JSVAL_NULL using memset, to let compilers optimize as they see - * fit, in case a caller allocates and pushes GC-things one by one, which - * could nest a last-ditch GC that will scan this segment. - */ - memset(sp, 0, nslots * sizeof(jsval)); - return sp; -} - -JS_FRIEND_API(void) -js_FreeStack(JSContext *cx, void *mark) -{ - JSStackHeader *sh; - jsuword slotdiff; - - /* Check for zero nslots allocation special case. */ - if (!mark) - return; - - /* We can assert because js_FreeStack always balances js_AllocStack. */ - sh = cx->stackHeaders; - JS_ASSERT(sh); - - /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ - slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); - if (slotdiff < (jsuword)sh->nslots) - sh->nslots = slotdiff; - else - cx->stackHeaders = sh->down; - - /* Release the stackPool space allocated since mark was set. */ - JS_ARENA_RELEASE(&cx->stackPool, mark); -} - -JSBool -js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSBool -js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSBool -js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSBool -js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -JSObject * -js_GetScopeChain(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj, *cursor, *clonedChild, *parent; - JSTempValueRooter tvr; - - obj = fp->blockChain; - if (!obj) { - /* - * Don't force a call object for a lightweight function call, but do - * insist that there is a call object for a heavyweight function call. - */ - JS_ASSERT(!fp->fun || - !(fp->fun->flags & JSFUN_HEAVYWEIGHT) || - fp->callobj); - JS_ASSERT(fp->scopeChain); - return fp->scopeChain; - } - - /* - * We have one or more lexical scopes to reflect into fp->scopeChain, so - * make sure there's a call object at the current head of the scope chain, - * if this frame is a call frame. - */ - if (fp->fun && !fp->callobj) { - JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass || - JS_GetPrivate(cx, fp->scopeChain) != fp); - if (!js_GetCallObject(cx, fp, fp->scopeChain)) - return NULL; - } - - /* - * Clone the block chain. To avoid recursive cloning we set the parent of - * the cloned child after we clone the parent. In the following loop when - * clonedChild is null it indicates the first iteration when no special GC - * rooting is necessary. On the second and the following iterations we - * have to protect cloned so far chain against the GC during cloning of - * the cursor object. - */ - cursor = obj; - clonedChild = NULL; - for (;;) { - parent = OBJ_GET_PARENT(cx, cursor); - - /* - * We pass fp->scopeChain and not null even if we override the parent - * slot later as null triggers useless calculations of slot's value in - * js_NewObject that js_CloneBlockObject calls. - */ - cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp); - if (!cursor) { - if (clonedChild) - JS_POP_TEMP_ROOT(cx, &tvr); - return NULL; - } - if (!clonedChild) { - /* - * The first iteration. Check if other follow and root obj if so - * to protect the whole cloned chain against GC. - */ - obj = cursor; - if (!parent) - break; - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - } else { - /* - * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to - * other threads. - */ - clonedChild->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(cursor); - if (!parent) { - JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj)); - JS_POP_TEMP_ROOT(cx, &tvr); - break; - } - } - clonedChild = cursor; - cursor = parent; - } - fp->flags |= JSFRAME_POP_BLOCKS; - fp->scopeChain = obj; - fp->blockChain = NULL; - return obj; -} - -/* - * Walk the scope chain looking for block scopes whose locals need to be - * copied from stack slots into object slots before fp goes away. - */ -static JSBool -PutBlockObjects(JSContext *cx, JSStackFrame *fp) -{ - JSBool ok; - JSObject *obj; - - ok = JS_TRUE; - for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - if (JS_GetPrivate(cx, obj) != fp) - break; - ok &= js_PutBlockObject(cx, obj); - } - } - return ok; -} - -JSObject * -js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv) -{ - if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { - /* Some objects (e.g., With) delegate 'this' to another object. */ - thisp = OBJ_THIS_OBJECT(cx, thisp); - if (!thisp) - return NULL; - } else { - /* - * ECMA requires "the global object", but in the presence of multiple - * top-level objects (windows, frames, or certain layers in the client - * object model), we prefer fun's parent. An example that causes this - * code to run: - * - * // in window w1 - * function f() { return this } - * function g() { return f } - * - * // in window w2 - * var h = w1.g() - * alert(h() == w1) - * - * The alert should display "true". - */ - if (JSVAL_IS_PRIMITIVE(argv[-2]) || - !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]))) { - thisp = cx->globalObject; - } else { - jsid id; - jsval v; - uintN attrs; - - /* Walk up the parent chain. */ - thisp = JSVAL_TO_OBJECT(argv[-2]); - id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); - for (;;) { - if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs)) - return NULL; - if (JSVAL_IS_VOID(v)) - v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); - if (JSVAL_IS_NULL(v)) - break; - thisp = JSVAL_TO_OBJECT(v); - } - } - } - argv[-1] = OBJECT_TO_JSVAL(thisp); - return thisp; -} - -#if JS_HAS_NO_SUCH_METHOD - -static JSBool -NoSuchMethod(JSContext *cx, JSStackFrame *fp, jsval *vp, uint32 flags, - uintN argc) -{ - JSObject *thisp, *argsobj; - jsval *sp, roots[3]; - JSTempValueRooter tvr; - jsid id; - JSBool ok; - jsbytecode *pc; - jsatomid atomIndex; - - /* - * We must call js_ComputeThis here to censor Call objects. A performance - * hit, since we'll call it again in the normal sequence of invoke events, - * but at least it's idempotent. - * - * Normally, we call ComputeThis after all frame members have been set, - * and in particular, after any revision of the callee value at *vp due - * to clasp->convert (see below). This matters because ComputeThis may - * access *vp via fp->argv[-2], to follow the parent chain to a global - * object to use as the 'this' parameter. - * - * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be any - * such defaulting of 'this' to callee (v, *vp) ancestor. - */ - JS_ASSERT(JSVAL_IS_PRIMITIVE(vp[0])); - RESTORE_SP(fp); - if (JSVAL_IS_OBJECT(vp[1])) { - thisp = JSVAL_TO_OBJECT(vp[1]); - } else { - PRIMITIVE_TO_OBJECT(cx, vp[1], thisp); - if (!thisp) - return JS_FALSE; - vp[1] = OBJECT_TO_JSVAL(thisp); - } - thisp = js_ComputeThis(cx, thisp, vp + 2); - if (!thisp) - return JS_FALSE; - vp[1] = OBJECT_TO_JSVAL(thisp); - - /* From here on, control must flow through label out: to return. */ - memset(roots, 0, sizeof roots); - JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); - - id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, thisp)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) thisp->map->ops; - thisp = ops->getMethod(cx, thisp, id, &roots[2]); - if (!thisp) { - ok = JS_FALSE; - goto out; - } - vp[1] = OBJECT_TO_JSVAL(thisp); - } else -#endif - { - ok = OBJ_GET_PROPERTY(cx, thisp, id, &roots[2]); - if (!ok) - goto out; - } - if (JSVAL_IS_PRIMITIVE(roots[2])) - goto not_function; - - pc = (jsbytecode *) vp[-(intN)fp->script->depth]; - switch ((JSOp) *pc) { - case JSOP_NAME: - case JSOP_GETPROP: -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: -#endif - atomIndex = GET_ATOM_INDEX(pc); - roots[0] = ATOM_KEY(js_GetAtom(cx, &fp->script->atomMap, atomIndex)); - argsobj = js_NewArrayObject(cx, argc, vp + 2); - if (!argsobj) { - ok = JS_FALSE; - goto out; - } - roots[1] = OBJECT_TO_JSVAL(argsobj); - ok = js_InternalInvoke(cx, thisp, roots[2], flags | JSINVOKE_INTERNAL, - 2, roots, &vp[0]); - break; - - default: - goto not_function; - } - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - - not_function: - js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); - ok = JS_FALSE; - goto out; -} - -#endif /* JS_HAS_NO_SUCH_METHOD */ - -#ifdef DUMP_CALL_TABLE - -#include "jsclist.h" -#include "jshash.h" -#include "jsdtoa.h" - -typedef struct CallKey { - jsval callee; /* callee value */ - const char *filename; /* function filename or null */ - uintN lineno; /* function lineno or 0 */ -} CallKey; - -/* Compensate for typeof null == "object" brain damage. */ -#define JSTYPE_NULL JSTYPE_LIMIT -#define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) -#define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t]) -#define NTYPEHIST (JSTYPE_LIMIT + 1) - -typedef struct CallValue { - uint32 total; /* total call count */ - uint32 recycled; /* LRU-recycled calls lost */ - uint16 minargc; /* minimum argument count */ - uint16 maxargc; /* maximum argument count */ - struct ArgInfo { - uint32 typeHist[NTYPEHIST]; /* histogram by type */ - JSCList lruList; /* top 10 values LRU list */ - struct ArgValCount { - JSCList lruLink; /* LRU list linkage */ - jsval value; /* recently passed value */ - uint32 count; /* number of times passed */ - char strbuf[112]; /* string conversion buffer */ - } topValCounts[10]; /* top 10 value storage */ - } argInfo[8]; -} CallValue; - -typedef struct CallEntry { - JSHashEntry entry; - CallKey key; - CallValue value; - char name[32]; /* function name copy */ -} CallEntry; - -static void * -AllocCallTable(void *pool, size_t size) -{ - return malloc(size); -} - -static void -FreeCallTable(void *pool, void *item) -{ - free(item); -} - -static JSHashEntry * -AllocCallEntry(void *pool, const void *key) -{ - return (JSHashEntry*) calloc(1, sizeof(CallEntry)); -} - -static void -FreeCallEntry(void *pool, JSHashEntry *he, uintN flag) -{ - JS_ASSERT(flag == HT_FREE_ENTRY); - free(he); -} - -static JSHashAllocOps callTableAllocOps = { - AllocCallTable, FreeCallTable, - AllocCallEntry, FreeCallEntry -}; - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_call_key(const void *key) -{ - CallKey *ck = (CallKey *) key; - JSHashNumber hash = (jsuword)ck->callee >> 3; - - if (ck->filename) { - hash = (hash << 4) ^ JS_HashString(ck->filename); - hash = (hash << 4) ^ ck->lineno; - } - return hash; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_compare_call_keys(const void *k1, const void *k2) -{ - CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2; - - return ck1->callee == ck2->callee && - ((ck1->filename && ck2->filename) - ? strcmp(ck1->filename, ck2->filename) == 0 - : ck1->filename == ck2->filename) && - ck1->lineno == ck2->lineno; -} - -JSHashTable *js_CallTable; -size_t js_LogCallToSourceLimit; - -JS_STATIC_DLL_CALLBACK(intN) -CallTableDumper(JSHashEntry *he, intN k, void *arg) -{ - CallEntry *ce = (CallEntry *)he; - FILE *fp = (FILE *)arg; - uintN argc, i, n; - struct ArgInfo *ai; - JSType save, type; - JSCList *cl; - struct ArgValCount *avc; - jsval argval; - - if (ce->key.filename) { - /* We're called at the end of the mark phase, so mark our filenames. */ - js_MarkScriptFilename(ce->key.filename); - fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno); - } else { - fprintf(fp, "@%p ", (void *) ce->key.callee); - } - - if (ce->name[0]) - fprintf(fp, "name %s ", ce->name); - fprintf(fp, "calls %lu (%lu) argc %u/%u\n", - (unsigned long) ce->value.total, - (unsigned long) ce->value.recycled, - ce->value.minargc, ce->value.maxargc); - - argc = JS_MIN(ce->value.maxargc, 8); - for (i = 0; i < argc; i++) { - ai = &ce->value.argInfo[i]; - - n = 0; - save = -1; - for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { - if (ai->typeHist[type]) { - save = type; - ++n; - } - } - if (n == 1) { - fprintf(fp, " arg %u type %s: %lu\n", - i, TYPENAME(save), (unsigned long) ai->typeHist[save]); - } else { - fprintf(fp, " arg %u type histogram:\n", i); - for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { - fprintf(fp, " %9s: %8lu ", - TYPENAME(type), (unsigned long) ai->typeHist[type]); - for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n) - fputc('*', fp); - fputc('\n', fp); - } - } - - fprintf(fp, " arg %u top 10 values:\n", i); - n = 1; - for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) { - avc = (struct ArgValCount *)cl; - if (!avc->count) - break; - argval = avc->value; - fprintf(fp, " %9u: %8lu %.*s (%#lx)\n", - n, (unsigned long) avc->count, - sizeof avc->strbuf, avc->strbuf, argval); - ++n; - } - } - - return HT_ENUMERATE_NEXT; -} - -void -js_DumpCallTable(JSContext *cx) -{ - char name[24]; - FILE *fp; - static uintN dumpCount; - - if (!js_CallTable) - return; - - JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7); - dumpCount++; - fp = fopen(name, "w"); - if (!fp) - return; - - JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp); - fclose(fp); -} - -static void -LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) -{ - CallKey key; - const char *name, *cstr; - JSFunction *fun; - JSHashNumber keyHash; - JSHashEntry **hep, *he; - CallEntry *ce; - uintN i, j; - jsval argval; - JSType type; - struct ArgInfo *ai; - struct ArgValCount *avc; - JSString *str; - - if (!js_CallTable) { - js_CallTable = JS_NewHashTable(1024, js_hash_call_key, - js_compare_call_keys, NULL, - &callTableAllocOps, NULL); - if (!js_CallTable) - return; - } - - key.callee = callee; - key.filename = NULL; - key.lineno = 0; - name = ""; - if (VALUE_IS_FUNCTION(cx, callee)) { - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); - if (fun->atom) - name = js_AtomToPrintableString(cx, fun->atom); - if (FUN_INTERPRETED(fun)) { - key.filename = fun->u.i.script->filename; - key.lineno = fun->u.i.script->lineno; - } - } - keyHash = js_hash_call_key(&key); - - hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key); - he = *hep; - if (he) { - ce = (CallEntry *) he; - JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0); - } else { - he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL); - if (!he) - return; - ce = (CallEntry *) he; - ce->entry.key = &ce->key; - ce->entry.value = &ce->value; - ce->key = key; - for (i = 0; i < 8; i++) { - ai = &ce->value.argInfo[i]; - JS_INIT_CLIST(&ai->lruList); - for (j = 0; j < 10; j++) - JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList); - } - strncpy(ce->name, name, sizeof ce->name); - } - - ++ce->value.total; - if (ce->value.minargc < argc) - ce->value.minargc = argc; - if (ce->value.maxargc < argc) - ce->value.maxargc = argc; - if (argc > 8) - argc = 8; - for (i = 0; i < argc; i++) { - ai = &ce->value.argInfo[i]; - argval = argv[i]; - type = TYPEOF(cx, argval); - ++ai->typeHist[type]; - - for (j = 0; ; j++) { - if (j == 10) { - avc = (struct ArgValCount *) ai->lruList.next; - ce->value.recycled += avc->count; - avc->value = argval; - avc->count = 1; - break; - } - avc = &ai->topValCounts[j]; - if (avc->value == argval) { - ++avc->count; - break; - } - } - - /* Move avc to the back of the LRU list. */ - JS_REMOVE_LINK(&avc->lruLink); - JS_APPEND_LINK(&avc->lruLink, &ai->lruList); - - str = NULL; - cstr = ""; - switch (TYPEOF(cx, argval)) { - case JSTYPE_VOID: - cstr = js_type_str[JSTYPE_VOID]; - break; - case JSTYPE_NULL: - cstr = js_null_str; - break; - case JSTYPE_BOOLEAN: - cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)]; - break; - case JSTYPE_NUMBER: - if (JSVAL_IS_INT(argval)) { - JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld", - JSVAL_TO_INT(argval)); - } else { - JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0, - *JSVAL_TO_DOUBLE(argval)); - } - continue; - case JSTYPE_STRING: - str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); - break; - case JSTYPE_FUNCTION: - if (VALUE_IS_FUNCTION(cx, argval)) { - fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); - if (fun && fun->atom) { - str = ATOM_TO_STRING(fun->atom); - break; - } - } - /* FALL THROUGH */ - case JSTYPE_OBJECT: - js_LogCallToSourceLimit = sizeof avc->strbuf; - cx->options |= JSOPTION_LOGCALL_TOSOURCE; - str = js_ValueToSource(cx, argval); - cx->options &= ~JSOPTION_LOGCALL_TOSOURCE; - break; - } - if (str) - cstr = JS_GetStringBytes(str); - strncpy(avc->strbuf, cstr, sizeof avc->strbuf); - } -} - -#endif /* DUMP_CALL_TABLE */ - -/* - * Conditional assert to detect failure to clear a pending exception that is - * suppressed (or unintentional suppression of a wanted exception). - */ -#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_shaver -# define DEBUG_NOT_THROWING 1 -#endif - -#ifdef DEBUG_NOT_THROWING -# define ASSERT_NOT_THROWING(cx) JS_ASSERT(!(cx)->throwing) -#else -# define ASSERT_NOT_THROWING(cx) /* nothing */ -#endif - -/* - * Find a function reference and its 'this' object implicit first parameter - * under argc arguments on cx's stack, and call the function. Push missing - * required arguments, allocate declared local variables, and pop everything - * when done. Then push the return value. - */ -JS_FRIEND_API(JSBool) -js_Invoke(JSContext *cx, uintN argc, uintN flags) -{ - void *mark; - JSStackFrame *fp, frame; - jsval *sp, *newsp, *limit; - jsval *vp, v, thisv; - JSObject *funobj, *parent, *thisp; - JSBool ok; - JSClass *clasp; - JSObjectOps *ops; - JSNative native; - JSFunction *fun; - JSScript *script; - uintN nslots, nvars, nalloc, surplus; - JSInterpreterHook hook; - void *hookData; - - /* Mark the top of stack and load frequently-used registers. */ - mark = JS_ARENA_MARK(&cx->stackPool); - fp = cx->fp; - sp = fp->sp; - - /* - * Set vp to the callee value's stack slot (it's where rval goes). - * Once vp is set, control should flow through label out2: to return. - * Set frame.rval early so native class and object ops can throw and - * return false, causing a goto out2 with ok set to false. - */ - vp = sp - (2 + argc); - v = *vp; - frame.rval = JSVAL_VOID; - - /* - * A callee must be an object reference, unless its 'this' parameter - * implements the __noSuchMethod__ method, in which case that method will - * be called like so: - * - * thisp.__noSuchMethod__(id, args) - * - * where id is the name of the method that this invocation attempted to - * call by name, and args is an Array containing this invocation's actual - * parameters. - */ - if (JSVAL_IS_PRIMITIVE(v)) { -#if JS_HAS_NO_SUCH_METHOD - if (fp->script && !(flags & JSINVOKE_INTERNAL)) { - ok = NoSuchMethod(cx, fp, vp, flags, argc); - if (ok) - frame.rval = *vp; - goto out2; - } -#endif - goto bad; - } - - /* Load thisv after potentially calling NoSuchMethod, which may set it. */ - thisv = vp[1]; - - funobj = JSVAL_TO_OBJECT(v); - parent = OBJ_GET_PARENT(cx, funobj); - clasp = OBJ_GET_CLASS(cx, funobj); - if (clasp != &js_FunctionClass) { - /* Function is inlined, all other classes use object ops. */ - ops = funobj->map->ops; - - /* - * XXX this makes no sense -- why convert to function if clasp->call? - * XXX better to call that hook without converting - * XXX the only thing that needs fixing is liveconnect - * - * Try converting to function, for closure and API compatibility. - * We attempt the conversion under all circumstances for 1.2, but - * only if there is a call op defined otherwise. - */ - if ((ops == &js_ObjectOps) ? clasp->call : ops->call) { - ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); - if (!ok) - goto out2; - - if (VALUE_IS_FUNCTION(cx, v)) { - /* Make vp refer to funobj to keep it available as argv[-2]. */ - *vp = v; - funobj = JSVAL_TO_OBJECT(v); - parent = OBJ_GET_PARENT(cx, funobj); - goto have_fun; - } - } - fun = NULL; - script = NULL; - nslots = nvars = 0; - - /* Try a call or construct native object op. */ - native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; - if (!native) - goto bad; - - if (JSVAL_IS_OBJECT(thisv)) { - thisp = JSVAL_TO_OBJECT(thisv); - } else { - PRIMITIVE_TO_OBJECT(cx, thisv, thisp); - if (!thisp) - goto out2; - vp[1] = thisv = OBJECT_TO_JSVAL(thisp); - } - } else { -have_fun: - /* Get private data and set derived locals from it. */ - fun = (JSFunction *) JS_GetPrivate(cx, funobj); - nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; - if (FUN_INTERPRETED(fun)) { - native = NULL; - script = fun->u.i.script; - nvars = fun->u.i.nvars; - } else { - native = fun->u.n.native; - script = NULL; - nvars = 0; - nslots += fun->u.n.extra; - } - - if (JSFUN_BOUND_METHOD_TEST(fun->flags)) { - /* Handle bound method special case. */ - thisp = parent; - } else if (JSVAL_IS_OBJECT(thisv)) { - thisp = JSVAL_TO_OBJECT(thisv); - } else { - uintN thispflags = JSFUN_THISP_FLAGS(fun->flags); - - JS_ASSERT(!(flags & JSINVOKE_CONSTRUCT)); - if (JSVAL_IS_STRING(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_STRING)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_StringToObject(cx, JSVAL_TO_STRING(thisv)); - } else if (JSVAL_IS_INT(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(thisv)); - } else if (JSVAL_IS_DOUBLE(thisv)) { - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_NUMBER)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(thisv)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(thisv)); - if (JSFUN_THISP_TEST(thispflags, JSFUN_THISP_BOOLEAN)) { - thisp = (JSObject *) thisv; - goto init_frame; - } - thisp = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(thisv)); - } - if (!thisp) { - ok = JS_FALSE; - goto out2; - } - goto init_frame; - } - } - - if (flags & JSINVOKE_CONSTRUCT) { - /* Default return value for a constructor is the new object. */ - frame.rval = OBJECT_TO_JSVAL(thisp); - } else { - thisp = js_ComputeThis(cx, thisp, vp + 2); - if (!thisp) { - ok = JS_FALSE; - goto out2; - } - } - - init_frame: - /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ - frame.thisp = thisp; - frame.varobj = NULL; - frame.callobj = frame.argsobj = NULL; - frame.script = script; - frame.fun = fun; - frame.argc = argc; - frame.argv = sp - argc; - frame.nvars = nvars; - frame.vars = sp; - frame.down = fp; - frame.annotation = NULL; - frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ - frame.pc = NULL; - frame.spbase = NULL; - frame.sharpDepth = 0; - frame.sharpArray = NULL; - frame.flags = flags; - frame.dormantNext = NULL; - frame.xmlNamespace = NULL; - frame.blockChain = NULL; - - /* From here on, control must flow through label out: to return. */ - cx->fp = &frame; - - /* Init these now in case we goto out before first hook call. */ - hook = cx->runtime->callHook; - hookData = NULL; - - /* Check for argument slots required by the function. */ - if (nslots) { - /* All arguments must be contiguous, so we may have to copy actuals. */ - nalloc = nslots; - limit = (jsval *) cx->stackPool.current->limit; - JS_ASSERT((jsval *) cx->stackPool.current->base <= sp && sp <= limit); - if (sp + nslots > limit) { - /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ - nalloc += 2 + argc; - } else { - /* Take advantage of surplus slots in the caller's frame depth. */ - JS_ASSERT((jsval *)mark >= sp); - surplus = (jsval *)mark - sp; - nalloc -= surplus; - } - - /* Check whether we have enough space in the caller's frame. */ - if ((intN)nalloc > 0) { - /* Need space for actuals plus missing formals minus surplus. */ - newsp = js_AllocRawStack(cx, nalloc, NULL); - if (!newsp) { - ok = JS_FALSE; - goto out; - } - - /* If we couldn't allocate contiguous args, copy actuals now. */ - if (newsp != mark) { - JS_ASSERT(sp + nslots > limit); - JS_ASSERT(2 + argc + nslots == nalloc); - *newsp++ = vp[0]; - *newsp++ = vp[1]; - if (argc) - memcpy(newsp, frame.argv, argc * sizeof(jsval)); - frame.argv = newsp; - sp = frame.vars = newsp + argc; - } - } - - /* Advance frame.vars to make room for the missing args. */ - frame.vars += nslots; - - /* Push void to initialize missing args. */ - do { - PUSH(JSVAL_VOID); - } while (--nslots != 0); - } - JS_ASSERT(nslots == 0); - - /* Now allocate stack space for local variables. */ - if (nvars) { - JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars); - surplus = (jsval *)cx->stackPool.current->avail - frame.vars; - if (surplus < nvars) { - newsp = js_AllocRawStack(cx, nvars, NULL); - if (!newsp) { - ok = JS_FALSE; - goto out; - } - if (newsp != sp) { - /* NB: Discontinuity between argv and vars. */ - sp = frame.vars = newsp; - } - } - - /* Push void to initialize local variables. */ - do { - PUSH(JSVAL_VOID); - } while (--nvars != 0); - } - JS_ASSERT(nvars == 0); - - /* Store the current sp in frame before calling fun. */ - SAVE_SP(&frame); - - /* call the hook if present */ - if (hook && (native || script)) - hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData); - - /* Call the function, either a native method or an interpreted script. */ - if (native) { -#ifdef DEBUG_NOT_THROWING - JSBool alreadyThrowing = cx->throwing; -#endif - -#if JS_HAS_LVALUE_RETURN - /* Set by JS_SetCallReturnValue2, used to return reference types. */ - cx->rval2set = JS_FALSE; -#endif - - /* If native, use caller varobj and scopeChain for eval. */ - frame.varobj = fp->varobj; - frame.scopeChain = fp->scopeChain; - ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); - JS_RUNTIME_METER(cx->runtime, nativeCalls); -#ifdef DEBUG_NOT_THROWING - if (ok && !alreadyThrowing) - ASSERT_NOT_THROWING(cx); -#endif - } else if (script) { -#ifdef DUMP_CALL_TABLE - LogCall(cx, *vp, argc, frame.argv); -#endif - /* Use parent scope so js_GetCallObject can find the right "Call". */ - frame.scopeChain = parent; - if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) { - /* Scope with a call object parented by the callee's parent. */ - if (!js_GetCallObject(cx, &frame, parent)) { - ok = JS_FALSE; - goto out; - } - } - ok = js_Interpret(cx, script->code, &v); - } else { - /* fun might be onerror trying to report a syntax error in itself. */ - frame.scopeChain = NULL; - ok = JS_TRUE; - } - -out: - if (hookData) { - hook = cx->runtime->callHook; - if (hook) - hook(cx, &frame, JS_FALSE, &ok, hookData); - } - - /* If frame has a call object, sync values and clear back-pointer. */ - if (frame.callobj) - ok &= js_PutCallObject(cx, &frame); - - /* If frame has an arguments object, sync values and clear back-pointer. */ - if (frame.argsobj) - ok &= js_PutArgsObject(cx, &frame); - - /* Restore cx->fp now that we're done releasing frame objects. */ - cx->fp = fp; - -out2: - /* Pop everything we may have allocated off the stack. */ - JS_ARENA_RELEASE(&cx->stackPool, mark); - - /* Store the return value and restore sp just above it. */ - *vp = frame.rval; - fp->sp = vp + 1; - - /* - * Store the location of the JSOP_CALL or JSOP_EVAL that generated the - * return value, but only if this is an external (compiled from script - * source) call that has stack budget for the generating pc. - */ - if (fp->script && !(flags & JSINVOKE_INTERNAL)) - vp[-(intN)fp->script->depth] = (jsval)fp->pc; - return ok; - -bad: - js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS); - ok = JS_FALSE; - goto out2; -} - -JSBool -js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, - uintN argc, jsval *argv, jsval *rval) -{ - JSStackFrame *fp, *oldfp, frame; - jsval *oldsp, *sp; - void *mark; - uintN i; - JSBool ok; - - fp = oldfp = cx->fp; - if (!fp) { - memset(&frame, 0, sizeof frame); - cx->fp = fp = &frame; - } - oldsp = fp->sp; - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) { - ok = JS_FALSE; - goto out; - } - - PUSH(fval); - PUSH(OBJECT_TO_JSVAL(obj)); - for (i = 0; i < argc; i++) - PUSH(argv[i]); - SAVE_SP(fp); - ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); - if (ok) { - RESTORE_SP(fp); - - /* - * Store *rval in the a scoped local root if a scope is open, else in - * the lastInternalResult pigeon-hole GC root, solely so users of - * js_InternalInvoke and its direct and indirect (js_ValueToString for - * example) callers do not need to manage roots for local, temporary - * references to such results. - */ - *rval = POP_OPND(); - if (JSVAL_IS_GCTHING(*rval)) { - if (cx->localRootStack) { - if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) - ok = JS_FALSE; - } else { - cx->weakRoots.lastInternalResult = *rval; - } - } - } - - js_FreeStack(cx, mark); -out: - fp->sp = oldsp; - if (oldfp != fp) - cx->fp = oldfp; - - return ok; -} - -JSBool -js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, - JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) -{ - int stackDummy; - - /* - * js_InternalInvoke could result in another try to get or set the same id - * again, see bug 355497. - */ - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OVER_RECURSED); - return JS_FALSE; - } - /* - * Check general (not object-ops/class-specific) access from the running - * script to obj.id only if id has a scripted getter or setter that we're - * about to invoke. If we don't check this case, nothing else will -- no - * other native code has the chance to check. - * - * Contrast this non-native (scripted) case with native getter and setter - * accesses, where the native itself must do an access check, if security - * policies requires it. We make a checkAccess or checkObjectAccess call - * back to the embedding program only in those cases where we're not going - * to call an embedding-defined native function, getter, setter, or class - * hook anyway. Where we do call such a native, there's no need for the - * engine to impose a separate access check callback on all embeddings -- - * many embeddings have no security policy at all. - */ - JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); - if (cx->runtime->checkObjectAccess && - VALUE_IS_FUNCTION(cx, fval) && - FUN_INTERPRETED((JSFunction *) - JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval))) && - !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, - &fval)) { - return JS_FALSE; - } - - return js_InternalCall(cx, obj, fval, argc, argv, rval); -} - -JSBool -js_Execute(JSContext *cx, JSObject *chain, JSScript *script, - JSStackFrame *down, uintN flags, jsval *result) -{ - JSInterpreterHook hook; - void *hookData, *mark; - JSStackFrame *oldfp, frame; - JSObject *obj, *tmp; - JSBool ok; - - hook = cx->runtime->executeHook; - hookData = mark = NULL; - oldfp = cx->fp; - frame.script = script; - if (down) { - /* Propagate arg/var state for eval and the debugger API. */ - frame.callobj = down->callobj; - frame.argsobj = down->argsobj; - frame.varobj = down->varobj; - frame.fun = down->fun; - frame.thisp = down->thisp; - frame.argc = down->argc; - frame.argv = down->argv; - frame.nvars = down->nvars; - frame.vars = down->vars; - frame.annotation = down->annotation; - frame.sharpArray = down->sharpArray; - } else { - frame.callobj = frame.argsobj = NULL; - obj = chain; - if (cx->options & JSOPTION_VAROBJFIX) { - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - } - frame.varobj = obj; - frame.fun = NULL; - frame.thisp = chain; - frame.argc = 0; - frame.argv = NULL; - frame.nvars = script->numGlobalVars; - if (frame.nvars) { - frame.vars = js_AllocRawStack(cx, frame.nvars, &mark); - if (!frame.vars) - return JS_FALSE; - memset(frame.vars, 0, frame.nvars * sizeof(jsval)); - } else { - frame.vars = NULL; - } - frame.annotation = NULL; - frame.sharpArray = NULL; - } - frame.rval = JSVAL_VOID; - frame.down = down; - frame.scopeChain = chain; - frame.pc = NULL; - frame.sp = oldfp ? oldfp->sp : NULL; - frame.spbase = NULL; - frame.sharpDepth = 0; - frame.flags = flags; - frame.dormantNext = NULL; - frame.xmlNamespace = NULL; - frame.blockChain = NULL; - - /* - * Here we wrap the call to js_Interpret with code to (conditionally) - * save and restore the old stack frame chain into a chain of 'dormant' - * frame chains. Since we are replacing cx->fp, we were running into - * the problem that if GC was called under this frame, some of the GC - * things associated with the old frame chain (available here only in - * the C variable 'oldfp') were not rooted and were being collected. - * - * So, now we preserve the links to these 'dormant' frame chains in cx - * before calling js_Interpret and cleanup afterwards. The GC walks - * these dormant chains and marks objects in the same way that it marks - * objects in the primary cx->fp chain. - */ - if (oldfp && oldfp != down) { - JS_ASSERT(!oldfp->dormantNext); - oldfp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = oldfp; - } - - cx->fp = &frame; - if (hook) - hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData); - - /* - * Use frame.rval, not result, so the last result stays rooted across any - * GC activations nested within this js_Interpret. - */ - ok = js_Interpret(cx, script->code, &frame.rval); - *result = frame.rval; - - if (hookData) { - hook = cx->runtime->executeHook; - if (hook) - hook(cx, &frame, JS_FALSE, &ok, hookData); - } - if (mark) - js_FreeRawStack(cx, mark); - cx->fp = oldfp; - - if (oldfp && oldfp != down) { - JS_ASSERT(cx->dormantFrameChain == oldfp); - cx->dormantFrameChain = oldfp->dormantNext; - oldfp->dormantNext = NULL; - } - - return ok; -} - -#if JS_HAS_EXPORT_IMPORT -/* - * If id is JSVAL_VOID, import all exported properties from obj. - */ -static JSBool -ImportProperty(JSContext *cx, JSObject *obj, jsid id) -{ - JSBool ok; - JSIdArray *ida; - JSProperty *prop; - JSObject *obj2, *target, *funobj, *closure; - JSString *str; - uintN attrs; - jsint i; - jsval value; - - if (JSVAL_IS_VOID(id)) { - ida = JS_Enumerate(cx, obj); - if (!ida) - return JS_FALSE; - ok = JS_TRUE; - if (ida->length == 0) - goto out; - } else { - ida = NULL; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (str) - js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); - return JS_FALSE; - } - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - return JS_FALSE; - if (!(attrs & JSPROP_EXPORTED)) { - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NOT_EXPORTED, - JS_GetStringBytes(str)); - } - return JS_FALSE; - } - } - - target = cx->fp->varobj; - i = 0; - do { - if (ida) { - id = ida->vector[i]; - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); - if (!ok) - goto out; - if (!(attrs & JSPROP_EXPORTED)) - continue; - } - ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); - if (!ok) - goto out; - if (VALUE_IS_FUNCTION(cx, value)) { - funobj = JSVAL_TO_OBJECT(value); - closure = js_CloneFunctionObject(cx, funobj, obj); - if (!closure) { - ok = JS_FALSE; - goto out; - } - value = OBJECT_TO_JSVAL(closure); - } - - /* - * Handle the case of importing a property that refers to a local - * variable or formal parameter of a function activation. These - * properties are accessed by opcodes using stack slot numbers - * generated by the compiler rather than runtime name-lookup. These - * local references, therefore, bypass the normal scope chain lookup. - * So, instead of defining a new property in the activation object, - * modify the existing value in the stack slot. - */ - if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { - ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); - if (!ok) - goto out; - } else { - prop = NULL; - } - if (prop && target == obj2) { - ok = OBJ_SET_PROPERTY(cx, target, id, &value); - } else { - ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, - attrs & ~(JSPROP_EXPORTED | - JSPROP_GETTER | - JSPROP_SETTER), - NULL); - } - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - goto out; - } while (ida && ++i < ida->length); - -out: - if (ida) - JS_DestroyIdArray(cx, ida); - return ok; -} -#endif /* JS_HAS_EXPORT_IMPORT */ - -JSBool -js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, - JSObject **objp, JSProperty **propp) -{ - JSObject *obj2; - JSProperty *prop; - uintN oldAttrs, report; - JSBool isFunction; - jsval value; - const char *type, *name; - - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (propp) { - *objp = obj2; - *propp = prop; - } - if (!prop) - return JS_TRUE; - - /* - * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error. - * An assertion at label bad: will insist that it is null. - */ - if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); -#ifdef DEBUG - prop = NULL; -#endif - goto bad; - } - - /* - * From here, return true, or else goto bad on failure to null out params. - * If our caller doesn't want prop, drop it (we don't need it any longer). - */ - if (!propp) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - prop = NULL; - } - - /* If either property is readonly, we have an error. */ - report = ((oldAttrs | attrs) & JSPROP_READONLY) - ? JSREPORT_ERROR - : JSREPORT_WARNING | JSREPORT_STRICT; - - if (report != JSREPORT_ERROR) { - /* - * Allow redeclaration of variables and functions, but insist that the - * new value is not a getter if the old value was, ditto for setters -- - * unless prop is impermanent (in which case anyone could delete it and - * redefine it, willy-nilly). - */ - if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) - return JS_TRUE; - if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) - return JS_TRUE; - if (!(oldAttrs & JSPROP_PERMANENT)) - return JS_TRUE; - report = JSREPORT_ERROR; - } - - isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; - if (!isFunction) { - if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) - goto bad; - isFunction = VALUE_IS_FUNCTION(cx, value); - } - type = (oldAttrs & attrs & JSPROP_GETTER) - ? js_getter_str - : (oldAttrs & attrs & JSPROP_SETTER) - ? js_setter_str - : (oldAttrs & JSPROP_READONLY) - ? js_const_str - : isFunction - ? js_function_str - : js_var_str; - name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); - if (!name) - goto bad; - return JS_ReportErrorFlagsAndNumber(cx, report, - js_GetErrorMessage, NULL, - JSMSG_REDECLARED_VAR, - type, name); - -bad: - if (propp) { - *objp = NULL; - *propp = NULL; - } - JS_ASSERT(!prop); - return JS_FALSE; -} - -JSBool -js_StrictlyEqual(jsval lval, jsval rval) -{ - jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); - jsdouble ld, rd; - - if (ltag == rtag) { - if (ltag == JSVAL_STRING) { - JSString *lstr = JSVAL_TO_STRING(lval), - *rstr = JSVAL_TO_STRING(rval); - return js_EqualStrings(lstr, rstr); - } - if (ltag == JSVAL_DOUBLE) { - ld = *JSVAL_TO_DOUBLE(lval); - rd = *JSVAL_TO_DOUBLE(rval); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); - } - return lval == rval; - } - if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { - ld = *JSVAL_TO_DOUBLE(lval); - rd = JSVAL_TO_INT(rval); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); - } - if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { - ld = JSVAL_TO_INT(lval); - rd = *JSVAL_TO_DOUBLE(rval); - return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); - } - return lval == rval; -} - -JSBool -js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc) -{ - JSFunction *fun; - JSObject *obj, *obj2, *proto, *parent; - jsval lval, rval; - JSClass *clasp, *funclasp; - - fun = NULL; - obj2 = NULL; - lval = *vp; - if (!JSVAL_IS_OBJECT(lval) || - (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || - /* XXX clean up to avoid special cases above ObjectOps layer */ - OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || - !obj2->map->ops->construct) - { - fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); - if (!fun) - return JS_FALSE; - } - - clasp = &js_ObjectClass; - if (!obj2) { - proto = parent = NULL; - fun = NULL; - } else { - /* - * Get the constructor prototype object for this function. - * Use the nominal 'this' parameter slot, vp[1], as a local - * root to protect this prototype, in case it has no other - * strong refs. - */ - if (!OBJ_GET_PROPERTY(cx, obj2, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &vp[1])) { - return JS_FALSE; - } - rval = vp[1]; - proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; - parent = OBJ_GET_PARENT(cx, obj2); - - if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { - funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; - if (funclasp) - clasp = funclasp; - } - } - obj = js_NewObject(cx, clasp, proto, parent); - if (!obj) - return JS_FALSE; - - /* Now we have an object with a constructor method; call it. */ - vp[1] = OBJECT_TO_JSVAL(obj); - if (!js_Invoke(cx, argc, JSINVOKE_CONSTRUCT)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return JS_FALSE; - } - - /* Check the return value and if it's primitive, force it to be obj. */ - rval = *vp; - if (JSVAL_IS_PRIMITIVE(rval)) { - if (!fun) { - /* native [[Construct]] returning primitive is error */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_NEW_RESULT, - js_ValueToPrintableString(cx, rval)); - return JS_FALSE; - } - *vp = OBJECT_TO_JSVAL(obj); - } - - JS_RUNTIME_METER(cx->runtime, constructs); - return JS_TRUE; -} - -static JSBool -InternStringElementId(JSContext *cx, jsval idval, jsid *idp) -{ - JSAtom *atom; - - atom = js_ValueToStringAtom(cx, idval); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - return JS_TRUE; -} - -static JSBool -InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) -{ - JS_ASSERT(!JSVAL_IS_INT(idval)); - -#if JS_HAS_XML_SUPPORT - if (JSVAL_IS_OBJECT(idval)) { - *idp = OBJECT_JSVAL_TO_JSID(idval); - return JS_TRUE; - } -#endif - - return InternStringElementId(cx, idval, idp); -} - -#if JS_HAS_XML_SUPPORT -#define CHECK_ELEMENT_ID(obj, id) \ - JS_BEGIN_MACRO \ - if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ - SAVE_SP_AND_PC(fp); \ - ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#else -#define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) -#endif - -#ifndef MAX_INTERP_LEVEL -#if defined(XP_OS2) -#define MAX_INTERP_LEVEL 250 -#else -#define MAX_INTERP_LEVEL 1000 -#endif -#endif - -#define MAX_INLINE_CALL_COUNT 1000 - -/* - * Threaded interpretation via computed goto appears to be well-supported by - * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., - * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler. - * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing. - * Add your compiler support macros here. - */ -#if JS_VERSION >= 160 && ( \ - __GNUC__ >= 3 || \ - (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ - __SUNPRO_C >= 0x570) -# define JS_THREADED_INTERP 1 -#else -# undef JS_THREADED_INTERP -#endif - -JSBool -js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) -{ - JSRuntime *rt; - JSStackFrame *fp; - JSScript *script; - uintN inlineCallCount; - JSObject *obj, *obj2, *parent; - JSVersion currentVersion, originalVersion; - JSBranchCallback onbranch; - JSBool ok, cond; - JSTrapHandler interruptHandler; - jsint depth, len; - jsval *sp, *newsp; - void *mark; - jsbytecode *endpc, *pc2; - JSOp op, op2; - jsatomid atomIndex; - JSAtom *atom; - uintN argc, attrs, flags, slot; - jsval *vp, lval, rval, ltmp, rtmp; - jsid id; - JSObject *withobj, *iterobj; - JSProperty *prop; - JSScopeProperty *sprop; - JSString *str, *str2; - jsint i, j; - jsdouble d, d2; - JSClass *clasp; - JSFunction *fun; - JSType type; -#if !defined JS_THREADED_INTERP && defined DEBUG - FILE *tracefp = NULL; -#endif -#if JS_HAS_EXPORT_IMPORT - JSIdArray *ida; -#endif - jsint low, high, off, npairs; - JSBool match; -#if JS_HAS_GETTER_SETTER - JSPropertyOp getter, setter; -#endif - int stackDummy; - -#ifdef __GNUC__ -# define JS_EXTENSION __extension__ -# define JS_EXTENSION_(s) __extension__ ({ s; }) -#else -# define JS_EXTENSION -# define JS_EXTENSION_(s) s -#endif - -#ifdef JS_THREADED_INTERP - static void *normalJumpTable[] = { -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - JS_EXTENSION &&L_##op, -# include "jsopcode.tbl" -# undef OPDEF - }; - - static void *interruptJumpTable[] = { -# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - ((op != JSOP_PUSHOBJ) \ - ? JS_EXTENSION &&interrupt \ - : JS_EXTENSION &&L_JSOP_PUSHOBJ), -# include "jsopcode.tbl" -# undef OPDEF - }; - - register void **jumpTable = normalJumpTable; - -# define DO_OP() JS_EXTENSION_(goto *jumpTable[op]) -# define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0) -# define BEGIN_CASE(OP) L_##OP: -# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); -# define END_VARLEN_CASE DO_NEXT_OP(len); -# define EMPTY_CASE(OP) BEGIN_CASE(OP) op = *++pc; DO_OP(); -#else -# define DO_OP() goto do_op -# define DO_NEXT_OP(n) goto advance_pc -# define BEGIN_CASE(OP) case OP: -# define END_CASE(OP) break; -# define END_VARLEN_CASE break; -# define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP) -#endif - - *result = JSVAL_VOID; - rt = cx->runtime; - - /* Set registerized frame pointer and derived script pointer. */ - fp = cx->fp; - script = fp->script; - JS_ASSERT(script->length != 0); - - /* Count of JS function calls that nest in this C js_Interpret frame. */ - inlineCallCount = 0; - - /* - * Optimized Get and SetVersion for proper script language versioning. - * - * If any native method or JSClass/JSObjectOps hook calls js_SetVersion - * and changes cx->version, the effect will "stick" and we will stop - * maintaining currentVersion. This is relied upon by testsuites, for - * the most part -- web browsers select version before compiling and not - * at run-time. - */ - currentVersion = script->version; - originalVersion = cx->version; - if (currentVersion != originalVersion) - js_SetVersion(cx, currentVersion); - -#ifdef __GNUC__ - flags = 0; /* suppress gcc warnings */ - id = 0; -#endif - - /* - * Prepare to call a user-supplied branch handler, and abort the script - * if it returns false. We reload onbranch after calling out to native - * functions (but not to getters, setters, or other native hooks). - */ -#define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) - - LOAD_BRANCH_CALLBACK(cx); -#define CHECK_BRANCH(len) \ - JS_BEGIN_MACRO \ - if (len <= 0 && onbranch) { \ - SAVE_SP_AND_PC(fp); \ - if (!(ok = (*onbranch)(cx, script))) \ - goto out; \ - } \ - JS_END_MACRO - - /* - * Load the debugger's interrupt hook here and after calling out to native - * functions (but not to getters, setters, or other native hooks), so we do - * not have to reload it each time through the interpreter loop -- we hope - * the compiler can keep it in a register when it is non-null. - */ -#ifdef JS_THREADED_INTERP -# define LOAD_JUMP_TABLE() \ - (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable) -#else -# define LOAD_JUMP_TABLE() /* nothing */ -#endif - -#define LOAD_INTERRUPT_HANDLER(rt) \ - JS_BEGIN_MACRO \ - interruptHandler = (rt)->interruptHandler; \ - LOAD_JUMP_TABLE(); \ - JS_END_MACRO - - LOAD_INTERRUPT_HANDLER(rt); - - /* Check for too much js_Interpret nesting, or too deep a C stack. */ - if (++cx->interpLevel == MAX_INTERP_LEVEL || - !JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - ok = JS_FALSE; - goto out2; - } - - /* - * Allocate operand and pc stack slots for the script's worst-case depth, - * unless we're called to interpret a part of an already active script, a - * filtering predicate expression for example. - */ - depth = (jsint) script->depth; - if (JS_LIKELY(!fp->spbase)) { - newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); - if (!newsp) { - ok = JS_FALSE; - goto out2; - } - sp = newsp + depth; - fp->spbase = sp; - SAVE_SP(fp); - } else { - sp = fp->sp; - JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); - newsp = fp->spbase - depth; - mark = NULL; - } - - /* - * To support generator_throw and to catch ignored exceptions, fail right - * away if cx->throwing is set. If no exception is pending, null obj in - * case a callable object is being sent into a yield expression, and the - * yield's result is invoked. - */ - ok = !cx->throwing; - if (!ok) { -#ifdef DEBUG_NOT_THROWING - printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n", - (unsigned long) cx->exception); -#endif - goto out; - } - obj = NULL; - -#ifdef JS_THREADED_INTERP - - /* - * This is a loop, but it does not look like a loop. The loop-closing - * jump is distributed throughout interruptJumpTable, and comes back to - * the interrupt label. The dispatch on op is through normalJumpTable. - * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately. - * - * It is important that "op" be initialized before the interrupt label - * because it is possible for "op" to be specially assigned during the - * normally processing of an opcode while looping (in particular, this - * happens in JSOP_TRAP while debugging). We rely on DO_NEXT_OP to - * correctly manage "op" in all other cases. - */ - op = (JSOp) *pc; - if (interruptHandler) { -interrupt: - SAVE_SP_AND_PC(fp); - switch (interruptHandler(cx, script, pc, &rval, - rt->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); - JS_EXTENSION_(goto *normalJumpTable[op]); - -#else /* !JS_THREADED_INTERP */ - - for (;;) { - op = (JSOp) *pc; - do_op: - len = js_CodeSpec[op].length; - -#ifdef DEBUG - tracefp = (FILE *) cx->tracefp; - if (tracefp) { - intN nuses, n; - - fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); - js_Disassemble1(cx, script, pc, - PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, - tracefp); - nuses = js_CodeSpec[op].nuses; - if (nuses) { - SAVE_SP_AND_PC(fp); - for (n = -nuses; n < 0; n++) { - str = js_DecompileValueGenerator(cx, n, sp[n], NULL); - if (str) { - fprintf(tracefp, "%s %s", - (n == -nuses) ? " inputs:" : ",", - JS_GetStringBytes(str)); - } - } - fprintf(tracefp, " @ %d\n", sp - fp->spbase); - } - } -#endif /* DEBUG */ - - if (interruptHandler && op != JSOP_PUSHOBJ) { - SAVE_SP_AND_PC(fp); - switch (interruptHandler(cx, script, pc, &rval, - rt->interruptHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - switch (op) { - -#endif /* !JS_THREADED_INTERP */ - - BEGIN_CASE(JSOP_STOP) - goto out; - - EMPTY_CASE(JSOP_NOP) - - BEGIN_CASE(JSOP_GROUP) - obj = NULL; - END_CASE(JSOP_GROUP) - - BEGIN_CASE(JSOP_PUSH) - PUSH_OPND(JSVAL_VOID); - END_CASE(JSOP_PUSH) - - BEGIN_CASE(JSOP_POP) - sp--; - END_CASE(JSOP_POP) - - BEGIN_CASE(JSOP_POP2) - sp -= 2; - END_CASE(JSOP_POP2) - - BEGIN_CASE(JSOP_SWAP) - vp = sp - depth; /* swap generating pc's for the decompiler */ - ltmp = vp[-1]; - vp[-1] = vp[-2]; - sp[-2] = ltmp; - rtmp = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = rtmp; - END_CASE(JSOP_SWAP) - - BEGIN_CASE(JSOP_POPV) - *result = POP_OPND(); - END_CASE(JSOP_POPV) - - BEGIN_CASE(JSOP_ENTERWITH) - FETCH_OBJECT(cx, -1, rval, obj); - SAVE_SP_AND_PC(fp); - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj || !(obj2 = js_GetScopeChain(cx, fp))) { - ok = JS_FALSE; - goto out; - } - withobj = js_NewWithObject(cx, obj, obj2, sp - fp->spbase - 1); - if (!withobj) { - ok = JS_FALSE; - goto out; - } - fp->scopeChain = withobj; - STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); - END_CASE(JSOP_ENTERWITH) - - BEGIN_CASE(JSOP_LEAVEWITH) - rval = POP_OPND(); - JS_ASSERT(JSVAL_IS_OBJECT(rval)); - withobj = JSVAL_TO_OBJECT(rval); - JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); - fp->scopeChain = OBJ_GET_PARENT(cx, withobj); - JS_SetPrivate(cx, withobj, NULL); - END_CASE(JSOP_LEAVEWITH) - - BEGIN_CASE(JSOP_SETRVAL) - ASSERT_NOT_THROWING(cx); - fp->rval = POP_OPND(); - END_CASE(JSOP_SETRVAL) - - BEGIN_CASE(JSOP_RETURN) - CHECK_BRANCH(-1); - fp->rval = POP_OPND(); - /* FALL THROUGH */ - - BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ - ASSERT_NOT_THROWING(cx); - if (inlineCallCount) - inline_return: - { - JSInlineFrame *ifp = (JSInlineFrame *) fp; - void *hookData = ifp->hookData; - - /* - * If fp has blocks on its scope chain, home their locals now, - * before calling any debugger hook, and before freeing stack. - * This matches the order of block putting and hook calling in - * the "out-of-line" return code at the bottom of js_Interpret - * and in js_Invoke. - */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - SAVE_SP_AND_PC(fp); - ok &= PutBlockObjects(cx, fp); - } - - if (hookData) { - JSInterpreterHook hook = rt->callHook; - if (hook) { - SAVE_SP_AND_PC(fp); - hook(cx, fp, JS_FALSE, &ok, hookData); - LOAD_INTERRUPT_HANDLER(rt); - } - } - - /* - * If fp has a call object, sync values and clear the back- - * pointer. This can happen for a lightweight function if it - * calls eval unexpectedly (in a way that is hidden from the - * compiler). See bug 325540. - */ - if (fp->callobj) { - SAVE_SP_AND_PC(fp); - ok &= js_PutCallObject(cx, fp); - } - - if (fp->argsobj) { - SAVE_SP_AND_PC(fp); - ok &= js_PutArgsObject(cx, fp); - } - - /* Restore context version only if callee hasn't set version. */ - if (JS_LIKELY(cx->version == currentVersion)) { - currentVersion = ifp->callerVersion; - if (currentVersion != cx->version) - js_SetVersion(cx, currentVersion); - } - - /* Store the return value in the caller's operand frame. */ - vp = ifp->rvp; - *vp = fp->rval; - - /* Restore cx->fp and release the inline frame's space. */ - cx->fp = fp = fp->down; - JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); - - /* Restore sp to point just above the return value. */ - fp->sp = vp + 1; - RESTORE_SP(fp); - - /* Restore the calling script's interpreter registers. */ - obj = NULL; - script = fp->script; - depth = (jsint) script->depth; - pc = fp->pc; -#ifndef JS_THREADED_INTERP - endpc = script->code + script->length; -#endif - - /* Store the generating pc for the return value. */ - vp[-depth] = (jsval)pc; - - /* Resume execution in the calling frame. */ - inlineCallCount--; - if (JS_LIKELY(ok)) { - JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH); - len = JSOP_CALL_LENGTH; - DO_NEXT_OP(len); - } - } - goto out; - - BEGIN_CASE(JSOP_DEFAULT) - (void) POP(); - /* FALL THROUGH */ - BEGIN_CASE(JSOP_GOTO) - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_IFEQ) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFEQ) - - BEGIN_CASE(JSOP_IFNE) - POP_BOOLEAN(cx, rval, cond); - if (cond != JS_FALSE) { - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFNE) - - BEGIN_CASE(JSOP_OR) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_TRUE) { - len = GET_JUMP_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_OR) - - BEGIN_CASE(JSOP_AND) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMP_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_AND) - - BEGIN_CASE(JSOP_DEFAULTX) - (void) POP(); - /* FALL THROUGH */ - BEGIN_CASE(JSOP_GOTOX) - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_IFEQX) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFEQX) - - BEGIN_CASE(JSOP_IFNEX) - POP_BOOLEAN(cx, rval, cond); - if (cond != JS_FALSE) { - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - END_CASE(JSOP_IFNEX) - - BEGIN_CASE(JSOP_ORX) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_TRUE) { - len = GET_JUMPX_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_ORX) - - BEGIN_CASE(JSOP_ANDX) - POP_BOOLEAN(cx, rval, cond); - if (cond == JS_FALSE) { - len = GET_JUMPX_OFFSET(pc); - PUSH_OPND(rval); - DO_NEXT_OP(len); - } - END_CASE(JSOP_ANDX) - -/* - * If the index value at sp[n] is not an int that fits in a jsval, it could - * be an object (an XML QName, AttributeName, or AnyName), but only if we are - * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a - * string atom id. - */ -#define FETCH_ELEMENT_ID(n, id) \ - JS_BEGIN_MACRO \ - jsval idval_ = FETCH_OPND(n); \ - if (JSVAL_IS_INT(idval_)) { \ - id = INT_JSVAL_TO_JSID(idval_); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = InternNonIntElementId(cx, idval_, &id); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - - BEGIN_CASE(JSOP_IN) - SAVE_SP_AND_PC(fp); - rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval)) { - str = js_DecompileValueGenerator(cx, -1, rval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_IN_NOT_OBJECT, - JS_GetStringBytes(str)); - } - ok = JS_FALSE; - goto out; - } - obj = JSVAL_TO_OBJECT(rval); - FETCH_ELEMENT_ID(-2, id); - CHECK_ELEMENT_ID(obj, id); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - END_CASE(JSOP_IN) - - BEGIN_CASE(JSOP_FOREACH) - flags = JSITER_ENUMERATE | JSITER_FOREACH; - goto value_to_iter; - -#if JS_HAS_DESTRUCTURING - BEGIN_CASE(JSOP_FOREACHKEYVAL) - flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE; - goto value_to_iter; -#endif - - BEGIN_CASE(JSOP_FORIN) - /* - * Set JSITER_ENUMERATE to indicate that for-in loop should use - * the enumeration protocol's iterator for compatibility if an - * explicit iterator is not given via the optional __iterator__ - * method. - */ - flags = JSITER_ENUMERATE; - - value_to_iter: - JS_ASSERT(sp > fp->spbase); - SAVE_SP_AND_PC(fp); - ok = js_ValueToIterator(cx, flags, &sp[-1]); - if (!ok) - goto out; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); - JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length); - END_CASE(JSOP_FORIN) - - BEGIN_CASE(JSOP_FORPROP) - /* - * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop - * is not paid for the more common cases. - */ - lval = FETCH_OPND(-1); - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - i = -2; - goto do_forinloop; - - BEGIN_CASE(JSOP_FORNAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - /* - * ECMA 12.6.3 says to eval the LHS after looking for properties - * to enumerate, and bail without LHS eval if there are no props. - * We do Find here to share the most code at label do_forinloop. - * If looking for enumerable properties could have side effects, - * then we'd have to move this into the common code and condition - * it on op == JSOP_FORNAME. - */ - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - lval = OBJECT_TO_JSVAL(obj); - /* FALL THROUGH */ - - BEGIN_CASE(JSOP_FORARG) - BEGIN_CASE(JSOP_FORVAR) - BEGIN_CASE(JSOP_FORLOCAL) - /* - * JSOP_FORARG and JSOP_FORVAR don't require any lval computation - * here, because they address slots on the stack (in fp->args and - * fp->vars, respectively). Same applies to JSOP_FORLOCAL, which - * addresses fp->spbase. - */ - /* FALL THROUGH */ - - BEGIN_CASE(JSOP_FORELEM) - /* - * JSOP_FORELEM simply initializes or updates the iteration state - * and leaves the index expression evaluation and assignment to the - * enumerator until after the next property has been acquired, via - * a JSOP_ENUMELEM bytecode. - */ - i = -1; - - do_forinloop: - /* - * Reach under the top of stack to find our property iterator, a - * JSObject that contains the iteration state. - */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i])); - iterobj = JSVAL_TO_OBJECT(sp[i]); - - SAVE_SP_AND_PC(fp); - ok = js_CallIteratorNext(cx, iterobj, &rval); - if (!ok) - goto out; - if (rval == JSVAL_HOLE) { - rval = JSVAL_FALSE; - goto end_forinloop; - } - - switch (op) { - case JSOP_FORARG: - slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->fun->nargs); - fp->argv[slot] = rval; - break; - - case JSOP_FORVAR: - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); - fp->vars[slot] = rval; - break; - - case JSOP_FORLOCAL: - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - vp = &fp->spbase[slot]; - GC_POKE(cx, *vp); - *vp = rval; - break; - - case JSOP_FORELEM: - /* FORELEM is not a SET operation, it's more like BINDNAME. */ - PUSH_OPND(rval); - break; - - default: - JS_ASSERT(op == JSOP_FORPROP || op == JSOP_FORNAME); - - /* Convert lval to a non-null object containing id. */ - VALUE_TO_OBJECT(cx, lval, obj); - if (op == JSOP_FORPROP) - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - - /* Set the variable obj[id] to refer to rval. */ - fp->flags |= JSFRAME_ASSIGNING; - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - fp->flags &= ~JSFRAME_ASSIGNING; - if (!ok) - goto out; - break; - } - - /* Push true to keep looping through properties. */ - rval = JSVAL_TRUE; - - end_forinloop: - sp += i + 1; - PUSH_OPND(rval); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - - BEGIN_CASE(JSOP_DUP) - JS_ASSERT(sp > fp->spbase); - vp = sp - 1; /* address top of stack */ - rval = *vp; - vp -= depth; /* address generating pc */ - vp[1] = *vp; - PUSH(rval); - END_CASE(JSOP_DUP) - - BEGIN_CASE(JSOP_DUP2) - JS_ASSERT(sp - 2 >= fp->spbase); - vp = sp - 1; /* address top of stack */ - lval = vp[-1]; - rval = *vp; - vp -= depth; /* address generating pc */ - vp[1] = vp[2] = *vp; - PUSH(lval); - PUSH(rval); - END_CASE(JSOP_DUP2) - -#define PROPERTY_OP(n, call) \ - JS_BEGIN_MACRO \ - /* Fetch the left part and resolve it to a non-null object. */ \ - FETCH_OBJECT(cx, n, lval, obj); \ - \ - /* Get or set the property, set ok false if error, true if success. */\ - SAVE_SP_AND_PC(fp); \ - call; \ - if (!ok) \ - goto out; \ - JS_END_MACRO - -#define ELEMENT_OP(n, call) \ - JS_BEGIN_MACRO \ - /* Fetch the right part and resolve it to an internal id. */ \ - FETCH_ELEMENT_ID(n, id); \ - \ - /* Fetch the left part and resolve it to a non-null object. */ \ - FETCH_OBJECT(cx, n - 1, lval, obj); \ - \ - /* Ensure that id has a type suitable for use with obj. */ \ - CHECK_ELEMENT_ID(obj, id); \ - \ - /* Get or set the element, set ok false if error, true if success. */ \ - SAVE_SP_AND_PC(fp); \ - call; \ - if (!ok) \ - goto out; \ - JS_END_MACRO - -#define NATIVE_GET(cx,obj,pobj,sprop,vp) \ - JS_BEGIN_MACRO \ - if (SPROP_HAS_STUB_GETTER(sprop)) { \ - /* Fast path for Object instance properties. */ \ - JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT || \ - !SPROP_HAS_STUB_SETTER(sprop)); \ - *vp = ((sprop)->slot != SPROP_INVALID_SLOT) \ - ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot) \ - : JSVAL_VOID; \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_NativeGet(cx, obj, pobj, sprop, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -#define NATIVE_SET(cx,obj,sprop,vp) \ - JS_BEGIN_MACRO \ - if (SPROP_HAS_STUB_SETTER(sprop) && \ - (sprop)->slot != SPROP_INVALID_SLOT) { \ - /* Fast path for Object instance properties. */ \ - LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *vp); \ - } else { \ - SAVE_SP_AND_PC(fp); \ - ok = js_NativeSet(cx, obj, sprop, vp); \ - if (!ok) \ - goto out; \ - } \ - JS_END_MACRO - -/* - * CACHED_GET and CACHED_SET use cx, obj, id, and rval from their callers' - * environments. - */ -#define CACHED_GET(call) \ - JS_BEGIN_MACRO \ - if (!OBJ_IS_NATIVE(obj)) { \ - ok = call; \ - } else { \ - JS_LOCK_OBJ(cx, obj); \ - PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ - if (sprop) { \ - NATIVE_GET(cx, obj, obj, sprop, &rval); \ - JS_UNLOCK_OBJ(cx, obj); \ - } else { \ - JS_UNLOCK_OBJ(cx, obj); \ - ok = call; \ - /* No fill here: js_GetProperty fills the cache. */ \ - } \ - } \ - JS_END_MACRO - -#define CACHED_SET(call) \ - JS_BEGIN_MACRO \ - if (!OBJ_IS_NATIVE(obj)) { \ - ok = call; \ - } else { \ - JSScope *scope_; \ - JS_LOCK_OBJ(cx, obj); \ - PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ - if (sprop && \ - !(sprop->attrs & JSPROP_READONLY) && \ - (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ - NATIVE_SET(cx, obj, sprop, &rval); \ - JS_UNLOCK_SCOPE(cx, scope_); \ - } else { \ - JS_UNLOCK_OBJ(cx, obj); \ - ok = call; \ - /* No fill here: js_SetProperty writes through the cache. */ \ - } \ - } \ - JS_END_MACRO - -#define BEGIN_LITOPX_CASE(OP,PCOFF) \ - BEGIN_CASE(OP) \ - pc2 = pc; \ - atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ - do_##OP: \ - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - -#define END_LITOPX_CASE(OP) \ - END_CASE(OP) - - BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) - obj = fp->varobj; - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, - NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_LITOPX_CASE(JSOP_SETCONST) - -#if JS_HAS_DESTRUCTURING - BEGIN_CASE(JSOP_ENUMCONSTELEM) - FETCH_ELEMENT_ID(-1, id); - FETCH_OBJECT(cx, -2, lval, obj); - CHECK_ELEMENT_ID(obj, id); - rval = FETCH_OPND(-3); - SAVE_SP_AND_PC(fp); - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - if (!ok) - goto out; - sp -= 3; - END_CASE(JSOP_ENUMCONSTELEM) -#endif - - BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) - SAVE_SP_AND_PC(fp); - obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_BINDNAME) - - BEGIN_CASE(JSOP_SETNAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); - JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); - obj = JSVAL_TO_OBJECT(lval); - SAVE_SP_AND_PC(fp); - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETNAME) - -#define INTEGER_OP(OP, EXTRA_CODE) \ - JS_BEGIN_MACRO \ - FETCH_INT(cx, -1, j); \ - FETCH_INT(cx, -2, i); \ - EXTRA_CODE \ - i = i OP j; \ - sp--; \ - STORE_INT(cx, -1, i); \ - JS_END_MACRO - -#define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) -#define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) - - BEGIN_CASE(JSOP_BITOR) - BITWISE_OP(|); - END_CASE(JSOP_BITOR) - - BEGIN_CASE(JSOP_BITXOR) - BITWISE_OP(^); - END_CASE(JSOP_BITXOR) - - BEGIN_CASE(JSOP_BITAND) - BITWISE_OP(&); - END_CASE(JSOP_BITAND) - -#define RELATIONAL_OP(OP) \ - JS_BEGIN_MACRO \ - rval = FETCH_OPND(-1); \ - lval = FETCH_OPND(-2); \ - /* Optimize for two int-tagged operands (typical loop control). */ \ - if ((lval & rval) & JSVAL_INT) { \ - ltmp = lval ^ JSVAL_VOID; \ - rtmp = rval ^ JSVAL_VOID; \ - if (ltmp && rtmp) { \ - cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ - } else { \ - d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ - d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ - cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ - } \ - } else { \ - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ - sp[-2] = lval; \ - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ - if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ - str = JSVAL_TO_STRING(lval); \ - str2 = JSVAL_TO_STRING(rval); \ - cond = js_CompareStrings(str, str2) OP 0; \ - } else { \ - VALUE_TO_NUMBER(cx, lval, d); \ - VALUE_TO_NUMBER(cx, rval, d2); \ - cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ - } \ - } \ - sp--; \ - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ - JS_END_MACRO - -/* - * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies - * because they begin if/else chains, so callers must not put semicolons after - * the call expressions! - */ -#if JS_HAS_XML_SUPPORT -#define XML_EQUALITY_OP(OP) \ - if ((ltmp == JSVAL_OBJECT && \ - (obj2 = JSVAL_TO_OBJECT(lval)) && \ - OBJECT_IS_XML(cx, obj2)) || \ - (rtmp == JSVAL_OBJECT && \ - (obj2 = JSVAL_TO_OBJECT(rval)) && \ - OBJECT_IS_XML(cx, obj2))) { \ - JSXMLObjectOps *ops; \ - \ - ops = (JSXMLObjectOps *) obj2->map->ops; \ - if (obj2 == JSVAL_TO_OBJECT(rval)) \ - rval = lval; \ - SAVE_SP_AND_PC(fp); \ - ok = ops->equality(cx, obj2, rval, &cond); \ - if (!ok) \ - goto out; \ - cond = cond OP JS_TRUE; \ - } else - -#define EXTENDED_EQUALITY_OP(OP) \ - if (ltmp == JSVAL_OBJECT && \ - (obj2 = JSVAL_TO_OBJECT(lval)) && \ - ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ - JSExtendedClass *xclasp; \ - \ - xclasp = (JSExtendedClass *) clasp; \ - SAVE_SP_AND_PC(fp); \ - ok = xclasp->equality(cx, obj2, rval, &cond); \ - if (!ok) \ - goto out; \ - cond = cond OP JS_TRUE; \ - } else -#else -#define XML_EQUALITY_OP(OP) /* nothing */ -#define EXTENDED_EQUALITY_OP(OP) /* nothing */ -#endif - -#define EQUALITY_OP(OP, IFNAN) \ - JS_BEGIN_MACRO \ - rval = FETCH_OPND(-1); \ - lval = FETCH_OPND(-2); \ - ltmp = JSVAL_TAG(lval); \ - rtmp = JSVAL_TAG(rval); \ - XML_EQUALITY_OP(OP) \ - if (ltmp == rtmp) { \ - if (ltmp == JSVAL_STRING) { \ - str = JSVAL_TO_STRING(lval); \ - str2 = JSVAL_TO_STRING(rval); \ - cond = js_EqualStrings(str, str2) OP JS_TRUE; \ - } else if (ltmp == JSVAL_DOUBLE) { \ - d = *JSVAL_TO_DOUBLE(lval); \ - d2 = *JSVAL_TO_DOUBLE(rval); \ - cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ - } else { \ - EXTENDED_EQUALITY_OP(OP) \ - /* Handle all undefined (=>NaN) and int combinations. */ \ - cond = lval OP rval; \ - } \ - } else { \ - if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ - cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ - } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ - cond = 1 OP 0; \ - } else { \ - if (ltmp == JSVAL_OBJECT) { \ - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \ - lval = sp[-2]; \ - ltmp = JSVAL_TAG(lval); \ - } else if (rtmp == JSVAL_OBJECT) { \ - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \ - rval = sp[-1]; \ - rtmp = JSVAL_TAG(rval); \ - } \ - if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ - str = JSVAL_TO_STRING(lval); \ - str2 = JSVAL_TO_STRING(rval); \ - cond = js_EqualStrings(str, str2) OP JS_TRUE; \ - } else { \ - VALUE_TO_NUMBER(cx, lval, d); \ - VALUE_TO_NUMBER(cx, rval, d2); \ - cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ - } \ - } \ - } \ - sp--; \ - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ - JS_END_MACRO - - BEGIN_CASE(JSOP_EQ) - EQUALITY_OP(==, JS_FALSE); - END_CASE(JSOP_EQ) - - BEGIN_CASE(JSOP_NE) - EQUALITY_OP(!=, JS_TRUE); - END_CASE(JSOP_NE) - -#define NEW_EQUALITY_OP(OP) \ - JS_BEGIN_MACRO \ - rval = FETCH_OPND(-1); \ - lval = FETCH_OPND(-2); \ - cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ - sp--; \ - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ - JS_END_MACRO - - BEGIN_CASE(JSOP_NEW_EQ) - NEW_EQUALITY_OP(==); - END_CASE(JSOP_NEW_EQ) - - BEGIN_CASE(JSOP_NEW_NE) - NEW_EQUALITY_OP(!=); - END_CASE(JSOP_NEW_NE) - - BEGIN_CASE(JSOP_CASE) - pc2 = (jsbytecode *) sp[-2-depth]; - NEW_EQUALITY_OP(==); - (void) POP(); - if (cond) { - len = GET_JUMP_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - sp[-depth] = (jsval)pc2; - PUSH(lval); - END_CASE(JSOP_CASE) - - BEGIN_CASE(JSOP_CASEX) - pc2 = (jsbytecode *) sp[-2-depth]; - NEW_EQUALITY_OP(==); - (void) POP(); - if (cond) { - len = GET_JUMPX_OFFSET(pc); - CHECK_BRANCH(len); - DO_NEXT_OP(len); - } - sp[-depth] = (jsval)pc2; - PUSH(lval); - END_CASE(JSOP_CASEX) - - BEGIN_CASE(JSOP_LT) - RELATIONAL_OP(<); - END_CASE(JSOP_LT) - - BEGIN_CASE(JSOP_LE) - RELATIONAL_OP(<=); - END_CASE(JSOP_LE) - - BEGIN_CASE(JSOP_GT) - RELATIONAL_OP(>); - END_CASE(JSOP_GT) - - BEGIN_CASE(JSOP_GE) - RELATIONAL_OP(>=); - END_CASE(JSOP_GE) - -#undef EQUALITY_OP -#undef RELATIONAL_OP - - BEGIN_CASE(JSOP_LSH) - SIGNED_SHIFT_OP(<<); - END_CASE(JSOP_LSH) - - BEGIN_CASE(JSOP_RSH) - SIGNED_SHIFT_OP(>>); - END_CASE(JSOP_RSH) - - BEGIN_CASE(JSOP_URSH) - { - uint32 u; - - FETCH_INT(cx, -1, j); - FETCH_UINT(cx, -2, u); - u >>= j & 31; - sp--; - STORE_UINT(cx, -1, u); - } - END_CASE(JSOP_URSH) - -#undef INTEGER_OP -#undef BITWISE_OP -#undef SIGNED_SHIFT_OP - - BEGIN_CASE(JSOP_ADD) - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); -#if JS_HAS_XML_SUPPORT - if (!JSVAL_IS_PRIMITIVE(lval) && - (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && - VALUE_IS_XML(cx, rval)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj2->map->ops; - SAVE_SP_AND_PC(fp); - ok = ops->concatenate(cx, obj2, rval, &rval); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, rval); - } else -#endif - { - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); - lval = sp[-2]; - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); - rval = sp[-1]; - if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { - SAVE_SP_AND_PC(fp); - if (cond) { - str = JSVAL_TO_STRING(lval); - ok = (str2 = js_ValueToString(cx, rval)) != NULL; - if (!ok) - goto out; - sp[-1] = STRING_TO_JSVAL(str2); - } else { - str2 = JSVAL_TO_STRING(rval); - ok = (str = js_ValueToString(cx, lval)) != NULL; - if (!ok) - goto out; - sp[-2] = STRING_TO_JSVAL(str); - } - str = js_ConcatStrings(cx, str, str2); - if (!str) { - ok = JS_FALSE; - goto out; - } - sp--; - STORE_OPND(-1, STRING_TO_JSVAL(str)); - } else { - VALUE_TO_NUMBER(cx, lval, d); - VALUE_TO_NUMBER(cx, rval, d2); - d += d2; - sp--; - STORE_NUMBER(cx, -1, d); - } - } - END_CASE(JSOP_ADD) - -#define BINARY_OP(OP) \ - JS_BEGIN_MACRO \ - FETCH_NUMBER(cx, -1, d2); \ - FETCH_NUMBER(cx, -2, d); \ - d = d OP d2; \ - sp--; \ - STORE_NUMBER(cx, -1, d); \ - JS_END_MACRO - - BEGIN_CASE(JSOP_SUB) - BINARY_OP(-); - END_CASE(JSOP_SUB) - - BEGIN_CASE(JSOP_MUL) - BINARY_OP(*); - END_CASE(JSOP_MUL) - - BEGIN_CASE(JSOP_DIV) - FETCH_NUMBER(cx, -1, d2); - FETCH_NUMBER(cx, -2, d); - sp--; - if (d2 == 0) { -#ifdef XP_WIN - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - rval = DOUBLE_TO_JSVAL(rt->jsNaN); - else -#endif - if (d == 0 || JSDOUBLE_IS_NaN(d)) - rval = DOUBLE_TO_JSVAL(rt->jsNaN); - else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) - rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); - else - rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); - STORE_OPND(-1, rval); - } else { - d /= d2; - STORE_NUMBER(cx, -1, d); - } - END_CASE(JSOP_DIV) - - BEGIN_CASE(JSOP_MOD) - FETCH_NUMBER(cx, -1, d2); - FETCH_NUMBER(cx, -2, d); - sp--; - if (d2 == 0) { - STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); - } else { -#ifdef XP_WIN - /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ - if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) -#endif - d = fmod(d, d2); - STORE_NUMBER(cx, -1, d); - } - END_CASE(JSOP_MOD) - - BEGIN_CASE(JSOP_NOT) - POP_BOOLEAN(cx, rval, cond); - PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); - END_CASE(JSOP_NOT) - - BEGIN_CASE(JSOP_BITNOT) - FETCH_INT(cx, -1, i); - i = ~i; - STORE_INT(cx, -1, i); - END_CASE(JSOP_BITNOT) - - BEGIN_CASE(JSOP_NEG) - /* - * Optimize the case of an int-tagged operand by noting that - * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0 - * when -i is the negative zero which is jsdouble. - */ - rval = FETCH_OPND(-1); - if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) { - i = -i; - JS_ASSERT(INT_FITS_IN_JSVAL(i)); - rval = INT_TO_JSVAL(i); - } else { - if (JSVAL_IS_DOUBLE(rval)) { - d = *JSVAL_TO_DOUBLE(rval); - } else { - SAVE_SP_AND_PC(fp); - ok = js_ValueToNumber(cx, rval, &d); - if (!ok) - goto out; - } -#ifdef HPUX - /* - * Negation of a zero doesn't produce a negative - * zero on HPUX. Perform the operation by bit - * twiddling. - */ - JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; -#else - d = -d; -#endif - ok = js_NewNumberValue(cx, d, &rval); - if (!ok) - goto out; - } - STORE_OPND(-1, rval); - END_CASE(JSOP_NEG) - - BEGIN_CASE(JSOP_POS) - rval = FETCH_OPND(-1); - if (!JSVAL_IS_NUMBER(rval)) { - SAVE_SP_AND_PC(fp); - ok = js_ValueToNumber(cx, rval, &d); - if (!ok) - goto out; - ok = js_NewNumberValue(cx, d, &rval); - if (!ok) - goto out; - sp[-1] = rval; - } - sp[-1-depth] = (jsval)pc; - END_CASE(JSOP_POS) - - BEGIN_CASE(JSOP_NEW) - /* Get immediate argc and find the constructor function. */ - argc = GET_ARGC(pc); - - do_new: - SAVE_SP_AND_PC(fp); - vp = sp - (2 + argc); - JS_ASSERT(vp >= fp->spbase); - - ok = js_InvokeConstructor(cx, vp, argc); - if (!ok) - goto out; - RESTORE_SP(fp); - LOAD_BRANCH_CALLBACK(cx); - LOAD_INTERRUPT_HANDLER(rt); - obj = JSVAL_TO_OBJECT(*vp); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - - BEGIN_CASE(JSOP_DELNAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - - /* ECMA says to return true if name is undefined or inherited. */ - rval = JSVAL_TRUE; - if (prop) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - } - PUSH_OPND(rval); - END_CASE(JSOP_DELNAME) - - BEGIN_CASE(JSOP_DELPROP) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); - STORE_OPND(-1, rval); - END_CASE(JSOP_DELPROP) - - BEGIN_CASE(JSOP_DELELEM) - ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); - sp--; - STORE_OPND(-1, rval); - END_CASE(JSOP_DELELEM) - - BEGIN_CASE(JSOP_TYPEOFEXPR) - BEGIN_CASE(JSOP_TYPEOF) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - type = JS_TypeOfValue(cx, rval); - atom = rt->atomState.typeAtoms[type]; - STORE_OPND(-1, ATOM_KEY(atom)); - END_CASE(JSOP_TYPEOF) - - BEGIN_CASE(JSOP_VOID) - (void) POP_OPND(); - PUSH_OPND(JSVAL_VOID); - END_CASE(JSOP_VOID) - - BEGIN_CASE(JSOP_INCNAME) - BEGIN_CASE(JSOP_DECNAME) - BEGIN_CASE(JSOP_NAMEINC) - BEGIN_CASE(JSOP_NAMEDEC) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - if (!prop) - goto atom_not_defined; - - OBJ_DROP_PROPERTY(cx, obj2, prop); - lval = OBJECT_TO_JSVAL(obj); - i = 0; - goto do_incop; - - BEGIN_CASE(JSOP_INCPROP) - BEGIN_CASE(JSOP_DECPROP) - BEGIN_CASE(JSOP_PROPINC) - BEGIN_CASE(JSOP_PROPDEC) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - lval = FETCH_OPND(-1); - i = -1; - goto do_incop; - - BEGIN_CASE(JSOP_INCELEM) - BEGIN_CASE(JSOP_DECELEM) - BEGIN_CASE(JSOP_ELEMINC) - BEGIN_CASE(JSOP_ELEMDEC) - FETCH_ELEMENT_ID(-1, id); - lval = FETCH_OPND(-2); - i = -2; - - do_incop: - { - const JSCodeSpec *cs; - - VALUE_TO_OBJECT(cx, lval, obj); - if (i < 0) - STORE_OPND(i, OBJECT_TO_JSVAL(obj)); - CHECK_ELEMENT_ID(obj, id); - - /* The operand must contain a number. */ - SAVE_SP_AND_PC(fp); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - - /* Preload for use in the if/else immediately below. */ - cs = &js_CodeSpec[op]; - - /* The expression result goes in rtmp, the updated value in rval. */ - if (JSVAL_IS_INT(rval) && - rval != INT_TO_JSVAL(JSVAL_INT_MIN) && - rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { - if (cs->format & JOF_POST) { - rtmp = rval; - (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); - } else { - (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); - rtmp = rval; - } - } else { - -/* - * Initially, rval contains the value to increment or decrement, which is not - * yet converted. As above, the expression result goes in rtmp, the updated - * value goes in rval. Our caller must set vp to point at a GC-rooted jsval - * in which we home rtmp, to protect it from GC in case the unconverted rval - * is not a number. - */ -#define NONINT_INCREMENT_OP_MIDDLE() \ - JS_BEGIN_MACRO \ - VALUE_TO_NUMBER(cx, rval, d); \ - if (cs->format & JOF_POST) { \ - rtmp = rval; \ - if (!JSVAL_IS_NUMBER(rtmp)) { \ - ok = js_NewNumberValue(cx, d, &rtmp); \ - if (!ok) \ - goto out; \ - } \ - *vp = rtmp; \ - (cs->format & JOF_INC) ? d++ : d--; \ - ok = js_NewNumberValue(cx, d, &rval); \ - } else { \ - (cs->format & JOF_INC) ? ++d : --d; \ - ok = js_NewNumberValue(cx, d, &rval); \ - rtmp = rval; \ - } \ - if (!ok) \ - goto out; \ - JS_END_MACRO - - if (cs->format & JOF_POST) { - /* - * We must push early to protect the postfix increment - * or decrement result, if converted to a jsdouble from - * a non-number value, from GC nesting in the setter. - */ - vp = sp; - PUSH(JSVAL_VOID); - SAVE_SP(fp); - --i; - } -#ifdef __GNUC__ - else vp = NULL; /* suppress bogus gcc warnings */ -#endif - - NONINT_INCREMENT_OP_MIDDLE(); - } - - fp->flags |= JSFRAME_ASSIGNING; - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - fp->flags &= ~JSFRAME_ASSIGNING; - if (!ok) - goto out; - sp += i; - PUSH_OPND(rtmp); - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - } - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OPEQ,MINMAX) \ - slot = SLOT; \ - JS_ASSERT(slot < fp->fun->COUNT); \ - vp = fp->BASE + slot; \ - rval = *vp; \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - *vp = rval; \ - PUSH_OPND(PRE); \ - goto end_nonint_fast_incop - - BEGIN_CASE(JSOP_INCARG) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); - BEGIN_CASE(JSOP_DECARG) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); - BEGIN_CASE(JSOP_ARGINC) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); - BEGIN_CASE(JSOP_ARGDEC) - FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); - - BEGIN_CASE(JSOP_INCVAR) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, +=, MAX); - BEGIN_CASE(JSOP_DECVAR) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rval, -=, MIN); - BEGIN_CASE(JSOP_VARINC) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, +=, MAX); - BEGIN_CASE(JSOP_VARDEC) - FAST_INCREMENT_OP(GET_VARNO(pc), u.i.nvars, vars, rtmp, -=, MIN); - - end_nonint_fast_incop: - len = JSOP_INCARG_LENGTH; /* all fast incops are same length */ - DO_NEXT_OP(len); - -#undef FAST_INCREMENT_OP - - do_nonint_fast_incop: - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - - NONINT_INCREMENT_OP_MIDDLE(); - *vp = rval; - PUSH_OPND(rtmp); - len = cs->length; - DO_NEXT_OP(len); - } - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OPEQ,MINMAX) \ - slot = GET_VARNO(pc); \ - JS_ASSERT(slot < fp->nvars); \ - lval = fp->vars[slot]; \ - if (JSVAL_IS_NULL(lval)) { \ - op = SLOWOP; \ - DO_OP(); \ - } \ - slot = JSVAL_TO_INT(lval); \ - obj = fp->varobj; \ - rval = OBJ_GET_SLOT(cx, obj, slot); \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_global_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - OBJ_SET_SLOT(cx, obj, slot, rval); \ - PUSH_OPND(PRE); \ - goto end_nonint_fast_global_incop - - BEGIN_CASE(JSOP_INCGVAR) - FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); - BEGIN_CASE(JSOP_DECGVAR) - FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); - BEGIN_CASE(JSOP_GVARINC) - FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); - BEGIN_CASE(JSOP_GVARDEC) - FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); - - end_nonint_fast_global_incop: - len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ - JS_ASSERT(len == js_CodeSpec[op].length); - DO_NEXT_OP(len); - -#undef FAST_GLOBAL_INCREMENT_OP - - do_nonint_fast_global_incop: - { - const JSCodeSpec *cs = &js_CodeSpec[op]; - - vp = sp++; - SAVE_SP(fp); - NONINT_INCREMENT_OP_MIDDLE(); - OBJ_SET_SLOT(cx, obj, slot, rval); - STORE_OPND(-1, rtmp); - len = cs->length; - DO_NEXT_OP(len); - } - - BEGIN_CASE(JSOP_GETPROP) - BEGIN_CASE(JSOP_GETXPROP) - /* Get an immediate atom naming the property. */ - atom = GET_ATOM(cx, script, pc); - lval = FETCH_OPND(-1); - if (JSVAL_IS_STRING(lval) && - atom == cx->runtime->atomState.lengthAtom) { - rval = INT_TO_JSVAL(JSSTRING_LENGTH(JSVAL_TO_STRING(lval))); - obj = NULL; - } else { - id = ATOM_TO_JSID(atom); - VALUE_TO_OBJECT(cx, lval, obj); - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - SAVE_SP_AND_PC(fp); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - } - STORE_OPND(-1, rval); - END_CASE(JSOP_GETPROP) - - BEGIN_CASE(JSOP_SETPROP) - /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ - rval = FETCH_OPND(-1); - - /* Get an immediate atom naming the property. */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); - sp--; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETPROP) - - BEGIN_CASE(JSOP_GETELEM) - BEGIN_CASE(JSOP_GETXELEM) - ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); - sp--; - STORE_OPND(-1, rval); - END_CASE(JSOP_GETELEM) - - BEGIN_CASE(JSOP_SETELEM) - rval = FETCH_OPND(-1); - ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); - sp -= 2; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETELEM) - - BEGIN_CASE(JSOP_ENUMELEM) - /* Funky: the value to set is under the [obj, id] pair. */ - FETCH_ELEMENT_ID(-1, id); - FETCH_OBJECT(cx, -2, lval, obj); - CHECK_ELEMENT_ID(obj, id); - rval = FETCH_OPND(-3); - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - sp -= 3; - END_CASE(JSOP_ENUMELEM) - -/* - * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the - * arguments object until it is truly needed. JSOP_ARGSUB optimizes away - * arguments objects when the only uses of the 'arguments' parameter are to - * fetch individual actual parameters. But if such a use were then invoked, - * e.g., arguments[i](), the 'this' parameter would and must bind to the - * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. - */ -#define LAZY_ARGS_THISP ((JSObject *) JSVAL_VOID) - - BEGIN_CASE(JSOP_PUSHOBJ) - if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_PUSHOBJ) - - BEGIN_CASE(JSOP_CALL) - BEGIN_CASE(JSOP_EVAL) - argc = GET_ARGC(pc); - vp = sp - (argc + 2); - lval = *vp; - SAVE_SP_AND_PC(fp); - if (VALUE_IS_FUNCTION(cx, lval) && - (obj = JSVAL_TO_OBJECT(lval), - fun = (JSFunction *) JS_GetPrivate(cx, obj), - FUN_INTERPRETED(fun))) - /* inline_call: */ - { - uintN nframeslots, nvars, nslots, missing; - JSArena *a; - jsuword avail, nbytes; - JSBool overflow; - void *newmark; - jsval *rvp; - JSInlineFrame *newifp; - JSInterpreterHook hook; - - /* Restrict recursion of lightweight functions. */ - if (inlineCallCount == MAX_INLINE_CALL_COUNT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_OVER_RECURSED); - ok = JS_FALSE; - goto out; - } - - /* Compute the total number of stack slots needed for fun. */ - nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval)); - nvars = fun->u.i.nvars; - script = fun->u.i.script; - depth = (jsint) script->depth; - nslots = nframeslots + nvars + 2 * depth; - - /* Allocate missing expected args adjacent to actual args. */ - missing = (fun->nargs > argc) ? fun->nargs - argc : 0; - a = cx->stackPool.current; - avail = a->avail; - newmark = (void *) avail; - if (missing) { - newsp = sp + missing; - overflow = (jsuword) newsp > a->limit; - if (overflow) - nslots += 2 + argc + missing; - else if ((jsuword) newsp > avail) - avail = a->avail = (jsuword) newsp; - } -#ifdef __GNUC__ - else overflow = JS_FALSE; /* suppress bogus gcc warnings */ -#endif - - /* Allocate the inline frame with its vars and operand slots. */ - newsp = (jsval *) avail; - nbytes = nslots * sizeof(jsval); - avail += nbytes; - if (avail <= a->limit) { - a->avail = avail; - } else { - JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool, - nbytes); - if (!newsp) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_STACK_OVERFLOW, - (fp && fp->fun) - ? JS_GetFunctionName(fp->fun) - : "script"); - goto bad_inline_call; - } - } - - /* Move args if missing overflow arena a, push missing args. */ - rvp = vp; - if (missing) { - if (overflow) { - memcpy(newsp, vp, (2 + argc) * sizeof(jsval)); - vp = newsp; - sp = vp + 2 + argc; - newsp = sp + missing; - } - do { - PUSH(JSVAL_VOID); - } while (--missing != 0); - } - - /* Claim space for the stack frame and initialize it. */ - newifp = (JSInlineFrame *) newsp; - newsp += nframeslots; - newifp->frame.callobj = NULL; - newifp->frame.argsobj = NULL; - newifp->frame.varobj = NULL; - newifp->frame.script = script; - newifp->frame.fun = fun; - newifp->frame.argc = argc; - newifp->frame.argv = vp + 2; - newifp->frame.rval = JSVAL_VOID; - newifp->frame.nvars = nvars; - newifp->frame.vars = newsp; - newifp->frame.down = fp; - newifp->frame.annotation = NULL; - newifp->frame.scopeChain = parent = OBJ_GET_PARENT(cx, obj); - newifp->frame.sharpDepth = 0; - newifp->frame.sharpArray = NULL; - newifp->frame.flags = 0; - newifp->frame.dormantNext = NULL; - newifp->frame.xmlNamespace = NULL; - newifp->frame.blockChain = NULL; - newifp->rvp = rvp; - newifp->mark = newmark; - - /* Compute the 'this' parameter now that argv is set. */ - if (!JSVAL_IS_OBJECT(vp[1])) { - PRIMITIVE_TO_OBJECT(cx, vp[1], obj2); - if (!obj2) - goto bad_inline_call; - vp[1] = OBJECT_TO_JSVAL(obj2); - } - newifp->frame.thisp = - js_ComputeThis(cx, - JSFUN_BOUND_METHOD_TEST(fun->flags) - ? parent - : JSVAL_TO_OBJECT(vp[1]), - newifp->frame.argv); - if (!newifp->frame.thisp) - goto bad_inline_call; -#ifdef DUMP_CALL_TABLE - LogCall(cx, *vp, argc, vp + 2); -#endif - - /* Push void to initialize local variables. */ - sp = newsp; - while (nvars--) - PUSH(JSVAL_VOID); - sp += depth; - newifp->frame.spbase = sp; - SAVE_SP(&newifp->frame); - - /* Call the debugger hook if present. */ - hook = rt->callHook; - if (hook) { - newifp->frame.pc = NULL; - newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, - rt->callHookData); - LOAD_INTERRUPT_HANDLER(rt); - } else { - newifp->hookData = NULL; - } - - /* Scope with a call object parented by the callee's parent. */ - if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) && - !js_GetCallObject(cx, &newifp->frame, parent)) { - goto bad_inline_call; - } - - /* Switch to new version if currentVersion wasn't overridden. */ - newifp->callerVersion = cx->version; - if (JS_LIKELY(cx->version == currentVersion)) { - currentVersion = script->version; - if (currentVersion != cx->version) - js_SetVersion(cx, currentVersion); - } - - /* Push the frame and set interpreter registers. */ - cx->fp = fp = &newifp->frame; - pc = script->code; -#ifndef JS_THREADED_INTERP - endpc = pc + script->length; -#endif - obj = NULL; - inlineCallCount++; - JS_RUNTIME_METER(rt, inlineCalls); - - /* Load first opcode and dispatch it (safe since JSOP_STOP). */ - op = *pc; - DO_OP(); - - bad_inline_call: - RESTORE_SP(fp); - JS_ASSERT(fp->pc == pc); - script = fp->script; - depth = (jsint) script->depth; - js_FreeRawStack(cx, newmark); - ok = JS_FALSE; - goto out; - } - - ok = js_Invoke(cx, argc, 0); - RESTORE_SP(fp); - LOAD_BRANCH_CALLBACK(cx); - LOAD_INTERRUPT_HANDLER(rt); - if (!ok) - goto out; - JS_RUNTIME_METER(rt, nonInlineCalls); -#if JS_HAS_LVALUE_RETURN - if (cx->rval2set) { - /* - * Use the stack depth we didn't claim in our budget, but that - * we know is there on account of [fun, this] already having - * been pushed, at a minimum (if no args). Those two slots - * have been popped and [rval] has been pushed, which leaves - * one more slot for rval2 before we might overflow. - * - * NB: rval2 must be the property identifier, and rval the - * object from which to get the property. The pair form an - * ECMA "reference type", which can be used on the right- or - * left-hand side of assignment ops. Note well: only native - * methods can return reference types. See JSOP_SETCALL just - * below for the left-hand-side case. - */ - PUSH_OPND(cx->rval2); - ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); - - sp--; - STORE_OPND(-1, rval); - cx->rval2set = JS_FALSE; - } -#endif /* JS_HAS_LVALUE_RETURN */ - obj = NULL; - END_CASE(JSOP_CALL) - -#if JS_HAS_LVALUE_RETURN - BEGIN_CASE(JSOP_SETCALL) - argc = GET_ARGC(pc); - SAVE_SP_AND_PC(fp); - ok = js_Invoke(cx, argc, 0); - RESTORE_SP(fp); - LOAD_BRANCH_CALLBACK(cx); - LOAD_INTERRUPT_HANDLER(rt); - if (!ok) - goto out; - if (!cx->rval2set) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_LEFTSIDE_OF_ASS); - ok = JS_FALSE; - goto out; - } - PUSH_OPND(cx->rval2); - cx->rval2set = JS_FALSE; - obj = NULL; - END_CASE(JSOP_SETCALL) -#endif - - BEGIN_CASE(JSOP_NAME) - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - - SAVE_SP_AND_PC(fp); - ok = js_FindProperty(cx, id, &obj, &obj2, &prop); - if (!ok) - goto out; - if (!prop) { - /* Kludge to allow (typeof foo == "undefined") tests. */ - len = JSOP_NAME_LENGTH; - endpc = script->code + script->length; - for (pc2 = pc + len; pc2 < endpc; pc2++) { - op2 = (JSOp)*pc2; - if (op2 == JSOP_TYPEOF) { - PUSH_OPND(JSVAL_VOID); - DO_NEXT_OP(len); - } - if (op2 != JSOP_GROUP) - break; - } - goto atom_not_defined; - } - - /* Take the slow path if prop was not found in a native object. */ - if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - } else { - sprop = (JSScopeProperty *)prop; - NATIVE_GET(cx, obj, obj2, sprop, &rval); - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - PUSH_OPND(rval); - END_CASE(JSOP_NAME) - - BEGIN_CASE(JSOP_UINT16) - i = (jsint) GET_ATOM_INDEX(pc); - rval = INT_TO_JSVAL(i); - PUSH_OPND(rval); - obj = NULL; - END_CASE(JSOP_UINT16) - - BEGIN_CASE(JSOP_UINT24) - i = (jsint) GET_LITERAL_INDEX(pc); - rval = INT_TO_JSVAL(i); - PUSH_OPND(rval); - END_CASE(JSOP_UINT24) - - BEGIN_CASE(JSOP_LITERAL) - atomIndex = GET_LITERAL_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - PUSH_OPND(ATOM_KEY(atom)); - obj = NULL; - END_CASE(JSOP_LITERAL) - - BEGIN_CASE(JSOP_FINDNAME) - atomIndex = GET_LITERAL_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - SAVE_SP_AND_PC(fp); - obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - PUSH_OPND(ATOM_KEY(atom)); - END_CASE(JSOP_FINDNAME) - - BEGIN_CASE(JSOP_LITOPX) - /* - * Load atomIndex, which is used by code at each do_JSOP_* label. - * - * Also set pc2 to point at the bytecode extended by this prefix - * to have a leading 24 bit atomIndex, instead of the unextended - * 16-bit atomIndex that normally comes after op. This enables - * JOF_INDEXCONST format ops (which have multiple immediates) to - * collect their other immediate via GET_VARNO(pc2) or similar. - * - * Finally, load op and, if threading, adjust pc so that it will - * be advanced properly at the end of op's case by DO_NEXT_OP. - */ - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - pc += JSOP_LITOPX_LENGTH - (1 + ATOM_INDEX_LEN); -#ifndef JS_THREADED_INTERP - len = js_CodeSpec[op].length; -#endif - switch (op) { - case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; - case JSOP_BINDNAME: goto do_JSOP_BINDNAME; - case JSOP_CLOSURE: goto do_JSOP_CLOSURE; - case JSOP_DEFCONST: goto do_JSOP_DEFCONST; - case JSOP_DEFFUN: goto do_JSOP_DEFFUN; - case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; - case JSOP_DEFVAR: goto do_JSOP_DEFVAR; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; -#endif -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; - case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; -#endif - case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; - case JSOP_NUMBER: goto do_JSOP_NUMBER; - case JSOP_OBJECT: goto do_JSOP_OBJECT; -#if JS_HAS_XML_SUPPORT - case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; - case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; -#endif - case JSOP_REGEXP: goto do_JSOP_REGEXP; - case JSOP_SETCONST: goto do_JSOP_SETCONST; - case JSOP_STRING: goto do_JSOP_STRING; -#if JS_HAS_XML_SUPPORT - case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; - case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; - case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; - case JSOP_XMLPI: goto do_JSOP_XMLPI; -#endif - case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; - default: JS_ASSERT(0); - } - /* NOTREACHED */ - - BEGIN_CASE(JSOP_NUMBER) - BEGIN_CASE(JSOP_STRING) - BEGIN_CASE(JSOP_OBJECT) - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_NUMBER: - do_JSOP_STRING: - do_JSOP_OBJECT: - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - PUSH_OPND(ATOM_KEY(atom)); - obj = NULL; - END_CASE(JSOP_NUMBER) - - BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) - { - JSRegExp *re; - JSObject *funobj; - - /* - * Push a regexp object for the atom mapped by the bytecode at pc, - * cloning the literal's regexp object if necessary, to simulate in - * the pre-compile/execute-later case what ECMA specifies for the - * compile-and-go case: that scanning each regexp literal creates - * a single corresponding RegExp object. - * - * To support pre-compilation transparently, we must handle the - * case where a regexp object literal is used in a different global - * at execution time from the global with which it was scanned at - * compile time. We do this by re-wrapping the JSRegExp private - * data struct with a cloned object having the right prototype and - * parent, and having its own lastIndex property value storage. - * - * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone - * literal objects, we don't want to pay a script prolog execution - * price for all regexp literals in a script (many may not be used - * by a particular execution of that script, depending on control - * flow), so we initialize lazily here. - * - * XXX This code is specific to regular expression objects. If we - * need a similar op for other kinds of object literals, we should - * push cloning down under JSObjectOps and reuse code here. - */ - JS_ASSERT(ATOM_IS_OBJECT(atom)); - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); - - re = (JSRegExp *) JS_GetPrivate(cx, obj); - slot = re->cloneIndex; - if (fp->fun) { - /* - * We're in function code, not global or eval code (in eval - * code, JSOP_REGEXP is never emitted). The code generator - * recorded in fp->fun->nregexps the number of re->cloneIndex - * slots that it reserved in the cloned funobj. - */ - funobj = JSVAL_TO_OBJECT(fp->argv[-2]); - slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); - if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) - return JS_FALSE; - if (JSVAL_IS_VOID(rval)) - rval = JSVAL_NULL; - } else { - /* - * We're in global code. The code generator already arranged - * via script->numGlobalVars to reserve a global variable slot - * at cloneIndex. All global variable slots are initialized - * to null, not void, for faster testing in JSOP_*GVAR cases. - */ - rval = fp->vars[slot]; -#ifdef __GNUC__ - funobj = NULL; /* suppress bogus gcc warnings */ -#endif - } - - if (JSVAL_IS_NULL(rval)) { - /* Compute the current global object in obj2. */ - obj2 = fp->scopeChain; - while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) - obj2 = parent; - - /* - * We must home sp here, because either js_CloneRegExpObject - * or JS_SetReservedSlot could nest a last-ditch GC. We home - * pc as well, in case js_CloneRegExpObject has to lookup the - * "RegExp" class in the global object, which could entail a - * JSNewResolveOp call. - */ - SAVE_SP_AND_PC(fp); - - /* - * If obj's parent is not obj2, we must clone obj so that it - * has the right parent, and therefore, the right prototype. - * - * Yes, this means we assume that the correct RegExp.prototype - * to which regexp instances (including literals) delegate can - * be distinguished solely by the instance's parent, which was - * set to the parent of the RegExp constructor function object - * when the instance was created. In other words, - * - * (/x/.__parent__ == RegExp.__parent__) implies - * (/x/.__proto__ == RegExp.prototype) - * - * (unless you assign a different object to RegExp.prototype - * at runtime, in which case, ECMA doesn't specify operation, - * and you get what you deserve). - * - * This same coupling between instance parent and constructor - * parent turns up everywhere (see jsobj.c's FindClassObject, - * js_ConstructObject, and js_NewObject). It's fundamental to - * the design of the language when you consider multiple global - * objects and separate compilation and execution, even though - * it is not specified fully in ECMA. - */ - if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneRegExpObject(cx, obj, obj2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - rval = OBJECT_TO_JSVAL(obj); - - /* Store the regexp object value in its cloneIndex slot. */ - if (fp->fun) { - if (!JS_SetReservedSlot(cx, funobj, slot, rval)) - return JS_FALSE; - } else { - fp->vars[slot] = rval; - } - } - - PUSH_OPND(rval); - obj = NULL; - } - END_LITOPX_CASE(JSOP_REGEXP) - - BEGIN_CASE(JSOP_ZERO) - PUSH_OPND(JSVAL_ZERO); - obj = NULL; - END_CASE(JSOP_ZERO) - - BEGIN_CASE(JSOP_ONE) - PUSH_OPND(JSVAL_ONE); - obj = NULL; - END_CASE(JSOP_ONE) - - BEGIN_CASE(JSOP_NULL) - PUSH_OPND(JSVAL_NULL); - obj = NULL; - END_CASE(JSOP_NULL) - - BEGIN_CASE(JSOP_THIS) - obj = fp->thisp; - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - JSExtendedClass *xclasp; - - xclasp = (JSExtendedClass *) clasp; - if (xclasp->outerObject) { - obj = xclasp->outerObject(cx, obj); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - } - - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_CASE(JSOP_THIS) - - BEGIN_CASE(JSOP_FALSE) - PUSH_OPND(JSVAL_FALSE); - obj = NULL; - END_CASE(JSOP_FALSE) - - BEGIN_CASE(JSOP_TRUE) - PUSH_OPND(JSVAL_TRUE); - obj = NULL; - END_CASE(JSOP_TRUE) - - BEGIN_CASE(JSOP_TABLESWITCH) - pc2 = pc; - len = GET_JUMP_OFFSET(pc2); - - /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to - * the default case if the discriminant isn't already an int jsval. - * (This opcode is emitted only for dense jsint-domain switches.) - */ - rval = POP_OPND(); - if (!JSVAL_IS_INT(rval)) - DO_NEXT_OP(len); - i = JSVAL_TO_INT(rval); - - pc2 += JUMP_OFFSET_LEN; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - - i -= low; - if ((jsuint)i < (jsuint)(high - low + 1)) { - pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; - off = (jsint) GET_JUMP_OFFSET(pc2); - if (off) - len = off; - } - END_VARLEN_CASE - - BEGIN_CASE(JSOP_LOOKUPSWITCH) - lval = POP_OPND(); - pc2 = pc; - len = GET_JUMP_OFFSET(pc2); - - if (!JSVAL_IS_NUMBER(lval) && - !JSVAL_IS_STRING(lval) && - !JSVAL_IS_BOOLEAN(lval)) { - DO_NEXT_OP(len); - } - - pc2 += JUMP_OFFSET_LEN; - npairs = (jsint) GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - -#define SEARCH_PAIRS(MATCH_CODE) \ - while (npairs) { \ - atom = GET_ATOM(cx, script, pc2); \ - rval = ATOM_KEY(atom); \ - MATCH_CODE \ - if (match) { \ - pc2 += ATOM_INDEX_LEN; \ - len = GET_JUMP_OFFSET(pc2); \ - DO_NEXT_OP(len); \ - } \ - pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ - npairs--; \ - } - if (JSVAL_IS_STRING(lval)) { - str = JSVAL_TO_STRING(lval); - SEARCH_PAIRS( - match = (JSVAL_IS_STRING(rval) && - ((str2 = JSVAL_TO_STRING(rval)) == str || - js_EqualStrings(str2, str))); - ) - } else if (JSVAL_IS_DOUBLE(lval)) { - d = *JSVAL_TO_DOUBLE(lval); - SEARCH_PAIRS( - match = (JSVAL_IS_DOUBLE(rval) && - *JSVAL_TO_DOUBLE(rval) == d); - ) - } else { - SEARCH_PAIRS( - match = (lval == rval); - ) - } -#undef SEARCH_PAIRS - END_VARLEN_CASE - - BEGIN_CASE(JSOP_TABLESWITCHX) - pc2 = pc; - len = GET_JUMPX_OFFSET(pc2); - - /* - * ECMAv2+ forbids conversion of discriminant, so we will skip to - * the default case if the discriminant isn't already an int jsval. - * (This opcode is emitted only for dense jsint-domain switches.) - */ - rval = POP_OPND(); - if (!JSVAL_IS_INT(rval)) - DO_NEXT_OP(len); - i = JSVAL_TO_INT(rval); - - pc2 += JUMPX_OFFSET_LEN; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - - i -= low; - if ((jsuint)i < (jsuint)(high - low + 1)) { - pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; - off = (jsint) GET_JUMPX_OFFSET(pc2); - if (off) - len = off; - } - END_VARLEN_CASE - - BEGIN_CASE(JSOP_LOOKUPSWITCHX) - lval = POP_OPND(); - pc2 = pc; - len = GET_JUMPX_OFFSET(pc2); - - if (!JSVAL_IS_NUMBER(lval) && - !JSVAL_IS_STRING(lval) && - !JSVAL_IS_BOOLEAN(lval)) { - DO_NEXT_OP(len); - } - - pc2 += JUMPX_OFFSET_LEN; - npairs = (jsint) GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - -#define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ - while (npairs) { \ - atom = GET_ATOM(cx, script, pc2); \ - rval = ATOM_KEY(atom); \ - MATCH_CODE \ - if (match) { \ - pc2 += ATOM_INDEX_LEN; \ - len = GET_JUMPX_OFFSET(pc2); \ - DO_NEXT_OP(len); \ - } \ - pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ - npairs--; \ - } - if (JSVAL_IS_STRING(lval)) { - str = JSVAL_TO_STRING(lval); - SEARCH_EXTENDED_PAIRS( - match = (JSVAL_IS_STRING(rval) && - ((str2 = JSVAL_TO_STRING(rval)) == str || - js_EqualStrings(str2, str))); - ) - } else if (JSVAL_IS_DOUBLE(lval)) { - d = *JSVAL_TO_DOUBLE(lval); - SEARCH_EXTENDED_PAIRS( - match = (JSVAL_IS_DOUBLE(rval) && - *JSVAL_TO_DOUBLE(rval) == d); - ) - } else { - SEARCH_EXTENDED_PAIRS( - match = (lval == rval); - ) - } -#undef SEARCH_EXTENDED_PAIRS - END_VARLEN_CASE - - EMPTY_CASE(JSOP_CONDSWITCH) - -#if JS_HAS_EXPORT_IMPORT - BEGIN_CASE(JSOP_EXPORTALL) - obj = fp->varobj; - SAVE_SP_AND_PC(fp); - ida = JS_Enumerate(cx, obj); - if (!ida) { - ok = JS_FALSE; - } else { - for (i = 0, j = ida->length; i < j; i++) { - id = ida->vector[i]; - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - break; - if (!prop) - continue; - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); - if (ok) { - attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (!ok) - break; - } - JS_DestroyIdArray(cx, ida); - } - END_CASE(JSOP_EXPORTALL) - - BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) - id = ATOM_TO_JSID(atom); - obj = fp->varobj; - SAVE_SP_AND_PC(fp); - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - goto out; - if (!prop) { - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, - JSPROP_EXPORTED, NULL); - } else { - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); - if (ok) { - attrs |= JSPROP_EXPORTED; - ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - if (!ok) - goto out; - END_LITOPX_CASE(JSOP_EXPORTNAME) - - BEGIN_CASE(JSOP_IMPORTALL) - id = (jsid) JSVAL_VOID; - PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); - sp--; - END_CASE(JSOP_IMPORTALL) - - BEGIN_CASE(JSOP_IMPORTPROP) - /* Get an immediate atom naming the property. */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); - sp--; - END_CASE(JSOP_IMPORTPROP) - - BEGIN_CASE(JSOP_IMPORTELEM) - ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); - sp -= 2; - END_CASE(JSOP_IMPORTELEM) -#endif /* JS_HAS_EXPORT_IMPORT */ - - BEGIN_CASE(JSOP_TRAP) - SAVE_SP_AND_PC(fp); - switch (JS_HandleTrap(cx, script, pc, &rval)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - JS_ASSERT(JSVAL_IS_INT(rval)); - op = (JSOp) JSVAL_TO_INT(rval); - JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); - LOAD_INTERRUPT_HANDLER(rt); - DO_OP(); - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - END_CASE(JSOP_TRAP) - - BEGIN_CASE(JSOP_ARGUMENTS) - SAVE_SP_AND_PC(fp); - ok = js_GetArgsValue(cx, fp, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - obj = NULL; - END_CASE(JSOP_ARGUMENTS) - - BEGIN_CASE(JSOP_ARGSUB) - id = INT_TO_JSID(GET_ARGNO(pc)); - SAVE_SP_AND_PC(fp); - ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); - if (!ok) - goto out; - if (!obj) { - /* - * If arguments was not overridden by eval('arguments = ...'), - * set obj to the magic cookie respected by JSOP_PUSHOBJ, just - * in case this bytecode is part of an 'arguments[i](j, k)' or - * similar such invocation sequence, where the function that - * is invoked expects its 'this' parameter to be the caller's - * arguments object. - */ - obj = LAZY_ARGS_THISP; - } - PUSH_OPND(rval); - END_CASE(JSOP_ARGSUB) - -#undef LAZY_ARGS_THISP - - BEGIN_CASE(JSOP_ARGCNT) - id = ATOM_TO_JSID(rt->atomState.lengthAtom); - SAVE_SP_AND_PC(fp); - ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - END_CASE(JSOP_ARGCNT) - - BEGIN_CASE(JSOP_GETARG) - slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->fun->nargs); - PUSH_OPND(fp->argv[slot]); - obj = NULL; - END_CASE(JSOP_GETARG) - - BEGIN_CASE(JSOP_SETARG) - slot = GET_ARGNO(pc); - JS_ASSERT(slot < fp->fun->nargs); - vp = &fp->argv[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETARG) - - BEGIN_CASE(JSOP_GETVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); - PUSH_OPND(fp->vars[slot]); - obj = NULL; - END_CASE(JSOP_GETVAR) - - BEGIN_CASE(JSOP_SETVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->fun->u.i.nvars); - vp = &fp->vars[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETVAR) - - BEGIN_CASE(JSOP_GETGVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->nvars); - lval = fp->vars[slot]; - if (JSVAL_IS_NULL(lval)) { - op = JSOP_NAME; - DO_OP(); - } - slot = JSVAL_TO_INT(lval); - obj = fp->varobj; - rval = OBJ_GET_SLOT(cx, obj, slot); - PUSH_OPND(rval); - END_CASE(JSOP_GETGVAR) - - BEGIN_CASE(JSOP_SETGVAR) - slot = GET_VARNO(pc); - JS_ASSERT(slot < fp->nvars); - rval = FETCH_OPND(-1); - lval = fp->vars[slot]; - obj = fp->varobj; - if (JSVAL_IS_NULL(lval)) { - /* - * Inline-clone and specialize JSOP_SETNAME code here because - * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] - * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. - */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - if (!ok) - goto out; - STORE_OPND(-1, rval); - } else { - slot = JSVAL_TO_INT(lval); - GC_POKE(cx, obj->slots[slot]); - OBJ_SET_SLOT(cx, obj, slot, rval); - } - obj = NULL; - END_CASE(JSOP_SETGVAR) - - BEGIN_CASE(JSOP_DEFCONST) - BEGIN_CASE(JSOP_DEFVAR) - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_DEFCONST: - do_JSOP_DEFVAR: - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - obj = fp->varobj; - attrs = JSPROP_ENUMERATE; - if (!(fp->flags & JSFRAME_EVAL)) - attrs |= JSPROP_PERMANENT; - if (op == JSOP_DEFCONST) - attrs |= JSPROP_READONLY; - - /* Lookup id in order to check for redeclaration problems. */ - id = ATOM_TO_JSID(atom); - SAVE_SP_AND_PC(fp); - ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); - if (!ok) - goto out; - - /* Bind a variable only if it's not yet defined. */ - if (!prop) { - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, - attrs, &prop); - if (!ok) - goto out; - JS_ASSERT(prop); - obj2 = obj; - } - - /* - * Try to optimize a property we either just created, or found - * directly in the global object, that is permanent, has a slot, - * and has stub getter and setter, into a "fast global" accessed - * by the JSOP_*GVAR opcodes. - */ - if (atomIndex < script->numGlobalVars && - (attrs & JSPROP_PERMANENT) && - obj2 == obj && - OBJ_IS_NATIVE(obj)) { - sprop = (JSScopeProperty *) prop; - if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && - SPROP_HAS_STUB_GETTER(sprop) && - SPROP_HAS_STUB_SETTER(sprop)) { - /* - * Fast globals use fp->vars to map the global name's - * atomIndex to the permanent fp->varobj slot number, - * tagged as a jsval. The atomIndex for the global's - * name literal is identical to its fp->vars index. - */ - fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); - } - } - - OBJ_DROP_PROPERTY(cx, obj2, prop); - END_CASE(JSOP_DEFVAR) - - BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) - obj = ATOM_TO_OBJECT(atom); - fun = (JSFunction *) JS_GetPrivate(cx, obj); - id = ATOM_TO_JSID(fun->atom); - - /* - * We must be at top-level (either outermost block that forms a - * function's body, or a global) scope, not inside an expression - * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE) - * in the same compilation unit (ECMA Program). - * - * However, we could be in a Program being eval'd from inside a - * with statement, so we need to distinguish scope chain head from - * variables object. Hence the obj2 vs. parent distinction below. - * First we make sure the function object we're defining has the - * right scope chain. Then we define its name in fp->varobj. - * - * If static link is not current scope, clone fun's object to link - * to the current scope via parent. This clause exists to enable - * sharing of compiled functions among multiple equivalent scopes, - * splitting the cost of compilation evenly among the scopes and - * amortizing it over a number of executions. Examples include XUL - * scripts and event handlers shared among Mozilla chrome windows, - * and server-side JS user-defined functions shared among requests. - * - * NB: The Script object exposes compile and exec in the language, - * such that this clause introduces an incompatible change from old - * JS versions that supported Script. Such a JS version supported - * executing a script that defined and called functions scoped by - * the compile-time static link, not by the exec-time scope chain. - * - * We sacrifice compatibility, breaking such scripts, in order to - * promote compile-cost sharing and amortizing, and because Script - * is not and will not be standardized. - */ - JS_ASSERT(!fp->blockChain); - obj2 = fp->scopeChain; - if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneFunctionObject(cx, obj, obj2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - - /* - * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All - * paths from here must flow through the "Restore fp->scopeChain" - * code below the OBJ_DEFINE_PROPERTY call. - */ - fp->scopeChain = obj; - rval = OBJECT_TO_JSVAL(obj); - - /* - * ECMA requires functions defined when entering Global code to be - * permanent, and functions defined when entering Eval code to be - * impermanent. - */ - attrs = JSPROP_ENUMERATE; - if (!(fp->flags & JSFRAME_EVAL)) - attrs |= JSPROP_PERMANENT; - - /* - * Load function flags that are also property attributes. Getters - * and setters do not need a slot, their value is stored elsewhere - * in the property itself, not in obj->slots. - */ - flags = JSFUN_GSFLAG2ATTR(fun->flags); - if (flags) { - attrs |= flags | JSPROP_SHARED; - rval = JSVAL_VOID; - } - - /* - * Check for a const property of the same name -- or any kind - * of property if executing with the strict option. We check - * here at runtime as well as at compile-time, to handle eval - * as well as multiple HTML script tags. - */ - parent = fp->varobj; - SAVE_SP_AND_PC(fp); - ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); - if (ok) { - ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, - (flags & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - (flags & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - attrs, - &prop); - } - - /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ - fp->scopeChain = obj2; - if (!ok) - goto out; - -#if 0 - if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && - script->numGlobalVars) { - /* - * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals - * use fp->vars to map the global function name's atomIndex to - * its permanent fp->varobj slot number, tagged as a jsval. - */ - sprop = (JSScopeProperty *) prop; - fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); - } -#endif - OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE(JSOP_DEFFUN) - - BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) - /* - * Define a local function (i.e., one nested at the top level of - * another function), parented by the current scope chain, and - * stored in a local variable slot that the compiler allocated. - * This is an optimization over JSOP_DEFFUN that avoids requiring - * a call object for the outer function's activation. - */ - slot = GET_VARNO(pc2); - obj = ATOM_TO_OBJECT(atom); - - JS_ASSERT(!fp->blockChain); - if (!(fp->flags & JSFRAME_POP_BLOCKS)) { - /* - * If the compiler-created function object (obj) is scoped by a - * let-induced body block, temporarily update fp->blockChain so - * that js_GetScopeChain will clone the block into the runtime - * scope needed to parent the function object's clone. - */ - parent = OBJ_GET_PARENT(cx, obj); - if (OBJ_GET_CLASS(cx, parent) == &js_BlockClass) - fp->blockChain = parent; - parent = js_GetScopeChain(cx, fp); - } else { - /* - * We have already emulated JSOP_ENTERBLOCK for the enclosing - * body block, for a prior JSOP_DEFLOCALFUN in the prolog, so - * we just load fp->scopeChain into parent. - * - * In typical execution scenarios, the prolog bytecodes that - * include this JSOP_DEFLOCALFUN run, then come main bytecodes - * including JSOP_ENTERBLOCK for the outermost (body) block. - * JSOP_ENTERBLOCK will detect that it need not do anything if - * the body block was entered above due to a local function. - * Finally the matching JSOP_LEAVEBLOCK runs. - * - * If the matching JSOP_LEAVEBLOCK for the body block does not - * run for some reason, the body block will be properly "put" - * (via js_PutBlockObject) by the PutBlockObjects call at the - * bottom of js_Interpret. - */ - parent = fp->scopeChain; - JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); - JS_ASSERT(OBJ_GET_PROTO(cx, parent) == OBJ_GET_PARENT(cx, obj)); - JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, parent)) - == &js_CallClass); - } - - /* If re-parenting, store a clone of the function object. */ - if (OBJ_GET_PARENT(cx, obj) != parent) { - SAVE_SP_AND_PC(fp); - obj = js_CloneFunctionObject(cx, obj, parent); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - fp->vars[slot] = OBJECT_TO_JSVAL(obj); - END_LITOPX_CASE(JSOP_DEFLOCALFUN) - - BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) - /* Push the specified function object literal. */ - obj = ATOM_TO_OBJECT(atom); - - /* If re-parenting, push a clone of the function object. */ - SAVE_SP_AND_PC(fp); - parent = js_GetScopeChain(cx, fp); - if (!parent) { - ok = JS_FALSE; - goto out; - } - if (OBJ_GET_PARENT(cx, obj) != parent) { - obj = js_CloneFunctionObject(cx, obj, parent); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_LITOPX_CASE(JSOP_ANONFUNOBJ) - - BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) - /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ - rval = ATOM_KEY(atom); - JS_ASSERT(VALUE_IS_FUNCTION(cx, rval)); - - /* - * 1. Create a new object as if by the expression new Object(). - * 2. Add Result(1) to the front of the scope chain. - * - * Step 2 is achieved by making the new object's parent be the - * current scope chain, and then making the new object the parent - * of the Function object clone. - */ - SAVE_SP_AND_PC(fp); - obj2 = js_GetScopeChain(cx, fp); - if (!obj2) { - ok = JS_FALSE; - goto out; - } - parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); - if (!parent) { - ok = JS_FALSE; - goto out; - } - - /* - * 3. Create a new Function object as specified in section 13.2 - * with [parameters and body specified by the function expression - * that was parsed by the compiler into a Function object, and - * saved in the script's atom map]. - * - * Protect parent from GC after js_CloneFunctionObject calls into - * js_NewObject, which displaces the newborn object root in cx by - * allocating the clone, then runs a last-ditch GC while trying - * to allocate the clone's slots vector. Another, multi-threaded - * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS - * which may suspend the current request in ClaimScope, with the - * newborn displaced as in the first scenario. - */ - fp->scopeChain = parent; - obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); - if (!obj) { - ok = JS_FALSE; - goto out; - } - - /* - * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All - * paths from here must flow through the "Restore fp->scopeChain" - * code below the OBJ_DEFINE_PROPERTY call. - */ - fp->scopeChain = obj; - rval = OBJECT_TO_JSVAL(obj); - - /* - * 4. Create a property in the object Result(1). The property's - * name is [fun->atom, the identifier parsed by the compiler], - * value is Result(3), and attributes are { DontDelete, ReadOnly }. - */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - attrs = JSFUN_GSFLAG2ATTR(fun->flags); - if (attrs) { - attrs |= JSPROP_SHARED; - rval = JSVAL_VOID; - } - ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, - (attrs & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - (attrs & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - attrs | - JSPROP_ENUMERATE | JSPROP_PERMANENT | - JSPROP_READONLY, - NULL); - - /* Restore fp->scopeChain now that obj is defined in parent. */ - fp->scopeChain = obj2; - if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - goto out; - } - - /* - * 5. Remove Result(1) from the front of the scope chain [no-op]. - * 6. Return Result(3). - */ - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_LITOPX_CASE(JSOP_NAMEDFUNOBJ) - - BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) - /* - * ECMA ed. 3 extension: a named function expression in a compound - * statement (not at the top statement level of global code, or at - * the top level of a function body). - * - * Get immediate operand atom, which is a function object literal. - * From it, get the function to close. - */ - JS_ASSERT(VALUE_IS_FUNCTION(cx, ATOM_KEY(atom))); - obj = ATOM_TO_OBJECT(atom); - - /* - * Clone the function object with the current scope chain as the - * clone's parent. The original function object is the prototype - * of the clone. Do this only if re-parenting; the compiler may - * have seen the right parent already and created a sufficiently - * well-scoped function object. - */ - SAVE_SP_AND_PC(fp); - obj2 = js_GetScopeChain(cx, fp); - if (!obj2) { - ok = JS_FALSE; - goto out; - } - if (OBJ_GET_PARENT(cx, obj) != obj2) { - obj = js_CloneFunctionObject(cx, obj, obj2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - } - - /* - * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All - * paths from here must flow through the "Restore fp->scopeChain" - * code below the OBJ_DEFINE_PROPERTY call. - */ - fp->scopeChain = obj; - rval = OBJECT_TO_JSVAL(obj); - - /* - * Make a property in fp->varobj with id fun->atom and value obj, - * unless fun is a getter or setter (in which case, obj is cast to - * a JSPropertyOp and passed accordingly). - */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - attrs = JSFUN_GSFLAG2ATTR(fun->flags); - if (attrs) { - attrs |= JSPROP_SHARED; - rval = JSVAL_VOID; - } - parent = fp->varobj; - ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, - (attrs & JSPROP_GETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - (attrs & JSPROP_SETTER) - ? JS_EXTENSION (JSPropertyOp) obj - : NULL, - attrs | JSPROP_ENUMERATE - | JSPROP_PERMANENT, - &prop); - - /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ - fp->scopeChain = obj2; - if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - goto out; - } - -#if 0 - if (attrs == 0 && script->numGlobalVars) { - /* - * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals - * use fp->vars to map the global function name's atomIndex to - * its permanent fp->varobj slot number, tagged as a jsval. - */ - sprop = (JSScopeProperty *) prop; - fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); - } -#endif - OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE(JSOP_CLOSURE) - -#if JS_HAS_GETTER_SETTER - BEGIN_CASE(JSOP_GETTER) - BEGIN_CASE(JSOP_SETTER) - op2 = (JSOp) *++pc; - switch (op2) { - case JSOP_SETNAME: - case JSOP_SETPROP: - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - rval = FETCH_OPND(-1); - i = -1; - goto gs_pop_lval; - - case JSOP_SETELEM: - rval = FETCH_OPND(-1); - FETCH_ELEMENT_ID(-2, id); - i = -2; - gs_pop_lval: - FETCH_OBJECT(cx, i - 1, lval, obj); - break; - - case JSOP_INITPROP: - JS_ASSERT(sp - fp->spbase >= 2); - rval = FETCH_OPND(-1); - i = -1; - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - goto gs_get_lval; - - case JSOP_INITELEM: - JS_ASSERT(sp - fp->spbase >= 3); - rval = FETCH_OPND(-1); - FETCH_ELEMENT_ID(-2, id); - i = -2; - gs_get_lval: - lval = FETCH_OPND(i-1); - JS_ASSERT(JSVAL_IS_OBJECT(lval)); - obj = JSVAL_TO_OBJECT(lval); - break; - - default: - JS_ASSERT(0); - } - - /* Ensure that id has a type suitable for use with obj. */ - CHECK_ELEMENT_ID(obj, id); - - SAVE_SP_AND_PC(fp); - if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - (op == JSOP_GETTER) - ? js_getter_str - : js_setter_str); - ok = JS_FALSE; - goto out; - } - - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); - if (!ok) - goto out; - - if (op == JSOP_GETTER) { - getter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); - setter = NULL; - attrs = JSPROP_GETTER; - } else { - getter = NULL; - setter = JS_EXTENSION (JSPropertyOp) JSVAL_TO_OBJECT(rval); - attrs = JSPROP_SETTER; - } - attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; - - /* Check for a readonly or permanent property of the same name. */ - ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL); - if (!ok) - goto out; - - ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, - attrs, NULL); - if (!ok) - goto out; - - obj = NULL; - sp += i; - if (js_CodeSpec[op2].ndefs) - STORE_OPND(-1, rval); - len = js_CodeSpec[op2].length; - DO_NEXT_OP(len); -#endif /* JS_HAS_GETTER_SETTER */ - - BEGIN_CASE(JSOP_NEWINIT) - argc = 0; - fp->sharpDepth++; - goto do_new; - - BEGIN_CASE(JSOP_ENDINIT) - if (--fp->sharpDepth == 0) - fp->sharpArray = NULL; - - /* Re-set the newborn root to the top of this object tree. */ - JS_ASSERT(sp - fp->spbase >= 1); - lval = FETCH_OPND(-1); - JS_ASSERT(JSVAL_IS_OBJECT(lval)); - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); - END_CASE(JSOP_ENDINIT) - - BEGIN_CASE(JSOP_INITPROP) - /* Pop the property's value into rval. */ - JS_ASSERT(sp - fp->spbase >= 2); - rval = FETCH_OPND(-1); - - /* Get the immediate property name into id. */ - atom = GET_ATOM(cx, script, pc); - id = ATOM_TO_JSID(atom); - i = -1; - goto do_init; - - BEGIN_CASE(JSOP_INITELEM) - /* Pop the element's value into rval. */ - JS_ASSERT(sp - fp->spbase >= 3); - rval = FETCH_OPND(-1); - - /* Pop and conditionally atomize the element id. */ - FETCH_ELEMENT_ID(-2, id); - i = -2; - - do_init: - /* Find the object being initialized at top of stack. */ - lval = FETCH_OPND(i-1); - JS_ASSERT(JSVAL_IS_OBJECT(lval)); - obj = JSVAL_TO_OBJECT(lval); - - /* Ensure that id has a type suitable for use with obj. */ - CHECK_ELEMENT_ID(obj, id); - - /* Set the property named by obj[id] to rval. */ - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - sp += i; - len = js_CodeSpec[op].length; - DO_NEXT_OP(len); - -#if JS_HAS_SHARP_VARS - BEGIN_CASE(JSOP_DEFSHARP) - SAVE_SP_AND_PC(fp); - obj = fp->sharpArray; - if (!obj) { - obj = js_NewArrayObject(cx, 0, NULL); - if (!obj) { - ok = JS_FALSE; - goto out; - } - fp->sharpArray = obj; - } - i = (jsint) GET_ATOM_INDEX(pc); - id = INT_TO_JSID(i); - rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval)) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SHARP_DEF, numBuf); - ok = JS_FALSE; - goto out; - } - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - END_CASE(JSOP_DEFSHARP) - - BEGIN_CASE(JSOP_USESHARP) - i = (jsint) GET_ATOM_INDEX(pc); - id = INT_TO_JSID(i); - obj = fp->sharpArray; - if (!obj) { - rval = JSVAL_VOID; - } else { - SAVE_SP_AND_PC(fp); - ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - } - if (!JSVAL_IS_OBJECT(rval)) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); - - SAVE_SP_AND_PC(fp); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SHARP_USE, numBuf); - ok = JS_FALSE; - goto out; - } - PUSH_OPND(rval); - END_CASE(JSOP_USESHARP) -#endif /* JS_HAS_SHARP_VARS */ - - /* No-ops for ease of decompilation and jit'ing. */ - EMPTY_CASE(JSOP_TRY) - EMPTY_CASE(JSOP_FINALLY) - - /* Reset the stack to the given depth. */ - BEGIN_CASE(JSOP_SETSP) - i = (jsint) GET_ATOM_INDEX(pc); - JS_ASSERT(i >= 0); - - for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) { - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); - if (OBJ_BLOCK_DEPTH(cx, obj) + (jsint)OBJ_BLOCK_COUNT(cx, obj) <= i) { - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) < i || OBJ_BLOCK_COUNT(cx, obj) == 0); - break; - } - } - fp->blockChain = obj; - - JS_ASSERT(ok); - for (obj = fp->scopeChain; - (clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass || - clasp == &js_BlockClass; - obj = OBJ_GET_PARENT(cx, obj)) { - if (JS_GetPrivate(cx, obj) != fp || - OBJ_BLOCK_DEPTH(cx, obj) < i) { - break; - } - if (clasp == &js_BlockClass) - ok &= js_PutBlockObject(cx, obj); - else - JS_SetPrivate(cx, obj, NULL); - } - - fp->scopeChain = obj; - - /* Set sp after js_PutBlockObject to avoid potential GC hazards. */ - sp = fp->spbase + i; - - /* Don't fail until after we've updated all stacks. */ - if (!ok) - goto out; - END_CASE(JSOP_SETSP) - - BEGIN_CASE(JSOP_GOSUB) - JS_ASSERT(cx->exception != JSVAL_HOLE); - if (!cx->throwing) { - lval = JSVAL_HOLE; - } else { - lval = cx->exception; - cx->throwing = JS_FALSE; - } - PUSH(lval); - i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH; - len = GET_JUMP_OFFSET(pc); - PUSH(INT_TO_JSVAL(i)); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_GOSUBX) - JS_ASSERT(cx->exception != JSVAL_HOLE); - if (!cx->throwing) { - lval = JSVAL_HOLE; - } else { - lval = cx->exception; - cx->throwing = JS_FALSE; - } - PUSH(lval); - i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH; - len = GET_JUMPX_OFFSET(pc); - PUSH(INT_TO_JSVAL(i)); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_RETSUB) - rval = POP(); - JS_ASSERT(JSVAL_IS_INT(rval)); - lval = POP(); - if (lval != JSVAL_HOLE) { - /* - * Exception was pending during finally, throw it *before* we - * adjust pc, because pc indexes into script->trynotes. This - * turns out not to be necessary, but it seems clearer. And - * it points out a FIXME: 350509, due to Igor Bukanov. - */ - cx->throwing = JS_TRUE; - cx->exception = lval; - ok = JS_FALSE; - goto out; - } - len = JSVAL_TO_INT(rval); - pc = script->main; - END_VARLEN_CASE - - BEGIN_CASE(JSOP_EXCEPTION) - JS_ASSERT(cx->throwing); - PUSH(cx->exception); - cx->throwing = JS_FALSE; - END_CASE(JSOP_EXCEPTION) - - BEGIN_CASE(JSOP_THROWING) - JS_ASSERT(!cx->throwing); - cx->throwing = JS_TRUE; - cx->exception = POP_OPND(); - END_CASE(JSOP_THROWING) - - BEGIN_CASE(JSOP_THROW) - JS_ASSERT(!cx->throwing); - cx->throwing = JS_TRUE; - cx->exception = POP_OPND(); - ok = JS_FALSE; - /* let the code at out try to catch the exception. */ - goto out; - - BEGIN_CASE(JSOP_SETLOCALPOP) - /* - * The stack must have a block with at least one local slot below - * the exception object. - */ - JS_ASSERT(sp - fp->spbase >= 2); - slot = GET_UINT16(pc); - JS_ASSERT(slot + 1 < (uintN)depth); - fp->spbase[slot] = POP_OPND(); - END_CASE(JSOP_SETLOCALPOP) - - BEGIN_CASE(JSOP_INSTANCEOF) - SAVE_SP_AND_PC(fp); - rval = FETCH_OPND(-1); - if (JSVAL_IS_PRIMITIVE(rval) || - !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { - str = js_DecompileValueGenerator(cx, -1, rval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INSTANCEOF_RHS, - JS_GetStringBytes(str)); - } - ok = JS_FALSE; - goto out; - } - lval = FETCH_OPND(-2); - cond = JS_FALSE; - ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); - if (!ok) - goto out; - sp--; - STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); - END_CASE(JSOP_INSTANCEOF) - -#if JS_HAS_DEBUGGER_KEYWORD - BEGIN_CASE(JSOP_DEBUGGER) - { - JSTrapHandler handler = rt->debuggerHandler; - if (handler) { - SAVE_SP_AND_PC(fp); - switch (handler(cx, script, pc, &rval, - rt->debuggerHandlerData)) { - case JSTRAP_ERROR: - ok = JS_FALSE; - goto out; - case JSTRAP_CONTINUE: - break; - case JSTRAP_RETURN: - fp->rval = rval; - goto out; - case JSTRAP_THROW: - cx->throwing = JS_TRUE; - cx->exception = rval; - ok = JS_FALSE; - goto out; - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - } - END_CASE(JSOP_DEBUGGER) -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - BEGIN_CASE(JSOP_DEFXMLNS) - rval = POP(); - SAVE_SP_AND_PC(fp); - ok = js_SetDefaultXMLNamespace(cx, rval); - if (!ok) - goto out; - END_CASE(JSOP_DEFXMLNS) - - BEGIN_CASE(JSOP_ANYNAME) - SAVE_SP_AND_PC(fp); - ok = js_GetAnyName(cx, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - END_CASE(JSOP_ANYNAME) - - BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) - PUSH_OPND(ATOM_KEY(atom)); - END_LITOPX_CASE(JSOP_QNAMEPART) - - BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) - rval = ATOM_KEY(atom); - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - obj = js_ConstructXMLQNameObject(cx, lval, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_QNAMECONST) - - BEGIN_CASE(JSOP_QNAME) - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); - SAVE_SP_AND_PC(fp); - obj = js_ConstructXMLQNameObject(cx, lval, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - sp--; - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_QNAME) - - BEGIN_CASE(JSOP_TOATTRNAME) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_ToAttributeName(cx, &rval); - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_CASE(JSOP_TOATTRNAME) - - BEGIN_CASE(JSOP_TOATTRVAL) - rval = FETCH_OPND(-1); - JS_ASSERT(JSVAL_IS_STRING(rval)); - SAVE_SP_AND_PC(fp); - str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); - if (!str) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_TOATTRVAL) - - BEGIN_CASE(JSOP_ADDATTRNAME) - BEGIN_CASE(JSOP_ADDATTRVAL) - rval = FETCH_OPND(-1); - lval = FETCH_OPND(-2); - str = JSVAL_TO_STRING(lval); - str2 = JSVAL_TO_STRING(rval); - SAVE_SP_AND_PC(fp); - str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); - if (!str) { - ok = JS_FALSE; - goto out; - } - sp--; - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_ADDATTRNAME) - - BEGIN_CASE(JSOP_BINDXMLNAME) - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_FindXMLProperty(cx, lval, &obj, &rval); - if (!ok) - goto out; - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - PUSH_OPND(rval); - END_CASE(JSOP_BINDXMLNAME) - - BEGIN_CASE(JSOP_SETXMLNAME) - obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); - lval = FETCH_OPND(-2); - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_SetXMLProperty(cx, obj, lval, &rval); - if (!ok) - goto out; - sp -= 2; - STORE_OPND(-1, rval); - obj = NULL; - END_CASE(JSOP_SETXMLNAME) - - BEGIN_CASE(JSOP_XMLNAME) - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_FindXMLProperty(cx, lval, &obj, &rval); - if (!ok) - goto out; - ok = js_GetXMLProperty(cx, obj, rval, &rval); - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_CASE(JSOP_XMLNAME) - - BEGIN_CASE(JSOP_DESCENDANTS) - BEGIN_CASE(JSOP_DELDESC) - FETCH_OBJECT(cx, -2, lval, obj); - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - ok = js_GetXMLDescendants(cx, obj, rval, &rval); - if (!ok) - goto out; - - if (op == JSOP_DELDESC) { - sp[-1] = rval; /* set local root */ - ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); - if (!ok) - goto out; - rval = JSVAL_TRUE; /* always succeed */ - } - - sp--; - STORE_OPND(-1, rval); - END_CASE(JSOP_DESCENDANTS) - - BEGIN_CASE(JSOP_FILTER) - FETCH_OBJECT(cx, -1, lval, obj); - len = GET_JUMP_OFFSET(pc); - SAVE_SP_AND_PC(fp); - ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval); - if (!ok) - goto out; - JS_ASSERT(fp->sp == sp); - STORE_OPND(-1, rval); - END_VARLEN_CASE - - BEGIN_CASE(JSOP_ENDFILTER) - *result = POP_OPND(); - goto out; - - EMPTY_CASE(JSOP_STARTXML) - EMPTY_CASE(JSOP_STARTXMLEXPR) - - BEGIN_CASE(JSOP_TOXML) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - obj = js_ValueToXMLObject(cx, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_TOXML) - - BEGIN_CASE(JSOP_TOXMLLIST) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - obj = js_ValueToXMLListObject(cx, rval); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_CASE(JSOP_TOXMLLIST) - - BEGIN_CASE(JSOP_XMLTAGEXPR) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - str = js_ValueToString(cx, rval); - if (!str) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_XMLTAGEXPR) - - BEGIN_CASE(JSOP_XMLELTEXPR) - rval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - if (VALUE_IS_XML(cx, rval)) { - str = js_ValueToXMLString(cx, rval); - } else { - str = js_ValueToString(cx, rval); - if (str) - str = js_EscapeElementValue(cx, str); - } - if (!str) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, STRING_TO_JSVAL(str)); - END_CASE(JSOP_XMLELTEXPR) - - BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) - SAVE_SP_AND_PC(fp); - obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - obj = NULL; - END_LITOPX_CASE(JSOP_XMLOBJECT) - - BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) - str = ATOM_TO_STRING(atom); - obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLCDATA) - - BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) - str = ATOM_TO_STRING(atom); - obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); - if (!obj) { - ok = JS_FALSE; - goto out; - } - PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLCOMMENT) - - BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) - str = ATOM_TO_STRING(atom); - rval = FETCH_OPND(-1); - str2 = JSVAL_TO_STRING(rval); - SAVE_SP_AND_PC(fp); - obj = js_NewXMLSpecialObject(cx, - JSXML_CLASS_PROCESSING_INSTRUCTION, - str, str2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE(JSOP_XMLPI) - - BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) - /* Get an immediate atom naming the property. */ - id = ATOM_TO_JSID(atom); - lval = FETCH_OPND(-1); - SAVE_SP_AND_PC(fp); - if (!JSVAL_IS_PRIMITIVE(lval)) { - STORE_OPND(-1, lval); - obj = JSVAL_TO_OBJECT(lval); - - /* Special-case XML object method lookup, per ECMA-357. */ - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - obj = ops->getMethod(cx, obj, id, &rval); - if (!obj) - ok = JS_FALSE; - } else { - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - } - } else { - if (JSVAL_IS_STRING(lval)) { - i = JSProto_String; - } else if (JSVAL_IS_NUMBER(lval)) { - i = JSProto_Number; - } else if (JSVAL_IS_BOOLEAN(lval)) { - i = JSProto_Boolean; - } else { - JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)); - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - lval, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_PROPERTIES, - JS_GetStringBytes(str)); - } - ok = JS_FALSE; - goto out; - } - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj); - if (!ok) - goto out; - JS_ASSERT(obj); - STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); - obj = (JSObject *) lval; /* keep tagged as non-object */ - } - if (!ok) - goto out; - STORE_OPND(-1, rval); - END_LITOPX_CASE(JSOP_GETMETHOD) - - BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) - /* Get an immediate atom naming the property. */ - id = ATOM_TO_JSID(atom); - rval = FETCH_OPND(-1); - FETCH_OBJECT(cx, -2, lval, obj); - SAVE_SP_AND_PC(fp); - - /* Special-case XML object method lookup, per ECMA-357. */ - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - ok = ops->setMethod(cx, obj, id, &rval); - } else { - CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); - } - if (!ok) - goto out; - --sp; - STORE_OPND(-1, rval); - obj = NULL; - END_LITOPX_CASE(JSOP_SETMETHOD) - - BEGIN_CASE(JSOP_GETFUNNS) - SAVE_SP_AND_PC(fp); - ok = js_GetFunctionNamespace(cx, &rval); - if (!ok) - goto out; - PUSH_OPND(rval); - END_CASE(JSOP_GETFUNNS) -#endif /* JS_HAS_XML_SUPPORT */ - - BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK, 0) - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); - vp = sp + OBJ_BLOCK_COUNT(cx, obj); - JS_ASSERT(vp <= fp->spbase + depth); - while (sp < vp) { - STORE_OPND(0, JSVAL_VOID); - sp++; - } - - /* - * If this frame had to reflect the compile-time block chain into - * the runtime scope chain, we can't optimize block scopes out of - * runtime any longer, because an outer block that parents obj has - * been cloned onto the scope chain. To avoid re-cloning such a - * parent and accumulating redundant clones via js_GetScopeChain, - * we must clone each block eagerly on entry, and push it on the - * scope chain, until this frame pops. - */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - JS_ASSERT(!fp->blockChain); - - /* - * Check whether JSOP_DEFLOCALFUN emulated JSOP_ENTERBLOCK for - * the body block in order to correctly scope the local cloned - * function object it creates. - */ - parent = fp->scopeChain; - if (OBJ_GET_PROTO(cx, parent) == obj) { - JS_ASSERT(OBJ_GET_CLASS(cx, parent) == &js_BlockClass); - } else { - obj = js_CloneBlockObject(cx, obj, parent, fp); - if (!obj) { - ok = JS_FALSE; - goto out; - } - fp->scopeChain = obj; - } - } else { - JS_ASSERT(!fp->blockChain || - OBJ_GET_PARENT(cx, obj) == fp->blockChain); - fp->blockChain = obj; - } - END_LITOPX_CASE(JSOP_ENTERBLOCK) - - BEGIN_CASE(JSOP_LEAVEBLOCKEXPR) - BEGIN_CASE(JSOP_LEAVEBLOCK) - { - JSObject **chainp; - - /* Grab the result of the expression. */ - if (op == JSOP_LEAVEBLOCKEXPR) - rval = FETCH_OPND(-1); - - chainp = &fp->blockChain; - obj = *chainp; - if (!obj) { - chainp = &fp->scopeChain; - obj = *chainp; - - /* - * This block was cloned, so clear its private data and sync - * its locals to their property slots. - */ - SAVE_SP_AND_PC(fp); - ok = js_PutBlockObject(cx, obj); - if (!ok) - goto out; - } - - sp -= GET_UINT16(pc); - JS_ASSERT(fp->spbase <= sp && sp <= fp->spbase + depth); - - /* Store the result into the topmost stack slot. */ - if (op == JSOP_LEAVEBLOCKEXPR) - STORE_OPND(-1, rval); - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass); - JS_ASSERT(op == JSOP_LEAVEBLOCKEXPR - ? fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp - 1 - : fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp); - - *chainp = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(chainp != &fp->blockChain || - !*chainp || - OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass); - } - END_CASE(JSOP_LEAVEBLOCK) - - BEGIN_CASE(JSOP_GETLOCAL) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - PUSH_OPND(fp->spbase[slot]); - obj = NULL; - END_CASE(JSOP_GETLOCAL) - - BEGIN_CASE(JSOP_SETLOCAL) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - vp = &fp->spbase[slot]; - GC_POKE(cx, *vp); - *vp = FETCH_OPND(-1); - obj = NULL; - END_CASE(JSOP_SETLOCAL) - -/* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */ -#define FAST_LOCAL_INCREMENT_OP(PRE,OPEQ,MINMAX) \ - slot = GET_UINT16(pc); \ - JS_ASSERT(slot < (uintN)depth); \ - vp = fp->spbase + slot; \ - rval = *vp; \ - if (!JSVAL_IS_INT(rval) || rval == INT_TO_JSVAL(JSVAL_INT_##MINMAX)) \ - goto do_nonint_fast_incop; \ - PRE = rval; \ - rval OPEQ 2; \ - *vp = rval; \ - PUSH_OPND(PRE) - - BEGIN_CASE(JSOP_INCLOCAL) - FAST_LOCAL_INCREMENT_OP(rval, +=, MAX); - END_CASE(JSOP_INCLOCAL) - - BEGIN_CASE(JSOP_DECLOCAL) - FAST_LOCAL_INCREMENT_OP(rval, -=, MIN); - END_CASE(JSOP_DECLOCAL) - - BEGIN_CASE(JSOP_LOCALINC) - FAST_LOCAL_INCREMENT_OP(rtmp, +=, MAX); - END_CASE(JSOP_LOCALINC) - - BEGIN_CASE(JSOP_LOCALDEC) - FAST_LOCAL_INCREMENT_OP(rtmp, -=, MIN); - END_CASE(JSOP_LOCALDEC) - -#undef FAST_LOCAL_INCREMENT_OP - - EMPTY_CASE(JSOP_STARTITER) - - BEGIN_CASE(JSOP_ENDITER) - JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); - iterobj = JSVAL_TO_OBJECT(sp[-1]); - - /* - * js_CloseNativeIterator checks whether the iterator is not - * native, and also detects the case of a native iterator that - * has already escaped, even though a for-in loop caused it to - * be created. See jsiter.c. - */ - SAVE_SP_AND_PC(fp); - js_CloseNativeIterator(cx, iterobj); - *--sp = JSVAL_NULL; - END_CASE(JSOP_ENDITER) - -#if JS_HAS_GENERATORS - BEGIN_CASE(JSOP_GENERATOR) - pc += JSOP_GENERATOR_LENGTH; - SAVE_SP_AND_PC(fp); - obj = js_NewGenerator(cx, fp); - if (!obj) { - ok = JS_FALSE; - } else { - JS_ASSERT(!fp->callobj && !fp->argsobj); - fp->rval = OBJECT_TO_JSVAL(obj); - } - goto out; - - BEGIN_CASE(JSOP_YIELD) - ASSERT_NOT_THROWING(cx); - if (fp->flags & JSFRAME_FILTERING) { - /* FIXME: bug 309894 -- fix to eliminate this error. */ - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_YIELD_FROM_FILTER); - ok = JS_FALSE; - goto out; - } - if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - fp->argv[-2], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_YIELD, - JSSTRING_CHARS(str)); - } - ok = JS_FALSE; - goto out; - } - fp->rval = FETCH_OPND(-1); - fp->flags |= JSFRAME_YIELDING; - pc += JSOP_YIELD_LENGTH; - SAVE_SP_AND_PC(fp); - goto out; - - BEGIN_CASE(JSOP_ARRAYPUSH) - slot = GET_UINT16(pc); - JS_ASSERT(slot < (uintN)depth); - lval = fp->spbase[slot]; - obj = JSVAL_TO_OBJECT(lval); - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); - rval = FETCH_OPND(-1); - - /* We know that the array is created with only a 'length' slot. */ - i = obj->map->freeslot - (JSSLOT_FREE(&js_ArrayClass) + 1); - id = INT_TO_JSID(i); - - SAVE_SP_AND_PC(fp); - ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); - if (!ok) - goto out; - --sp; - END_CASE(JSOP_ARRAYPUSH) -#endif /* JS_HAS_GENERATORS */ - -#if !JS_HAS_GENERATORS - L_JSOP_GENERATOR: - L_JSOP_YIELD: - L_JSOP_ARRAYPUSH: -#endif - -#if !JS_HAS_DESTRUCTURING - L_JSOP_FOREACHKEYVAL: - L_JSOP_ENUMCONSTELEM: -#endif - -#ifdef JS_THREADED_INTERP - L_JSOP_BACKPATCH: - L_JSOP_BACKPATCH_POP: -#else - default: -#endif - { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", op); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_BYTECODE, numBuf); - ok = JS_FALSE; - goto out; - } - -#ifndef JS_THREADED_INTERP - - } /* switch (op) */ - - advance_pc: - pc += len; - -#ifdef DEBUG - if (tracefp) { - intN ndefs, n; - jsval *siter; - - ndefs = js_CodeSpec[op].ndefs; - if (ndefs) { - SAVE_SP_AND_PC(fp); - if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) - --ndefs; - for (n = -ndefs; n < 0; n++) { - str = js_DecompileValueGenerator(cx, n, sp[n], NULL); - if (str) { - fprintf(tracefp, "%s %s", - (n == -ndefs) ? " output:" : ",", - JS_GetStringBytes(str)); - } - } - fprintf(tracefp, " @ %d\n", sp - fp->spbase); - } - fprintf(tracefp, " stack: "); - for (siter = fp->spbase; siter < sp; siter++) { - str = js_ValueToSource(cx, *siter); - fprintf(tracefp, "%s ", - str ? JS_GetStringBytes(str) : ""); - } - fputc('\n', tracefp); - } -#endif /* DEBUG */ - } -#endif /* !JS_THREADED_INTERP */ - -out: - if (!ok) { - /* - * Has an exception been raised? Also insist that we are not in an - * XML filtering predicate expression, to avoid catching exceptions - * within the filtering predicate, such as this example taken from - * tests/e4x/Regress/regress-301596.js: - * - * try { - * .(@a == 1); - * throw 5; - * } catch (e) { - * } - * - * The inner interpreter activation executing the predicate bytecode - * will throw "reference to undefined XML name @a" (or 5, in older - * versions that followed the first edition of ECMA-357 and evaluated - * unbound identifiers to undefined), and the exception must not be - * caught until control unwinds to the outer interpreter activation. - * - * Otherwise, the wrong stack depth will be restored by JSOP_SETSP, - * and the catch will move into the filtering predicate expression, - * leading to double catch execution if it rethrows. - * - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 - */ - if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) { - /* - * Call debugger throw hook if set (XXX thread safety?). - */ - JSTrapHandler handler = rt->throwHook; - if (handler) { - SAVE_SP_AND_PC(fp); - switch (handler(cx, script, pc, &rval, rt->throwHookData)) { - case JSTRAP_ERROR: - cx->throwing = JS_FALSE; - goto no_catch; - case JSTRAP_RETURN: - ok = JS_TRUE; - cx->throwing = JS_FALSE; - fp->rval = rval; - goto no_catch; - case JSTRAP_THROW: - cx->exception = rval; - case JSTRAP_CONTINUE: - default:; - } - LOAD_INTERRUPT_HANDLER(rt); - } - - /* - * Look for a try block in script that can catch this exception. - */ -#if JS_HAS_GENERATORS - if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) { - SCRIPT_FIND_CATCH_START(script, pc, pc); - if (!pc) - goto no_catch; - } else { - pc = js_FindFinallyHandler(script, pc); - if (!pc) { - cx->throwing = JS_FALSE; - ok = JS_TRUE; - fp->rval = JSVAL_VOID; - goto no_catch; - } - } -#else - SCRIPT_FIND_CATCH_START(script, pc, pc); - if (!pc) - goto no_catch; -#endif - - /* Don't clear cx->throwing to save cx->exception from GC. */ - len = 0; - ok = JS_TRUE; - DO_NEXT_OP(len); - } -no_catch:; - } - - /* - * Check whether control fell off the end of a lightweight function, or an - * exception thrown under such a function was not caught by it. If so, go - * to the inline code under JSOP_RETURN. - */ - if (inlineCallCount) - goto inline_return; - - /* - * Reset sp before freeing stack slots, because our caller may GC soon. - * Clear spbase to indicate that we've popped the 2 * depth operand slots. - * Restore the previous frame's execution state. - */ - if (JS_LIKELY(mark != NULL)) { - /* If fp has blocks on its scope chain, home their locals now. */ - if (fp->flags & JSFRAME_POP_BLOCKS) { - SAVE_SP_AND_PC(fp); - ok &= PutBlockObjects(cx, fp); - } - - fp->sp = fp->spbase; - fp->spbase = NULL; - js_FreeRawStack(cx, mark); - } else { - SAVE_SP(fp); - } - -out2: - if (cx->version == currentVersion && currentVersion != originalVersion) - js_SetVersion(cx, originalVersion); - cx->interpLevel--; - return ok; - -atom_not_defined: - { - const char *printable = js_AtomToPrintableString(cx, atom); - if (printable) - js_ReportIsNotDefined(cx, printable); - ok = JS_FALSE; - goto out; - } -} diff --git a/spidermonkey/libjs/jsinterp.h b/spidermonkey/libjs/jsinterp.h deleted file mode 100644 index ab60b3a..0000000 --- a/spidermonkey/libjs/jsinterp.h +++ /dev/null @@ -1,361 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsinterp_h___ -#define jsinterp_h___ -/* - * JS interpreter interface. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -/* - * JS stack frame, may be allocated on the C stack by native callers. Always - * allocated on cx->stackPool for calls from the interpreter to an interpreted - * function. - * - * NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you - * add new members, update both files. But first, try to remove members. The - * sharp* and xml* members should be moved onto the stack as local variables - * with well-known slots, if possible. - */ -struct JSStackFrame { - JSObject *callobj; /* lazily created Call object */ - JSObject *argsobj; /* lazily created arguments object */ - JSObject *varobj; /* variables object, where vars go */ - JSScript *script; /* script being interpreted */ - JSFunction *fun; /* function being called or null */ - JSObject *thisp; /* "this" pointer if in method */ - uintN argc; /* actual argument count */ - jsval *argv; /* base of argument stack slots */ - jsval rval; /* function return value */ - uintN nvars; /* local variable count */ - jsval *vars; /* base of variable stack slots */ - JSStackFrame *down; /* previous frame */ - void *annotation; /* used by Java security */ - JSObject *scopeChain; /* scope chain */ - jsbytecode *pc; /* program counter */ - jsval *sp; /* stack pointer */ - jsval *spbase; /* operand stack base */ - uintN sharpDepth; /* array/object initializer depth */ - JSObject *sharpArray; /* scope for #n= initializer vars */ - uint32 flags; /* frame flags -- see below */ - JSStackFrame *dormantNext; /* next dormant frame chain */ - JSObject *xmlNamespace; /* null or default xml namespace in E4X */ - JSObject *blockChain; /* active compile-time block scopes */ -}; - -typedef struct JSInlineFrame { - JSStackFrame frame; /* base struct */ - jsval *rvp; /* ptr to caller's return value slot */ - void *mark; /* mark before inline frame */ - void *hookData; /* debugger call hook data */ - JSVersion callerVersion; /* dynamic version of calling script */ -} JSInlineFrame; - -/* JS stack frame flags. */ -#define JSFRAME_CONSTRUCTING 0x01 /* frame is for a constructor invocation */ -#define JSFRAME_INTERNAL 0x02 /* internal call, not invoked by a script */ -#define JSFRAME_SKIP_CALLER 0x04 /* skip one link when evaluating f.caller - for this invocation of f */ -#define JSFRAME_ASSIGNING 0x08 /* a complex (not simplex JOF_ASSIGNING) op - is currently assigning to a property */ -#define JSFRAME_DEBUGGER 0x10 /* frame for JS_EvaluateInStackFrame */ -#define JSFRAME_EVAL 0x20 /* frame for obj_eval */ -#define JSFRAME_SPECIAL 0x30 /* special evaluation frame flags */ -#define JSFRAME_COMPILING 0x40 /* frame is being used by compiler */ -#define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name - references based on scope chain */ -#define JSFRAME_SCRIPT_OBJECT 0x100 /* compiling source for a Script object */ -#define JSFRAME_YIELDING 0x200 /* js_Interpret dispatched JSOP_YIELD */ -#define JSFRAME_FILTERING 0x400 /* XML filtering predicate expression */ -#define JSFRAME_ITERATOR 0x800 /* trying to get an iterator for for-in */ -#define JSFRAME_POP_BLOCKS 0x1000 /* scope chain contains blocks to pop */ -#define JSFRAME_GENERATOR 0x2000 /* frame belongs to generator-iterator */ - -#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ -#define JSFRAME_OVERRIDE_BITS 8 - -/* - * Property cache for quickened get/set property opcodes. - */ -#define PROPERTY_CACHE_LOG2 10 -#define PROPERTY_CACHE_SIZE JS_BIT(PROPERTY_CACHE_LOG2) -#define PROPERTY_CACHE_MASK JS_BITMASK(PROPERTY_CACHE_LOG2) - -#define PROPERTY_CACHE_HASH(obj, id) \ - ((((jsuword)(obj) >> JSVAL_TAGBITS) ^ (jsuword)(id)) & PROPERTY_CACHE_MASK) - -#ifdef JS_THREADSAFE - -#if HAVE_ATOMIC_DWORD_ACCESS - -#define PCE_LOAD(cache, pce, entry) JS_ATOMIC_DWORD_LOAD(pce, entry) -#define PCE_STORE(cache, pce, entry) JS_ATOMIC_DWORD_STORE(pce, entry) - -#else /* !HAVE_ATOMIC_DWORD_ACCESS */ - -#define JS_PROPERTY_CACHE_METERING 1 - -#define PCE_LOAD(cache, pce, entry) \ - JS_BEGIN_MACRO \ - uint32 prefills_; \ - uint32 fills_ = (cache)->fills; \ - do { \ - /* Load until cache->fills is stable (see FILL macro below). */ \ - prefills_ = fills_; \ - (entry) = *(pce); \ - } while ((fills_ = (cache)->fills) != prefills_); \ - JS_END_MACRO - -#define PCE_STORE(cache, pce, entry) \ - JS_BEGIN_MACRO \ - do { \ - /* Store until no racing collider stores half or all of pce. */ \ - *(pce) = (entry); \ - } while (PCE_OBJECT(*pce) != PCE_OBJECT(entry) || \ - PCE_PROPERTY(*pce) != PCE_PROPERTY(entry)); \ - JS_END_MACRO - -#endif /* !HAVE_ATOMIC_DWORD_ACCESS */ - -#else /* !JS_THREADSAFE */ - -#define PCE_LOAD(cache, pce, entry) ((entry) = *(pce)) -#define PCE_STORE(cache, pce, entry) (*(pce) = (entry)) - -#endif /* !JS_THREADSAFE */ - -typedef union JSPropertyCacheEntry { - struct { - JSObject *object; /* weak link to object */ - JSScopeProperty *property; /* weak link to property */ - } s; -#ifdef HAVE_ATOMIC_DWORD_ACCESS - prdword align; -#endif -} JSPropertyCacheEntry; - -/* These may be called in lvalue or rvalue position. */ -#define PCE_OBJECT(entry) ((entry).s.object) -#define PCE_PROPERTY(entry) ((entry).s.property) - -typedef struct JSPropertyCache { - JSPropertyCacheEntry table[PROPERTY_CACHE_SIZE]; - JSBool empty; - JSBool disabled; -#ifdef JS_PROPERTY_CACHE_METERING - uint32 fills; - uint32 recycles; - uint32 tests; - uint32 misses; - uint32 flushes; -# define PCMETER(x) x -#else -# define PCMETER(x) /* nothing */ -#endif -} JSPropertyCache; - -#define PROPERTY_CACHE_FILL(cache, obj, id, sprop) \ - JS_BEGIN_MACRO \ - JSPropertyCache *cache_ = (cache); \ - if (!cache_->disabled) { \ - uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ - JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ - JSPropertyCacheEntry entry_; \ - JSScopeProperty *pce_sprop_; \ - PCE_LOAD(cache_, pce_, entry_); \ - pce_sprop_ = PCE_PROPERTY(entry_); \ - PCMETER(if (pce_sprop_ && pce_sprop_ != sprop) \ - cache_->recycles++); \ - PCE_OBJECT(entry_) = obj; \ - PCE_PROPERTY(entry_) = sprop; \ - cache_->empty = JS_FALSE; \ - PCMETER(cache_->fills++); \ - PCE_STORE(cache_, pce_, entry_); \ - } \ - JS_END_MACRO - -#define PROPERTY_CACHE_TEST(cache, obj, id, sprop) \ - JS_BEGIN_MACRO \ - uintN hashIndex_ = (uintN) PROPERTY_CACHE_HASH(obj, id); \ - JSPropertyCache *cache_ = (cache); \ - JSPropertyCacheEntry *pce_ = &cache_->table[hashIndex_]; \ - JSPropertyCacheEntry entry_; \ - JSScopeProperty *pce_sprop_; \ - PCE_LOAD(cache_, pce_, entry_); \ - pce_sprop_ = PCE_PROPERTY(entry_); \ - PCMETER(cache_->tests++); \ - if (pce_sprop_ && \ - PCE_OBJECT(entry_) == obj && \ - pce_sprop_->id == id) { \ - sprop = pce_sprop_; \ - } else { \ - PCMETER(cache_->misses++); \ - sprop = NULL; \ - } \ - JS_END_MACRO - -extern void -js_FlushPropertyCache(JSContext *cx); - -extern void -js_DisablePropertyCache(JSContext *cx); - -extern void -js_EnablePropertyCache(JSContext *cx); - -extern JS_FRIEND_API(jsval *) -js_AllocStack(JSContext *cx, uintN nslots, void **markp); - -extern JS_FRIEND_API(void) -js_FreeStack(JSContext *cx, void *mark); - -extern JSBool -js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -#ifdef DUMP_CALL_TABLE -# define JSOPTION_LOGCALL_TOSOURCE JS_BIT(15) - -extern JSHashTable *js_CallTable; -extern size_t js_LogCallToSourceLimit; - -extern void js_DumpCallTable(JSContext *cx); -#endif - -/* - * Refresh and return fp->scopeChain. It may be stale if block scopes are - * active but not yet reflected by objects in the scope chain. If a block - * scope contains a with, eval, XML filtering predicate, or similar such - * dynamically scoped construct, then compile-time block scope at fp->blocks - * must reflect at runtime. - */ -extern JSObject * -js_GetScopeChain(JSContext *cx, JSStackFrame *fp); - -/* - * Compute the 'this' parameter for a call with nominal 'this' given by thisp - * and arguments including argv[-1] (nominal 'this') and argv[-2] (callee). - * Activation objects ("Call" objects not created with "new Call()", i.e., - * "Call" objects that have private data) may not be referred to by 'this', - * per ECMA-262, so js_ComputeThis censors them. - */ -extern JSObject * -js_ComputeThis(JSContext *cx, JSObject *thisp, jsval *argv); - -/* - * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp - * is non-null), and that the callee, |this| parameter, and actual arguments - * are already pushed on the stack under cx->fp->sp. - */ -extern JS_FRIEND_API(JSBool) -js_Invoke(JSContext *cx, uintN argc, uintN flags); - -/* - * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that - * we can share bits stored in JSStackFrame.flags and passed to: - * - * js_Invoke - * js_InternalInvoke - * js_ValueToFunction - * js_ValueToFunctionObject - * js_ValueToCallableObject - * js_ReportIsNotFunction - * - * See jsfun.h for the latter four and flag renaming macros. - */ -#define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING -#define JSINVOKE_INTERNAL JSFRAME_INTERNAL -#define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER -#define JSINVOKE_ITERATOR JSFRAME_ITERATOR - -/* - * Mask to isolate construct and iterator flags for use with jsfun.h functions. - */ -#define JSINVOKE_FUNFLAGS (JSINVOKE_CONSTRUCT | JSINVOKE_ITERATOR) - -/* - * "Internal" calls may come from C or C++ code using a JSContext on which no - * JS is running (!cx->fp), so they may need to push a dummy JSStackFrame. - */ -#define js_InternalCall(cx,obj,fval,argc,argv,rval) \ - js_InternalInvoke(cx, obj, fval, 0, argc, argv, rval) - -#define js_InternalConstruct(cx,obj,fval,argc,argv,rval) \ - js_InternalInvoke(cx, obj, fval, JSINVOKE_CONSTRUCT, argc, argv, rval) - -extern JSBool -js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, - uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, - JSAccessMode mode, uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_Execute(JSContext *cx, JSObject *chain, JSScript *script, - JSStackFrame *down, uintN flags, jsval *result); - -extern JSBool -js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, - JSObject **objp, JSProperty **propp); - -extern JSBool -js_StrictlyEqual(jsval lval, jsval rval); - -extern JSBool -js_InvokeConstructor(JSContext *cx, jsval *vp, uintN argc); - -extern JSBool -js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result); - -JS_END_EXTERN_C - -#endif /* jsinterp_h___ */ diff --git a/spidermonkey/libjs/jsiter.c b/spidermonkey/libjs/jsiter.c deleted file mode 100644 index 0a4de54..0000000 --- a/spidermonkey/libjs/jsiter.c +++ /dev/null @@ -1,1080 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JavaScript iterators. - */ -#include "jsstddef.h" -#include /* for memcpy */ -#include "jstypes.h" -#include "jsutil.h" -#include "jsarena.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsexn.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsiter.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsscope.h" -#include "jsscript.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -extern const char js_throw_str[]; /* from jsscan.h */ - -#define JSSLOT_ITER_STATE (JSSLOT_PRIVATE) -#define JSSLOT_ITER_FLAGS (JSSLOT_PRIVATE + 1) - -#if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS -#error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS. -#endif - -/* - * Shared code to close iterator's state either through an explicit call or - * when GC detects that the iterator is no longer reachable. - */ -void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj) -{ - jsval *slots; - jsval state, parent; - JSObject *iterable; - - JS_ASSERT(JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)); - slots = iterobj->slots; - - /* Avoid double work if js_CloseNativeIterator was called on obj. */ - state = slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - return; - - /* Protect against failure to fully initialize obj. */ - parent = slots[JSSLOT_PARENT]; - if (!JSVAL_IS_PRIMITIVE(parent)) { - iterable = JSVAL_TO_OBJECT(parent); -#if JS_HAS_XML_SUPPORT - if ((JSVAL_TO_INT(slots[JSSLOT_ITER_FLAGS]) & JSITER_FOREACH) && - OBJECT_IS_XML(cx, iterable)) { - ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state, - NULL, NULL); - } else -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL); - } - slots[JSSLOT_ITER_STATE] = JSVAL_NULL; -} - -JSClass js_IteratorClass = { - "Iterator", - JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */ - JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) -{ - jsval state; - JSBool ok; - - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - /* Initialize iterobj in case of enumerate hook failure. */ - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags); - if (!js_RegisterCloseableIterator(cx, iterobj)) - return JS_FALSE; - if (!obj) - return JS_TRUE; - - ok = -#if JS_HAS_XML_SUPPORT - ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj)) - ? ((JSXMLObjectOps *) obj->map->ops)-> - enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL) - : -#endif - OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL); - if (!ok) - return JS_FALSE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (flags & JSITER_ENUMERATE) { - /* - * The enumerating iterator needs the original object to suppress - * enumeration of deleted or shadowed prototype properties. Since the - * enumerator never escapes to scripts, we use the prototype slot to - * store the original object. - */ - JS_ASSERT(obj != iterobj); - iterobj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(obj); - } - return JS_TRUE; -} - -static JSBool -Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) -{ - JSBool keyonly; - uintN flags; - JSObject *obj; - - keyonly = JS_FALSE; - if (!js_ValueToBoolean(cx, argv[1], &keyonly)) - return JS_FALSE; - flags = keyonly ? 0 : JSITER_FOREACH; - - if (cx->fp->flags & JSFRAME_CONSTRUCTING) { - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(argv[0])) { - obj = JSVAL_TO_OBJECT(argv[0]); - } else { - obj = js_ValueToNonNullObject(cx, argv[0]); - if (!obj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(obj); - } - return InitNativeIterator(cx, iterobj, obj, flags); - } - - *rval = argv[0]; - return js_ValueToIterator(cx, flags, rval); -} - -static JSBool -NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) -{ - jsval vec[2]; - JSTempValueRooter tvr; - JSObject *aobj; - - vec[0] = ID_TO_VALUE(key); - vec[1] = val; - - JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr); - aobj = js_NewArrayObject(cx, 2, vec); - *rval = OBJECT_TO_JSVAL(aobj); - JS_POP_TEMP_ROOT(cx, &tvr); - - return aobj != NULL; -} - -static JSBool -IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) -{ - JSObject *iterable; - jsval state; - uintN flags; - JSBool foreach, ok; - jsid id; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); - - iterable = OBJ_GET_PARENT(cx, obj); - JS_ASSERT(iterable); - state = OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE); - if (JSVAL_IS_NULL(state)) - goto stop; - - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_FLAGS)); - JS_ASSERT(!(flags & JSITER_ENUMERATE)); - foreach = (flags & JSITER_FOREACH) != 0; - ok = -#if JS_HAS_XML_SUPPORT - (foreach && OBJECT_IS_XML(cx, iterable)) - ? ((JSXMLObjectOps *) iterable->map->ops)-> - enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, - &id, rval) - : -#endif - OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); - if (!ok) - return JS_FALSE; - - OBJ_SET_SLOT(cx, obj, JSSLOT_ITER_STATE, state); - if (JSVAL_IS_NULL(state)) - goto stop; - - if (foreach) { -#if JS_HAS_XML_SUPPORT - if (!OBJECT_IS_XML(cx, iterable) && - !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { - return JS_FALSE; - } -#endif - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } else { - *rval = ID_TO_VALUE(id); - } - return JS_TRUE; - - stop: - JS_ASSERT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE) == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -static JSBool -js_ThrowStopIteration(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(!JS_IsExceptionPending(cx)); - if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) - JS_SetPendingException(cx, v); - return JS_FALSE; -} - -static JSBool -iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) - return JS_FALSE; - - if (!IteratorNextImpl(cx, obj, rval)) - return JS_FALSE; - - if (*rval == JSVAL_HOLE) { - *rval = JSVAL_NULL; - js_ThrowStopIteration(cx, obj); - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -iterator_self(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec iterator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, iterator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -uintN -js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) -{ - if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass) - return 0; - return JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); -} - -void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) -{ - uintN flags; - - /* - * If this iterator is not an instance of the native default iterator - * class, leave it to be GC'ed. - */ - if (!JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL)) - return; - - /* - * If this iterator was not created by js_ValueToIterator called from the - * for-in loop code in js_Interpret, leave it to be GC'ed. - */ - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (!(flags & JSITER_ENUMERATE)) - return; - - js_CloseIteratorState(cx, iterobj); -} - -/* - * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. - * Otherwise construct the defualt iterator. - */ -JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) -{ - JSObject *obj; - JSTempValueRooter tvr; - const JSAtom *atom; - JSBool ok; - JSObject *iterobj; - jsval arg; - JSString *str; - - JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | - JSITER_FOREACH | - JSITER_KEYVALUE))); - - /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ - JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); - - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(*vp)) { - obj = JSVAL_TO_OBJECT(*vp); - } else { - /* - * Enumerating over null and undefined gives an empty enumerator. - * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of - * the first production in 12.6.4 and step 4 of the second production, - * but it's "web JS" compatible. - */ - if ((flags & JSITER_ENUMERATE)) { - if (!js_ValueToObject(cx, *vp, &obj)) - return JS_FALSE; - if (!obj) - goto default_iter; - } else { - obj = js_ValueToNonNullObject(cx, *vp); - if (!obj) - return JS_FALSE; - } - } - - JS_ASSERT(obj); - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - atom = cx->runtime->atomState.iteratorAtom; -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } else -#endif - { - if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) - goto bad; - } - - if (JSVAL_IS_VOID(*vp)) { - default_iter: - /* - * Fail over to the default enumerating native iterator. - * - * Create iterobj with a NULL parent to ensure that we use the correct - * scope chain to lookup the iterator's constructor. Since we use the - * parent slot to keep track of the iterable, we must fix it up after. - */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); - if (!iterobj) - goto bad; - - /* Store iterobj in *vp to protect it from GC (callers must root vp). */ - *vp = OBJECT_TO_JSVAL(iterobj); - - if (!InitNativeIterator(cx, iterobj, obj, flags)) - goto bad; - } else { - arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); - if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) - goto bad; - if (JSVAL_IS_PRIMITIVE(*vp)) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR_RETURN, - JSSTRING_CHARS(str), - JSSTRING_CHARS(ATOM_TO_STRING(atom))); - } - goto bad; - } - } - - ok = JS_TRUE; - out: - if (obj) - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; - bad: - ok = JS_FALSE; - goto out; -} - -static JSBool -CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) -{ - JSObject *obj, *origobj; - jsval state; - JSBool foreach; - jsid id; - JSObject *obj2; - JSBool cond; - JSClass *clasp; - JSExtendedClass *xclasp; - JSProperty *prop; - JSString *str; - - JS_ASSERT(flags & JSITER_ENUMERATE); - JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == - &js_IteratorClass); - - obj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PARENT]); - origobj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PROTO]); - state = iterobj->slots[JSSLOT_ITER_STATE]; - if (JSVAL_IS_NULL(state)) - goto stop; - - foreach = (flags & JSITER_FOREACH) != 0; -#if JS_HAS_XML_SUPPORT - /* - * Treat an XML object specially only when it starts the prototype chain. - * Otherwise we need to do the usual deleted and shadowed property checks. - */ - if (obj == origobj && OBJECT_IS_XML(cx, obj)) { - if (foreach) { - JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; - - if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, - &id, rval)) { - return JS_FALSE; - } - } else { - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_FALSE; - } - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) - goto stop; - } else -#endif - { - restart: - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) - return JS_TRUE; - - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (JSVAL_IS_NULL(state)) { -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - /* - * We just finished enumerating an XML obj that is present on - * the prototype chain of a non-XML origobj. Stop further - * prototype chain searches because XML objects don't - * enumerate prototypes. - */ - JS_ASSERT(origobj != obj); - JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); - } else -#endif - { - obj = OBJ_GET_PROTO(cx, obj); - if (obj) { - iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); - if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) - return JS_FALSE; - iterobj->slots[JSSLOT_ITER_STATE] = state; - if (!JSVAL_IS_NULL(state)) - goto restart; - } - } - goto stop; - } - - /* Skip properties not in obj when looking from origobj. */ - if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) - goto restart; - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* - * If the id was found in a prototype object or an unrelated object - * (specifically, not in an inner object for obj), skip it. This step - * means that all OBJ_LOOKUP_PROPERTY implementations must return an - * object further along on the prototype chain, or else possibly an - * object returned by the JSExtendedClass.outerObject optional hook. - */ - if (obj != obj2) { - cond = JS_FALSE; - clasp = OBJ_GET_CLASS(cx, obj2); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *) clasp; - cond = xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj; - } - if (!cond) - goto restart; - } - - if (foreach) { - /* Get property querying the original object. */ - if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) - return JS_FALSE; - } - } - - if (foreach) { - if (flags & JSITER_KEYVALUE) { - if (!NewKeyValuePair(cx, id, *rval, rval)) - return JS_FALSE; - } - } else { - /* Make rval a string for uniformity and compatibility. */ - if (JSID_IS_ATOM(id)) { - *rval = ATOM_KEY(JSID_TO_ATOM(id)); - } -#if JS_HAS_XML_SUPPORT - else if (JSID_IS_OBJECT(id)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } -#endif - else { - str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } - } - return JS_TRUE; - - stop: - JS_ASSERT(iterobj->slots[JSSLOT_ITER_STATE] == JSVAL_NULL); - *rval = JSVAL_HOLE; - return JS_TRUE; -} - -JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) -{ - uintN flags; - - /* Fast path for native iterators */ - if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { - flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); - if (flags & JSITER_ENUMERATE) - return CallEnumeratorNext(cx, iterobj, flags, rval); - - /* - * Call next directly as all the methods of the native iterator are - * read-only and permanent. - */ - if (!IteratorNextImpl(cx, iterobj, rval)) - return JS_FALSE; - } else { - jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); - - if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) - return JS_FALSE; - if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { - /* Check for StopIteration. */ - if (!cx->throwing || - JSVAL_IS_PRIMITIVE(cx->exception) || - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) - != &js_StopIterationClass) { - return JS_FALSE; - } - - /* Inline JS_ClearPendingException(cx). */ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; - *rval = JSVAL_HOLE; - return JS_TRUE; - } - } - - return JS_TRUE; -} - -static JSBool -stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - *bp = !JSVAL_IS_PRIMITIVE(v) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass; - return JS_TRUE; -} - -JSClass js_StopIterationClass = { - js_StopIteration_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration), - JS_PropertyStub, JS_PropertyStub, - JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, - NULL, NULL, - NULL, stopiter_hasInstance, - NULL, NULL -}; - -#if JS_HAS_GENERATORS - -static void -generator_finalize(JSContext *cx, JSObject *obj) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * gen can be open on shutdown when close hooks are ignored or when - * the embedding cancels scheduled close hooks. - */ - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED || - gen->state == JSGEN_OPEN); - JS_free(cx, gen); - } -} - -static uint32 -generator_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSGenerator *gen; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen) { - /* - * We must mark argv[-2], as js_MarkStackFrame will not. Note that - * js_MarkStackFrame will mark thisp (argv[-1]) and actual arguments, - * plus any missing formals and local GC roots. - */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(gen->frame.argv[-2])); - GC_MARK(cx, JSVAL_TO_GCTHING(gen->frame.argv[-2]), "generator"); - js_MarkStackFrame(cx, &gen->frame); - } - return 0; -} - -JSClass js_GeneratorClass = { - js_Generator_str, - JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | - JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, generator_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, generator_mark, NULL -}; - -/* - * Called from the JSOP_GENERATOR case in the interpreter, with fp referring - * to the frame by which the generator function was activated. Create a new - * JSGenerator object, which contains its own JSStackFrame that we populate - * from *fp. We know that upon return, the JSOP_GENERATOR opcode will return - * from the activation in fp, so we can steal away fp->callobj and fp->argsobj - * if they are non-null. - */ -JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp) -{ - JSObject *obj; - uintN argc, nargs, nvars, depth, nslots; - JSGenerator *gen; - jsval *newsp; - - /* After the following return, failing control flow must goto bad. */ - obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL); - if (!obj) - return NULL; - - /* Load and compute stack slot counts. */ - argc = fp->argc; - nargs = JS_MAX(argc, fp->fun->nargs); - nvars = fp->nvars; - depth = fp->script->depth; - nslots = 2 + nargs + nvars + 2 * depth; - - /* Allocate obj's private data struct. */ - gen = (JSGenerator *) - JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); - if (!gen) - goto bad; - - gen->obj = obj; - - /* Steal away objects reflecting fp and point them at gen->frame. */ - gen->frame.callobj = fp->callobj; - if (fp->callobj) { - JS_SetPrivate(cx, fp->callobj, &gen->frame); - fp->callobj = NULL; - } - gen->frame.argsobj = fp->argsobj; - if (fp->argsobj) { - JS_SetPrivate(cx, fp->argsobj, &gen->frame); - fp->argsobj = NULL; - } - - /* These two references can be shared with fp until it goes away. */ - gen->frame.varobj = fp->varobj; - gen->frame.thisp = fp->thisp; - - /* Copy call-invariant script and function references. */ - gen->frame.script = fp->script; - gen->frame.fun = fp->fun; - - /* Use newsp to carve space out of gen->stack. */ - newsp = gen->stack; - gen->arena.next = NULL; - gen->arena.base = (jsuword) newsp; - gen->arena.limit = gen->arena.avail = (jsuword) (newsp + nslots); - -#define COPY_STACK_ARRAY(vec,cnt,num) \ - JS_BEGIN_MACRO \ - gen->frame.cnt = cnt; \ - gen->frame.vec = newsp; \ - newsp += (num); \ - memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \ - JS_END_MACRO - - /* Copy argv, rval, and vars. */ - *newsp++ = fp->argv[-2]; - *newsp++ = fp->argv[-1]; - COPY_STACK_ARRAY(argv, argc, nargs); - gen->frame.rval = fp->rval; - COPY_STACK_ARRAY(vars, nvars, nvars); - -#undef COPY_STACK_ARRAY - - /* Initialize or copy virtual machine state. */ - gen->frame.down = NULL; - gen->frame.annotation = NULL; - gen->frame.scopeChain = fp->scopeChain; - gen->frame.pc = fp->pc; - - /* Allocate generating pc and operand stack space. */ - gen->frame.spbase = gen->frame.sp = newsp + depth; - - /* Copy remaining state (XXX sharp* and xml* should be local vars). */ - gen->frame.sharpDepth = 0; - gen->frame.sharpArray = NULL; - gen->frame.flags = fp->flags | JSFRAME_GENERATOR; - gen->frame.dormantNext = NULL; - gen->frame.xmlNamespace = NULL; - gen->frame.blockChain = NULL; - - /* Note that gen is newborn. */ - gen->state = JSGEN_NEWBORN; - - if (!JS_SetPrivate(cx, obj, gen)) { - JS_free(cx, gen); - goto bad; - } - - /* - * Register with GC to ensure that suspended finally blocks will be - * executed. - */ - js_RegisterGenerator(cx, gen); - return obj; - - bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; -} - -typedef enum JSGeneratorOp { - JSGENOP_NEXT, - JSGENOP_SEND, - JSGENOP_THROW, - JSGENOP_CLOSE -} JSGeneratorOp; - -/* - * Start newborn or restart yielding generator and perform the requested - * operation inside its frame. - */ -static JSBool -SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, - JSGenerator *gen, jsval arg, jsval *rval) -{ - JSStackFrame *fp; - jsval junk; - JSArena *arena; - JSBool ok; - - JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - if (gen->state == JSGEN_OPEN) { - /* - * Store the argument to send as the result of the yield - * expression. - */ - gen->frame.sp[-1] = arg; - } - gen->state = JSGEN_RUNNING; - break; - - case JSGENOP_THROW: - JS_SetPendingException(cx, arg); - gen->state = JSGEN_RUNNING; - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - JS_SetPendingException(cx, JSVAL_ARETURN); - gen->state = JSGEN_CLOSING; - break; - } - - /* Extend the current stack pool with gen->arena. */ - arena = cx->stackPool.current; - JS_ASSERT(!arena->next); - JS_ASSERT(!gen->arena.next); - JS_ASSERT(cx->stackPool.current != &gen->arena); - cx->stackPool.current = arena->next = &gen->arena; - - /* Push gen->frame around the interpreter activation. */ - fp = cx->fp; - cx->fp = &gen->frame; - gen->frame.down = fp; - ok = js_Interpret(cx, gen->frame.pc, &junk); - cx->fp = fp; - gen->frame.down = NULL; - - /* Retract the stack pool and sanitize gen->arena. */ - JS_ASSERT(!gen->arena.next); - JS_ASSERT(arena->next == &gen->arena); - JS_ASSERT(cx->stackPool.current == &gen->arena); - cx->stackPool.current = arena; - arena->next = NULL; - - if (gen->frame.flags & JSFRAME_YIELDING) { - /* Yield cannot fail, throw or be called on closing. */ - JS_ASSERT(ok); - JS_ASSERT(!cx->throwing); - JS_ASSERT(gen->state == JSGEN_RUNNING); - JS_ASSERT(op != JSGENOP_CLOSE); - gen->frame.flags &= ~JSFRAME_YIELDING; - gen->state = JSGEN_OPEN; - *rval = gen->frame.rval; - return JS_TRUE; - } - - gen->state = JSGEN_CLOSED; - - if (ok) { - /* Returned, explicitly or by falling off the end. */ - if (op == JSGENOP_CLOSE) - return JS_TRUE; - return js_ThrowStopIteration(cx, obj); - } - - /* - * An error, silent termination by branch callback or an exception. - * Propagate the condition to the caller. - */ - return JS_FALSE; -} - -/* - * Execute gen's close hook after the GC detects that the object has become - * unreachable. - */ -JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen) -{ - /* We pass null as rval since SendToGenerator never uses it with CLOSE. */ - return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL); -} - -/* - * Common subroutine of generator_(next|send|throw|close) methods. - */ -static JSBool -generator_op(JSContext *cx, JSGeneratorOp op, - JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSGenerator *gen; - JSString *str; - jsval arg; - - if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv)) - return JS_FALSE; - - gen = (JSGenerator *) JS_GetPrivate(cx, obj); - if (gen == NULL) { - /* This happens when obj is the generator prototype. See bug 352885. */ - goto closed_generator; - } - - switch (gen->state) { - case JSGEN_NEWBORN: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_THROW: - break; - - case JSGENOP_SEND: - if (!JSVAL_IS_VOID(argv[0])) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - argv[0], NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GENERATOR_SEND, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - } - break; - - default: - JS_ASSERT(op == JSGENOP_CLOSE); - gen->state = JSGEN_CLOSED; - return JS_TRUE; - } - break; - - case JSGEN_OPEN: - break; - - case JSGEN_RUNNING: - case JSGEN_CLOSING: - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], - JS_GetFunctionId(gen->frame.fun)); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_NESTING_GENERATOR, - JSSTRING_CHARS(str)); - } - return JS_FALSE; - - default: - JS_ASSERT(gen->state == JSGEN_CLOSED); - - closed_generator: - switch (op) { - case JSGENOP_NEXT: - case JSGENOP_SEND: - return js_ThrowStopIteration(cx, obj); - case JSGENOP_THROW: - JS_SetPendingException(cx, argv[0]); - return JS_FALSE; - default: - JS_ASSERT(op == JSGENOP_CLOSE); - return JS_TRUE; - } - } - - arg = (op == JSGENOP_SEND || op == JSGENOP_THROW) - ? argv[0] - : JSVAL_VOID; - if (!SendToGenerator(cx, op, obj, gen, arg, rval)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -generator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval); -} - -static JSBool -generator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval); -} - -static JSBool -generator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval); -} - -static JSBool -generator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval); -} - -static JSFunctionSpec generator_methods[] = { - {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, - {0,0,0,0,0} -}; - -#endif /* JS_HAS_GENERATORS */ - -JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *stop; - - /* Idempotency required: we initialize several things, possibly lazily. */ - if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) - return NULL; - if (stop) - return stop; - - proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, - NULL, iterator_methods, NULL, NULL); - if (!proto) - return NULL; - proto->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; - -#if JS_HAS_GENERATORS - /* Initialize the generator internals if configured. */ - if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, - NULL, generator_methods, NULL, NULL)) { - return NULL; - } -#endif - - return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, - NULL, NULL, NULL, NULL); -} diff --git a/spidermonkey/libjs/jsiter.h b/spidermonkey/libjs/jsiter.h deleted file mode 100644 index 1a99b6b..0000000 --- a/spidermonkey/libjs/jsiter.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsiter_h___ -#define jsiter_h___ -/* - * JavaScript iterators. - */ -#include "jsprvtd.h" -#include "jspubtd.h" - -#define JSITER_ENUMERATE 0x1 /* for-in compatible hidden default iterator */ -#define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ -#define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ - -extern void -js_CloseNativeIterator(JSContext *cx, JSObject *iterobj); - -extern void -js_CloseIteratorState(JSContext *cx, JSObject *iterobj); - -/* - * Convert the value stored in *vp to its iteration object. The flags should - * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating - * for-in semantics are required, and when the caller can guarantee that the - * iterator will never be exposed to scripts. - */ -extern JSBool -js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp); - -/* - * Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to - * JSVAL_HOLE. Otherwise set it to the result of the next call. - */ -extern JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval); - -#if JS_HAS_GENERATORS - -/* - * Generator state codes. - */ -typedef enum JSGeneratorState { - JSGEN_NEWBORN, /* not yet started */ - JSGEN_OPEN, /* started by a .next() or .send(undefined) call */ - JSGEN_RUNNING, /* currently executing via .next(), etc., call */ - JSGEN_CLOSING, /* close method is doing asynchronous return */ - JSGEN_CLOSED /* closed, cannot be started or closed again */ -} JSGeneratorState; - -struct JSGenerator { - JSGenerator *next; - JSObject *obj; - JSGeneratorState state; - JSStackFrame frame; - JSArena arena; - jsval stack[1]; -}; - -#define FRAME_TO_GENERATOR(fp) \ - ((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame))) - -extern JSObject * -js_NewGenerator(JSContext *cx, JSStackFrame *fp); - -extern JSBool -js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen); - -#endif - -extern JSClass js_GeneratorClass; -extern JSClass js_IteratorClass; -extern JSClass js_StopIterationClass; - -extern JSObject * -js_InitIteratorClasses(JSContext *cx, JSObject *obj); - -#endif /* jsiter_h___ */ diff --git a/spidermonkey/libjs/jskeyword.tbl b/spidermonkey/libjs/jskeyword.tbl deleted file mode 100644 index 49b9c6c..0000000 --- a/spidermonkey/libjs/jskeyword.tbl +++ /dev/null @@ -1,124 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -JS_KEYWORD(break, TOK_BREAK, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(case, TOK_CASE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(continue, TOK_CONTINUE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(default, TOK_DEFAULT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(delete, TOK_DELETE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(do, TOK_DO, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(else, TOK_ELSE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(export, TOK_EXPORT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(false, TOK_PRIMARY, JSOP_FALSE, JSVERSION_DEFAULT) -JS_KEYWORD(for, TOK_FOR, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(function, TOK_FUNCTION, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(if, TOK_IF, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(in, TOK_IN, JSOP_IN, JSVERSION_DEFAULT) -JS_KEYWORD(new, TOK_NEW, JSOP_NEW, JSVERSION_DEFAULT) -JS_KEYWORD(null, TOK_PRIMARY, JSOP_NULL, JSVERSION_DEFAULT) -JS_KEYWORD(return, TOK_RETURN, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(switch, TOK_SWITCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(this, TOK_PRIMARY, JSOP_THIS, JSVERSION_DEFAULT) -JS_KEYWORD(true, TOK_PRIMARY, JSOP_TRUE, JSVERSION_DEFAULT) -JS_KEYWORD(typeof, TOK_UNARYOP, JSOP_TYPEOF, JSVERSION_DEFAULT) -JS_KEYWORD(var, TOK_VAR, JSOP_DEFVAR, JSVERSION_DEFAULT) -JS_KEYWORD(void, TOK_UNARYOP, JSOP_VOID, JSVERSION_DEFAULT) -JS_KEYWORD(while, TOK_WHILE, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(with, TOK_WITH, JSOP_NOP, JSVERSION_DEFAULT) -#if JS_HAS_CONST -JS_KEYWORD(const, TOK_VAR, JSOP_DEFCONST, JSVERSION_DEFAULT) -#else -JS_KEYWORD(const, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -JS_KEYWORD(try, TOK_TRY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(catch, TOK_CATCH, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(finally, TOK_FINALLY, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(throw, TOK_THROW, JSOP_NOP, JSVERSION_DEFAULT) - -JS_KEYWORD(instanceof, TOK_INSTANCEOF, JSOP_INSTANCEOF,JSVERSION_DEFAULT) - -#if JS_HAS_RESERVED_JAVA_KEYWORDS -JS_KEYWORD(abstract, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(boolean, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(byte, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(char, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(class, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(double, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(extends, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(final, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(float, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(goto, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(implements, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(import, TOK_IMPORT, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(int, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(interface, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(long, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(native, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(package, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(private, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(protected, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(public, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(short, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(static, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(super, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(synchronized,TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(throws, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(transient, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -JS_KEYWORD(volatile, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_RESERVED_ECMA_KEYWORDS -JS_KEYWORD(enum, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_DEBUGGER_KEYWORD -JS_KEYWORD(debugger, TOK_DEBUGGER, JSOP_NOP, JSVERSION_DEFAULT) -#elif JS_HAS_RESERVED_ECMA_KEYWORDS -JS_KEYWORD(debugger, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT) -#endif - -#if JS_HAS_GENERATORS -JS_KEYWORD(yield, TOK_YIELD, JSOP_NOP, JSVERSION_1_7) -#endif - -#if JS_HAS_BLOCK_SCOPE -JS_KEYWORD(let, TOK_LET, JSOP_NOP, JSVERSION_1_7) -#endif diff --git a/spidermonkey/libjs/jslibmath.h b/spidermonkey/libjs/jslibmath.h deleted file mode 100644 index 3f75f30..0000000 --- a/spidermonkey/libjs/jslibmath.h +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * By default all math calls go to fdlibm. The defines for each platform - * remap the math calls to native routines. - */ - -#ifndef _LIBMATH_H -#define _LIBMATH_H - -#include -#include "jsconfig.h" - -/* - * Define on which platforms to use fdlibm. Not used by default under - * assumption that native math library works unless proved guilty. - * Plus there can be problems with endian-ness and such in fdlibm itself. - * - * fdlibm compatibility notes: - * - fdlibm broken on OSF1/alpha - */ - -#ifndef JS_USE_FDLIBM_MATH -#define JS_USE_FDLIBM_MATH 0 -#endif - -#if !JS_USE_FDLIBM_MATH - -/* - * Use system provided math routines. - */ - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_atan2 atan2 -#define fd_ceil ceil - -/* The right copysign function is not always named the same thing. */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define fd_copysign __builtin_copysign -#elif defined WINCE -#define fd_copysign _copysign -#elif defined _WIN32 -#if _MSC_VER < 1400 -/* Try to work around apparent _copysign bustage in VC6 and VC7. */ -#define fd_copysign js_copysign -extern double js_copysign(double, double); -#else -#define fd_copysign _copysign -#endif -#else -#define fd_copysign copysign -#endif - -#define fd_cos cos -#define fd_exp exp -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod -#define fd_log log -#define fd_pow pow -#define fd_sin sin -#define fd_sqrt sqrt -#define fd_tan tan - -#else - -/* - * Use math routines in fdlibm. - */ - -#undef __P -#ifdef __STDC__ -#define __P(p) p -#else -#define __P(p) () -#endif - -#if (defined _WIN32 && !defined WINCE) || defined SUNOS4 - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_cos cos -#define fd_sin sin -#define fd_tan tan -#define fd_exp exp -#define fd_log log -#define fd_sqrt sqrt -#define fd_ceil ceil -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_atan2 __P((double, double)); -extern double fd_copysign __P((double, double)); -extern double fd_pow __P((double, double)); - -#elif defined IRIX - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_exp exp -#define fd_log log -#define fd_log10 log10 -#define fd_sqrt sqrt -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_cos __P((double)); -extern double fd_sin __P((double)); -extern double fd_tan __P((double)); -extern double fd_atan2 __P((double, double)); -extern double fd_pow __P((double, double)); -extern double fd_ceil __P((double)); -extern double fd_copysign __P((double, double)); - -#elif defined SOLARIS - -#define fd_atan atan -#define fd_cos cos -#define fd_sin sin -#define fd_tan tan -#define fd_exp exp -#define fd_sqrt sqrt -#define fd_ceil ceil -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_acos __P((double)); -extern double fd_asin __P((double)); -extern double fd_log __P((double)); -extern double fd_atan2 __P((double, double)); -extern double fd_pow __P((double, double)); -extern double fd_copysign __P((double, double)); - -#elif defined HPUX - -#define fd_cos cos -#define fd_sin sin -#define fd_exp exp -#define fd_sqrt sqrt -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod - -extern double fd_ceil __P((double)); -extern double fd_acos __P((double)); -extern double fd_log __P((double)); -extern double fd_atan2 __P((double, double)); -extern double fd_tan __P((double)); -extern double fd_pow __P((double, double)); -extern double fd_asin __P((double)); -extern double fd_atan __P((double)); -extern double fd_copysign __P((double, double)); - -#elif defined(OSF1) - -#define fd_acos acos -#define fd_asin asin -#define fd_atan atan -#define fd_copysign copysign -#define fd_cos cos -#define fd_exp exp -#define fd_fabs fabs -#define fd_fmod fmod -#define fd_sin sin -#define fd_sqrt sqrt -#define fd_tan tan - -extern double fd_atan2 __P((double, double)); -extern double fd_ceil __P((double)); -extern double fd_floor __P((double)); -extern double fd_log __P((double)); -extern double fd_pow __P((double, double)); - -#elif defined(AIX) - -#define fd_acos acos -#define fd_asin asin -#define fd_atan2 atan2 -#define fd_copysign copysign -#define fd_cos cos -#define fd_exp exp -#define fd_fabs fabs -#define fd_floor floor -#define fd_fmod fmod -#define fd_log log -#define fd_sin sin -#define fd_sqrt sqrt - -extern double fd_atan __P((double)); -extern double fd_ceil __P((double)); -extern double fd_pow __P((double,double)); -extern double fd_tan __P((double)); - -#else /* other platform.. generic paranoid slow fdlibm */ - -extern double fd_acos __P((double)); -extern double fd_asin __P((double)); -extern double fd_atan __P((double)); -extern double fd_cos __P((double)); -extern double fd_sin __P((double)); -extern double fd_tan __P((double)); - -extern double fd_exp __P((double)); -extern double fd_log __P((double)); -extern double fd_sqrt __P((double)); - -extern double fd_ceil __P((double)); -extern double fd_fabs __P((double)); -extern double fd_floor __P((double)); -extern double fd_fmod __P((double, double)); - -extern double fd_atan2 __P((double, double)); -extern double fd_pow __P((double, double)); -extern double fd_copysign __P((double, double)); - -#endif - -#endif /* JS_USE_FDLIBM_MATH */ - -#endif /* _LIBMATH_H */ - diff --git a/spidermonkey/libjs/jslock.c b/spidermonkey/libjs/jslock.c deleted file mode 100644 index 4855009..0000000 --- a/spidermonkey/libjs/jslock.c +++ /dev/null @@ -1,1303 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifdef JS_THREADSAFE - -/* - * JS locking stubs. - */ -#include "jsstddef.h" -#include -#include "jspubtd.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jstypes.h" -#include "jsbit.h" -#include "jscntxt.h" -#include "jsdtoa.h" -#include "jsgc.h" -#include "jslock.h" -#include "jsscope.h" -#include "jsstr.h" - -#define ReadWord(W) (W) - -#ifndef NSPR_LOCK - -#include - -static PRLock **global_locks; -static uint32 global_lock_count = 1; -static uint32 global_locks_log2 = 0; -static uint32 global_locks_mask = 0; - -#define GLOBAL_LOCK_INDEX(id) (((uint32)(id) >> 2) & global_locks_mask) - -static void -js_LockGlobal(void *id) -{ - uint32 i = GLOBAL_LOCK_INDEX(id); - PR_Lock(global_locks[i]); -} - -static void -js_UnlockGlobal(void *id) -{ - uint32 i = GLOBAL_LOCK_INDEX(id); - PR_Unlock(global_locks[i]); -} - -/* Exclude Alpha NT. */ -#if defined(_WIN32) && defined(_M_IX86) -#pragma warning( disable : 4035 ) - -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ - __asm { - mov eax, ov - mov ecx, nv - mov ebx, w - lock cmpxchg [ebx], ecx - sete al - and eax, 1h - } -} - -#elif defined(__GNUC__) && defined(__i386__) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ - unsigned int res; - - __asm__ __volatile__ ( - "lock\n" - "cmpxchgl %2, (%1)\n" - "sete %%al\n" - "andl $1, %%eax\n" - : "=a" (res) - : "r" (w), "r" (nv), "a" (ov) - : "cc", "memory"); - return (int)res; -} - -#elif (defined(__USLC__) || defined(_SCO_DS)) && defined(i386) - -/* Note: This fails on 386 cpus, cmpxchgl is a >= 486 instruction */ - -asm int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ -%ureg w, nv; - movl ov,%eax - lock - cmpxchgl nv,(w) - sete %al - andl $1,%eax -%ureg w; mem ov, nv; - movl ov,%eax - movl nv,%ecx - lock - cmpxchgl %ecx,(w) - sete %al - andl $1,%eax -%ureg nv; - movl ov,%eax - movl w,%edx - lock - cmpxchgl nv,(%edx) - sete %al - andl $1,%eax -%mem w, ov, nv; - movl ov,%eax - movl nv,%ecx - movl w,%edx - lock - cmpxchgl %ecx,(%edx) - sete %al - andl $1,%eax -} -#pragma asm full_optimization js_CompareAndSwap - -#elif defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC) - -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ -#if defined(__GNUC__) - unsigned int res; - JS_ASSERT(ov != nv); - asm volatile ("\ -stbar\n\ -cas [%1],%2,%3\n\ -cmp %2,%3\n\ -be,a 1f\n\ -mov 1,%0\n\ -mov 0,%0\n\ -1:" - : "=r" (res) - : "r" (w), "r" (ov), "r" (nv)); - return (int)res; -#else /* !__GNUC__ */ - extern int compare_and_swap(jsword*, jsword, jsword); - JS_ASSERT(ov != nv); - return compare_and_swap(w, ov, nv); -#endif -} - -#elif defined(AIX) - -#include - -static JS_INLINE int -js_CompareAndSwap(jsword *w, jsword ov, jsword nv) -{ - return !_check_lock((atomic_p)w, ov, nv); -} - -#else - -#error "Define NSPR_LOCK if your platform lacks a compare-and-swap instruction." - -#endif /* arch-tests */ - -#endif /* !NSPR_LOCK */ - -void -js_InitLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0; - tl->fat = (JSFatLock*)JS_NEW_LOCK(); -#else - memset(tl, 0, sizeof(JSThinLock)); -#endif -} - -void -js_FinishLock(JSThinLock *tl) -{ -#ifdef NSPR_LOCK - tl->owner = 0xdeadbeef; - if (tl->fat) - JS_DESTROY_LOCK(((JSLock*)tl->fat)); -#else - JS_ASSERT(tl->owner == 0); - JS_ASSERT(tl->fat == NULL); -#endif -} - -static void js_Dequeue(JSThinLock *); - -#ifdef DEBUG_SCOPE_COUNT - -#include -#include "jsdhash.h" - -static FILE *logfp; -static JSDHashTable logtbl; - -typedef struct logentry { - JSDHashEntryStub stub; - char op; - const char *file; - int line; -} logentry; - -static void -logit(JSScope *scope, char op, const char *file, int line) -{ - logentry *entry; - - if (!logfp) { - logfp = fopen("/tmp/scope.log", "w"); - if (!logfp) - return; - setvbuf(logfp, NULL, _IONBF, 0); - } - fprintf(logfp, "%p %c %s %d\n", scope, op, file, line); - - if (!logtbl.entryStore && - !JS_DHashTableInit(&logtbl, JS_DHashGetStubOps(), NULL, - sizeof(logentry), 100)) { - return; - } - entry = (logentry *) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_ADD); - if (!entry) - return; - entry->stub.key = scope; - entry->op = op; - entry->file = file; - entry->line = line; -} - -void -js_unlog_scope(JSScope *scope) -{ - if (!logtbl.entryStore) - return; - (void) JS_DHashTableOperate(&logtbl, scope, JS_DHASH_REMOVE); -} - -# define LOGIT(scope,op) logit(scope, op, __FILE__, __LINE__) - -#else - -# define LOGIT(scope,op) /* nothing */ - -#endif /* DEBUG_SCOPE_COUNT */ - -/* - * Return true if scope's ownercx, or the ownercx of a single-threaded scope - * for which ownercx is waiting to become multi-threaded and shared, is cx. - * That condition implies deadlock in ClaimScope if cx's thread were to wait - * to share scope. - * - * (i) rt->gcLock held - */ -static JSBool -WillDeadlock(JSScope *scope, JSContext *cx) -{ - JSContext *ownercx; - - do { - ownercx = scope->ownercx; - if (ownercx == cx) { - JS_RUNTIME_METER(cx->runtime, deadlocksAvoided); - return JS_TRUE; - } - } while (ownercx && (scope = ownercx->scopeToShare) != NULL); - return JS_FALSE; -} - -/* - * Make scope multi-threaded, i.e. share its ownership among contexts in rt - * using a "thin" or (if necessary due to contention) "fat" lock. Called only - * from ClaimScope, immediately below, when we detect deadlock were we to wait - * for scope's lock, because its ownercx is waiting on a scope owned by the - * calling cx. - * - * (i) rt->gcLock held - */ -static void -ShareScope(JSRuntime *rt, JSScope *scope) -{ - JSScope **todop; - - if (scope->u.link) { - for (todop = &rt->scopeSharingTodo; *todop != scope; - todop = &(*todop)->u.link) { - JS_ASSERT(*todop != NO_SCOPE_SHARING_TODO); - } - *todop = scope->u.link; - scope->u.link = NULL; /* null u.link for sanity ASAP */ - JS_NOTIFY_ALL_CONDVAR(rt->scopeSharingDone); - } - js_InitLock(&scope->lock); - if (scope == rt->setSlotScope) { - /* - * Nesting locks on another thread that's using scope->ownercx: give - * the held lock a reentrancy count of 1 and set its lock.owner field - * directly (no compare-and-swap needed while scope->ownercx is still - * non-null). See below in ClaimScope, before the ShareScope call, - * for more on why this is necessary. - * - * If NSPR_LOCK is defined, we cannot deadlock holding rt->gcLock and - * acquiring scope->lock.fat here, against another thread holding that - * fat lock and trying to grab rt->gcLock. This is because no other - * thread can attempt to acquire scope->lock.fat until scope->ownercx - * is null *and* our thread has released rt->gcLock, which interlocks - * scope->ownercx's transition to null against tests of that member - * in ClaimScope. - */ - scope->lock.owner = CX_THINLOCK_ID(scope->ownercx); -#ifdef NSPR_LOCK - JS_ACQUIRE_LOCK((JSLock*)scope->lock.fat); -#endif - scope->u.count = 1; - } else { - scope->u.count = 0; - } - js_FinishSharingScope(rt, scope); -} - -/* - * js_FinishSharingScope is the tail part of ShareScope, split out to become a - * subroutine of JS_EndRequest too. The bulk of the work here involves making - * mutable strings in the scope's object's slots be immutable. We have to do - * this because such strings will soon be available to multiple threads, so - * their buffers can't be realloc'd any longer in js_ConcatStrings, and their - * members can't be modified by js_ConcatStrings, js_MinimizeDependentStrings, - * or js_UndependString. - * - * The last bit of work done by js_FinishSharingScope nulls scope->ownercx and - * updates rt->sharedScopes. - */ -#define MAKE_STRING_IMMUTABLE(rt, v, vp) \ - JS_BEGIN_MACRO \ - JSString *str_ = JSVAL_TO_STRING(v); \ - uint8 *flagp_ = js_GetGCThingFlags(str_); \ - if (*flagp_ & GCF_MUTABLE) { \ - if (JSSTRING_IS_DEPENDENT(str_) && \ - !js_UndependString(NULL, str_)) { \ - JS_RUNTIME_METER(rt, badUndependStrings); \ - *vp = JSVAL_VOID; \ - } else { \ - *flagp_ &= ~GCF_MUTABLE; \ - } \ - } \ - JS_END_MACRO - -void -js_FinishSharingScope(JSRuntime *rt, JSScope *scope) -{ - JSObject *obj; - uint32 nslots; - jsval v, *vp, *end; - - obj = scope->object; - nslots = JS_MIN(obj->map->freeslot, obj->map->nslots); - for (vp = obj->slots, end = vp + nslots; vp < end; vp++) { - v = *vp; - if (JSVAL_IS_STRING(v)) - MAKE_STRING_IMMUTABLE(rt, v, vp); - } - - scope->ownercx = NULL; /* NB: set last, after lock init */ - JS_RUNTIME_METER(rt, sharedScopes); -} - -/* - * Given a scope with apparently non-null ownercx different from cx, try to - * set ownercx to cx, claiming exclusive (single-threaded) ownership of scope. - * If we claim ownership, return true. Otherwise, we wait for ownercx to be - * set to null (indicating that scope is multi-threaded); or if waiting would - * deadlock, we set ownercx to null ourselves via ShareScope. In any case, - * once ownercx is null we return false. - */ -static JSBool -ClaimScope(JSScope *scope, JSContext *cx) -{ - JSRuntime *rt; - JSContext *ownercx; - jsrefcount saveDepth; - PRStatus stat; - - rt = cx->runtime; - JS_RUNTIME_METER(rt, claimAttempts); - JS_LOCK_GC(rt); - - /* Reload in case ownercx went away while we blocked on the lock. */ - while ((ownercx = scope->ownercx) != NULL) { - /* - * Avoid selflock if ownercx is dead, or is not running a request, or - * has the same thread as cx. Set scope->ownercx to cx so that the - * matching JS_UNLOCK_SCOPE or JS_UNLOCK_OBJ macro call will take the - * fast path around the corresponding js_UnlockScope or js_UnlockObj - * function call. - * - * If scope->u.link is non-null, scope has already been inserted on - * the rt->scopeSharingTodo list, because another thread's context - * already wanted to lock scope while ownercx was running a request. - * We can't claim any scope whose u.link is non-null at this point, - * even if ownercx->requestDepth is 0 (see below where we suspend our - * request before waiting on rt->scopeSharingDone). - */ - if (!scope->u.link && - (!js_ValidContextPointer(rt, ownercx) || - !ownercx->requestDepth || - ownercx->thread == cx->thread)) { - JS_ASSERT(scope->u.count == 0); - scope->ownercx = cx; - JS_UNLOCK_GC(rt); - JS_RUNTIME_METER(rt, claimedScopes); - return JS_TRUE; - } - - /* - * Avoid deadlock if scope's owner context is waiting on a scope that - * we own, by revoking scope's ownership. This approach to deadlock - * avoidance works because the engine never nests scope locks, except - * for the notable case of js_SetProtoOrParent (see jsobj.c). - * - * If cx could hold locks on ownercx->scopeToShare, or if ownercx - * could hold locks on scope, we would need to keep reentrancy counts - * for all such "flyweight" (ownercx != NULL) locks, so that control - * would unwind properly once these locks became "thin" or "fat". - * Apart from the js_SetProtoOrParent exception, the engine promotes - * a scope from exclusive to shared access only when locking, never - * when holding or unlocking. - * - * If ownercx's thread is calling js_SetProtoOrParent, trying to lock - * the inner scope (the scope of the object being set as the prototype - * of the outer object), ShareScope will find the outer object's scope - * at rt->setSlotScope. If it's the same as scope, we give it a lock - * held by ownercx's thread with reentrancy count of 1, then we return - * here and break. After that we unwind to js_[GS]etSlotThreadSafe or - * js_LockScope (our caller), where we wait on the newly-fattened lock - * until ownercx's thread unwinds from js_SetProtoOrParent. - * - * Avoid deadlock before any of this scope/context cycle detection if - * cx is on the active GC's thread, because in that case, no requests - * will run until the GC completes. Any scope wanted by the GC (from - * a finalizer) that can't be claimed must be slated for sharing. - */ - if (rt->gcThread == cx->thread || - (ownercx->scopeToShare && - WillDeadlock(ownercx->scopeToShare, cx))) { - ShareScope(rt, scope); - break; - } - - /* - * Thanks to the non-zero NO_SCOPE_SHARING_TODO link terminator, we - * can decide whether scope is on rt->scopeSharingTodo with a single - * non-null test, and avoid double-insertion bugs. - */ - if (!scope->u.link) { - scope->u.link = rt->scopeSharingTodo; - rt->scopeSharingTodo = scope; - js_HoldObjectMap(cx, &scope->map); - } - - /* - * Inline JS_SuspendRequest before we wait on rt->scopeSharingDone, - * saving and clearing cx->requestDepth so we don't deadlock if the - * GC needs to run on ownercx. - * - * Unlike JS_SuspendRequest and JS_EndRequest, we must take care not - * to decrement rt->requestCount if cx is active on the GC's thread, - * because the GC has already reduced rt->requestCount to exclude all - * such such contexts. - */ - saveDepth = cx->requestDepth; - if (saveDepth) { - cx->requestDepth = 0; - if (rt->gcThread != cx->thread) { - JS_ASSERT(rt->requestCount > 0); - rt->requestCount--; - if (rt->requestCount == 0) - JS_NOTIFY_REQUEST_DONE(rt); - } - } - - /* - * We know that some other thread's context owns scope, which is now - * linked onto rt->scopeSharingTodo, awaiting the end of that other - * thread's request. So it is safe to wait on rt->scopeSharingDone. - */ - cx->scopeToShare = scope; - stat = PR_WaitCondVar(rt->scopeSharingDone, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(stat != PR_FAILURE); - - /* - * Inline JS_ResumeRequest after waiting on rt->scopeSharingDone, - * restoring cx->requestDepth. Same note as above for the inlined, - * specialized JS_SuspendRequest code: beware rt->gcThread. - */ - if (saveDepth) { - if (rt->gcThread != cx->thread) { - while (rt->gcLevel > 0) - JS_AWAIT_GC_DONE(rt); - rt->requestCount++; - } - cx->requestDepth = saveDepth; - } - - /* - * Don't clear cx->scopeToShare until after we're through waiting on - * all condition variables protected by rt->gcLock -- that includes - * rt->scopeSharingDone *and* rt->gcDone (hidden in JS_AWAIT_GC_DONE, - * in the inlined JS_ResumeRequest code immediately above). - * - * Otherwise, the GC could easily deadlock with another thread that - * owns a scope wanted by a finalizer. By keeping cx->scopeToShare - * set till here, we ensure that such deadlocks are detected, which - * results in the finalized object's scope being shared (it must, of - * course, have other, live objects sharing it). - */ - cx->scopeToShare = NULL; - } - - JS_UNLOCK_GC(rt); - return JS_FALSE; -} - -/* Exported to js.c, which calls it via OBJ_GET_* and JSVAL_IS_* macros. */ -JS_FRIEND_API(jsval) -js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) -{ - jsval v; - JSScope *scope; -#ifndef NSPR_LOCK - JSThinLock *tl; - jsword me; -#endif - - /* - * We handle non-native objects via JSObjectOps.getRequiredSlot, treating - * all slots starting from 0 as required slots. A property definition or - * some prior arrangement must have allocated slot. - * - * Note once again (see jspubtd.h, before JSGetRequiredSlotOp's typedef) - * the crucial distinction between a |required slot number| that's passed - * to the get/setRequiredSlot JSObjectOps, and a |reserved slot index| - * passed to the JS_Get/SetReservedSlot APIs. - */ - if (!OBJ_IS_NATIVE(obj)) - return OBJ_GET_REQUIRED_SLOT(cx, obj, slot); - - /* - * Native object locking is inlined here to optimize the single-threaded - * and contention-free multi-threaded cases. - */ - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->ownercx != cx); - JS_ASSERT(obj->slots && slot < obj->map->freeslot); - - /* - * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). - * Also avoid locking an object owning a sealed scope. If neither of those - * special cases applies, try to claim scope's flyweight lock from whatever - * context may have had it in an earlier request. - */ - if (CX_THREAD_IS_RUNNING_GC(cx) || - (SCOPE_IS_SEALED(scope) && scope->object == obj) || - (scope->ownercx && ClaimScope(scope, cx))) { - return obj->slots[slot]; - } - -#ifndef NSPR_LOCK - tl = &scope->lock; - me = CX_THINLOCK_ID(cx); - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (js_CompareAndSwap(&tl->owner, 0, me)) { - /* - * Got the lock with one compare-and-swap. Even so, someone else may - * have mutated obj so it now has its own scope and lock, which would - * require either a restart from the top of this routine, or a thin - * lock release followed by fat lock acquisition. - */ - if (scope == OBJ_SCOPE(obj)) { - v = obj->slots[slot]; - if (!js_CompareAndSwap(&tl->owner, me, 0)) { - /* Assert that scope locks never revert to flyweight. */ - JS_ASSERT(scope->ownercx != cx); - LOGIT(scope, '1'); - scope->u.count = 1; - js_UnlockObj(cx, obj); - } - return v; - } - if (!js_CompareAndSwap(&tl->owner, me, 0)) - js_Dequeue(tl); - } - else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { - return obj->slots[slot]; - } -#endif - - js_LockObj(cx, obj); - v = obj->slots[slot]; - - /* - * Test whether cx took ownership of obj's scope during js_LockObj. - * - * This does not mean that a given scope reverted to flyweight from "thin" - * or "fat" -- it does mean that obj's map pointer changed due to another - * thread setting a property, requiring obj to cease sharing a prototype - * object's scope (whose lock was not flyweight, else we wouldn't be here - * in the first place!). - */ - scope = OBJ_SCOPE(obj); - if (scope->ownercx != cx) - js_UnlockScope(cx, scope); - return v; -} - -void -js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) -{ - JSScope *scope; -#ifndef NSPR_LOCK - JSThinLock *tl; - jsword me; -#endif - - /* Any string stored in a thread-safe object must be immutable. */ - if (JSVAL_IS_STRING(v)) - MAKE_STRING_IMMUTABLE(cx->runtime, v, &v); - - /* - * We handle non-native objects via JSObjectOps.setRequiredSlot, as above - * for the Get case. - */ - if (!OBJ_IS_NATIVE(obj)) { - OBJ_SET_REQUIRED_SLOT(cx, obj, slot, v); - return; - } - - /* - * Native object locking is inlined here to optimize the single-threaded - * and contention-free multi-threaded cases. - */ - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->ownercx != cx); - JS_ASSERT(obj->slots && slot < obj->map->freeslot); - - /* - * Avoid locking if called from the GC (see GC_AWARE_GET_SLOT in jsobj.h). - * Also avoid locking an object owning a sealed scope. If neither of those - * special cases applies, try to claim scope's flyweight lock from whatever - * context may have had it in an earlier request. - */ - if (CX_THREAD_IS_RUNNING_GC(cx) || - (SCOPE_IS_SEALED(scope) && scope->object == obj) || - (scope->ownercx && ClaimScope(scope, cx))) { - obj->slots[slot] = v; - return; - } - -#ifndef NSPR_LOCK - tl = &scope->lock; - me = CX_THINLOCK_ID(cx); - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (js_CompareAndSwap(&tl->owner, 0, me)) { - if (scope == OBJ_SCOPE(obj)) { - obj->slots[slot] = v; - if (!js_CompareAndSwap(&tl->owner, me, 0)) { - /* Assert that scope locks never revert to flyweight. */ - JS_ASSERT(scope->ownercx != cx); - LOGIT(scope, '1'); - scope->u.count = 1; - js_UnlockObj(cx, obj); - } - return; - } - if (!js_CompareAndSwap(&tl->owner, me, 0)) - js_Dequeue(tl); - } - else if (Thin_RemoveWait(ReadWord(tl->owner)) == me) { - obj->slots[slot] = v; - return; - } -#endif - - js_LockObj(cx, obj); - obj->slots[slot] = v; - - /* - * Same drill as above, in js_GetSlotThreadSafe. Note that we cannot - * assume obj has its own mutable scope (where scope->object == obj) yet, - * because OBJ_SET_SLOT is called for the "universal", common slots such - * as JSSLOT_PROTO and JSSLOT_PARENT, without a prior js_GetMutableScope. - * See also the JSPROP_SHARED attribute and its usage. - */ - scope = OBJ_SCOPE(obj); - if (scope->ownercx != cx) - js_UnlockScope(cx, scope); -} - -#ifndef NSPR_LOCK - -static JSFatLock * -NewFatlock() -{ - JSFatLock *fl = (JSFatLock *)malloc(sizeof(JSFatLock)); /* for now */ - if (!fl) return NULL; - fl->susp = 0; - fl->next = NULL; - fl->prevp = NULL; - fl->slock = PR_NewLock(); - fl->svar = PR_NewCondVar(fl->slock); - return fl; -} - -static void -DestroyFatlock(JSFatLock *fl) -{ - PR_DestroyLock(fl->slock); - PR_DestroyCondVar(fl->svar); - free(fl); -} - -static JSFatLock * -ListOfFatlocks(int listc) -{ - JSFatLock *m; - JSFatLock *m0; - int i; - - JS_ASSERT(listc>0); - m0 = m = NewFatlock(); - for (i=1; inext = NewFatlock(); - m = m->next; - } - return m0; -} - -static void -DeleteListOfFatlocks(JSFatLock *m) -{ - JSFatLock *m0; - for (; m; m=m0) { - m0 = m->next; - DestroyFatlock(m); - } -} - -static JSFatLockTable *fl_list_table = NULL; -static uint32 fl_list_table_len = 0; -static uint32 fl_list_chunk_len = 0; - -static JSFatLock * -GetFatlock(void *id) -{ - JSFatLock *m; - - uint32 i = GLOBAL_LOCK_INDEX(id); - if (fl_list_table[i].free == NULL) { -#ifdef DEBUG - if (fl_list_table[i].taken) - printf("Ran out of fat locks!\n"); -#endif - fl_list_table[i].free = ListOfFatlocks(fl_list_chunk_len); - } - m = fl_list_table[i].free; - fl_list_table[i].free = m->next; - m->susp = 0; - m->next = fl_list_table[i].taken; - m->prevp = &fl_list_table[i].taken; - if (fl_list_table[i].taken) - fl_list_table[i].taken->prevp = &m->next; - fl_list_table[i].taken = m; - return m; -} - -static void -PutFatlock(JSFatLock *m, void *id) -{ - uint32 i; - if (m == NULL) - return; - - /* Unlink m from fl_list_table[i].taken. */ - *m->prevp = m->next; - if (m->next) - m->next->prevp = m->prevp; - - /* Insert m in fl_list_table[i].free. */ - i = GLOBAL_LOCK_INDEX(id); - m->next = fl_list_table[i].free; - fl_list_table[i].free = m; -} - -#endif /* !NSPR_LOCK */ - -JSBool -js_SetupLocks(int listc, int globc) -{ -#ifndef NSPR_LOCK - uint32 i; - - if (global_locks) - return JS_TRUE; -#ifdef DEBUG - if (listc > 10000 || listc < 0) /* listc == fat lock list chunk length */ - printf("Bad number %d in js_SetupLocks()!\n", listc); - if (globc > 100 || globc < 0) /* globc == number of global locks */ - printf("Bad number %d in js_SetupLocks()!\n", listc); -#endif - global_locks_log2 = JS_CeilingLog2(globc); - global_locks_mask = JS_BITMASK(global_locks_log2); - global_lock_count = JS_BIT(global_locks_log2); - global_locks = (PRLock **) malloc(global_lock_count * sizeof(PRLock*)); - if (!global_locks) - return JS_FALSE; - for (i = 0; i < global_lock_count; i++) { - global_locks[i] = PR_NewLock(); - if (!global_locks[i]) { - global_lock_count = i; - js_CleanupLocks(); - return JS_FALSE; - } - } - fl_list_table = (JSFatLockTable *) malloc(i * sizeof(JSFatLockTable)); - if (!fl_list_table) { - js_CleanupLocks(); - return JS_FALSE; - } - fl_list_table_len = global_lock_count; - for (i = 0; i < global_lock_count; i++) - fl_list_table[i].free = fl_list_table[i].taken = NULL; - fl_list_chunk_len = listc; -#endif /* !NSPR_LOCK */ - return JS_TRUE; -} - -void -js_CleanupLocks() -{ -#ifndef NSPR_LOCK - uint32 i; - - if (global_locks) { - for (i = 0; i < global_lock_count; i++) - PR_DestroyLock(global_locks[i]); - free(global_locks); - global_locks = NULL; - global_lock_count = 1; - global_locks_log2 = 0; - global_locks_mask = 0; - } - if (fl_list_table) { - for (i = 0; i < fl_list_table_len; i++) { - DeleteListOfFatlocks(fl_list_table[i].free); - fl_list_table[i].free = NULL; - DeleteListOfFatlocks(fl_list_table[i].taken); - fl_list_table[i].taken = NULL; - } - free(fl_list_table); - fl_list_table = NULL; - fl_list_table_len = 0; - } -#endif /* !NSPR_LOCK */ -} - -#ifndef NSPR_LOCK - -/* - * Fast locking and unlocking is implemented by delaying the allocation of a - * system lock (fat lock) until contention. As long as a locking thread A - * runs uncontended, the lock is represented solely by storing A's identity in - * the object being locked. - * - * If another thread B tries to lock the object currently locked by A, B is - * enqueued into a fat lock structure (which might have to be allocated and - * pointed to by the object), and suspended using NSPR conditional variables - * (wait). A wait bit (Bacon bit) is set in the lock word of the object, - * signalling to A that when releasing the lock, B must be dequeued and - * notified. - * - * The basic operation of the locking primitives (js_Lock, js_Unlock, - * js_Enqueue, and js_Dequeue) is compare-and-swap. Hence, when locking into - * the word pointed at by p, compare-and-swap(p, 0, A) success implies that p - * is unlocked. Similarly, when unlocking p, if compare-and-swap(p, A, 0) - * succeeds this implies that p is uncontended (no one is waiting because the - * wait bit is not set). - * - * When dequeueing, the lock is released, and one of the threads suspended on - * the lock is notified. If other threads still are waiting, the wait bit is - * kept (in js_Enqueue), and if not, the fat lock is deallocated. - * - * The functions js_Enqueue, js_Dequeue, js_SuspendThread, and js_ResumeThread - * are serialized using a global lock. For scalability, a hashtable of global - * locks is used, which is indexed modulo the thin lock pointer. - */ - -/* - * Invariants: - * (i) global lock is held - * (ii) fl->susp >= 0 - */ -static int -js_SuspendThread(JSThinLock *tl) -{ - JSFatLock *fl; - PRStatus stat; - - if (tl->fat == NULL) - fl = tl->fat = GetFatlock(tl); - else - fl = tl->fat; - JS_ASSERT(fl->susp >= 0); - fl->susp++; - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - stat = PR_WaitCondVar(fl->svar, PR_INTERVAL_NO_TIMEOUT); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); - js_LockGlobal(tl); - fl->susp--; - if (fl->susp == 0) { - PutFatlock(fl, tl); - tl->fat = NULL; - } - return tl->fat == NULL; -} - -/* - * (i) global lock is held - * (ii) fl->susp > 0 - */ -static void -js_ResumeThread(JSThinLock *tl) -{ - JSFatLock *fl = tl->fat; - PRStatus stat; - - JS_ASSERT(fl != NULL); - JS_ASSERT(fl->susp > 0); - PR_Lock(fl->slock); - js_UnlockGlobal(tl); - stat = PR_NotifyCondVar(fl->svar); - JS_ASSERT(stat != PR_FAILURE); - PR_Unlock(fl->slock); -} - -static void -js_Enqueue(JSThinLock *tl, jsword me) -{ - jsword o, n; - - js_LockGlobal(tl); - for (;;) { - o = ReadWord(tl->owner); - n = Thin_SetWait(o); - if (o != 0 && js_CompareAndSwap(&tl->owner, o, n)) { - if (js_SuspendThread(tl)) - me = Thin_RemoveWait(me); - else - me = Thin_SetWait(me); - } - else if (js_CompareAndSwap(&tl->owner, 0, me)) { - js_UnlockGlobal(tl); - return; - } - } -} - -static void -js_Dequeue(JSThinLock *tl) -{ - jsword o; - - js_LockGlobal(tl); - o = ReadWord(tl->owner); - JS_ASSERT(Thin_GetWait(o) != 0); - JS_ASSERT(tl->fat != NULL); - if (!js_CompareAndSwap(&tl->owner, o, 0)) /* release it */ - JS_ASSERT(0); - js_ResumeThread(tl); -} - -JS_INLINE void -js_Lock(JSThinLock *tl, jsword me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - if (js_CompareAndSwap(&tl->owner, 0, me)) - return; - if (Thin_RemoveWait(ReadWord(tl->owner)) != me) - js_Enqueue(tl, me); -#ifdef DEBUG - else - JS_ASSERT(0); -#endif -} - -JS_INLINE void -js_Unlock(JSThinLock *tl, jsword me) -{ - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - - /* - * Only me can hold the lock, no need to use compare and swap atomic - * operation for this common case. - */ - if (tl->owner == me) { - tl->owner = 0; - return; - } - JS_ASSERT(Thin_GetWait(tl->owner)); - if (Thin_RemoveWait(ReadWord(tl->owner)) == me) - js_Dequeue(tl); -#ifdef DEBUG - else - JS_ASSERT(0); /* unbalanced unlock */ -#endif -} - -#endif /* !NSPR_LOCK */ - -void -js_LockRuntime(JSRuntime *rt) -{ - PR_Lock(rt->rtLock); -#ifdef DEBUG - rt->rtLockOwner = js_CurrentThreadId(); -#endif -} - -void -js_UnlockRuntime(JSRuntime *rt) -{ -#ifdef DEBUG - rt->rtLockOwner = 0; -#endif - PR_Unlock(rt->rtLock); -} - -void -js_LockScope(JSContext *cx, JSScope *scope) -{ - jsword me = CX_THINLOCK_ID(cx); - - JS_ASSERT(CURRENT_THREAD_IS_ME(me)); - JS_ASSERT(scope->ownercx != cx); - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - if (scope->ownercx && ClaimScope(scope, cx)) - return; - - if (Thin_RemoveWait(ReadWord(scope->lock.owner)) == me) { - JS_ASSERT(scope->u.count > 0); - LOGIT(scope, '+'); - scope->u.count++; - } else { - JSThinLock *tl = &scope->lock; - JS_LOCK0(tl, me); - JS_ASSERT(scope->u.count == 0); - LOGIT(scope, '1'); - scope->u.count = 1; - } -} - -void -js_UnlockScope(JSContext *cx, JSScope *scope) -{ - jsword me = CX_THINLOCK_ID(cx); - - /* We hope compilers use me instead of reloading cx->thread in the macro. */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - if (cx->lockedSealedScope == scope) { - cx->lockedSealedScope = NULL; - return; - } - - /* - * If scope->ownercx is not null, it's likely that two contexts not using - * requests nested locks for scope. The first context, cx here, claimed - * scope; the second, scope->ownercx here, re-claimed it because the first - * was not in a request, or was on the same thread. We don't want to keep - * track of such nesting, because it penalizes the common non-nested case. - * Instead of asserting here and silently coping, we simply re-claim scope - * for cx and return. - * - * See http://bugzilla.mozilla.org/show_bug.cgi?id=229200 for a real world - * case where an asymmetric thread model (Mozilla's main thread is known - * to be the only thread that runs the GC) combined with multiple contexts - * per thread has led to such request-less nesting. - */ - if (scope->ownercx) { - JS_ASSERT(scope->u.count == 0); - JS_ASSERT(scope->lock.owner == 0); - scope->ownercx = cx; - return; - } - - JS_ASSERT(scope->u.count > 0); - if (Thin_RemoveWait(ReadWord(scope->lock.owner)) != me) { - JS_ASSERT(0); /* unbalanced unlock */ - return; - } - LOGIT(scope, '-'); - if (--scope->u.count == 0) { - JSThinLock *tl = &scope->lock; - JS_UNLOCK0(tl, me); - } -} - -/* - * NB: oldscope may be null if our caller is js_GetMutableScope and it just - * dropped the last reference to oldscope. - */ -void -js_TransferScopeLock(JSContext *cx, JSScope *oldscope, JSScope *newscope) -{ - jsword me; - JSThinLock *tl; - - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope)); - - /* - * If the last reference to oldscope went away, newscope needs no lock - * state update. - */ - if (!oldscope) - return; - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, oldscope)); - - /* - * Special case in js_LockScope and js_UnlockScope for the GC calling - * code that locks, unlocks, or mutates. Nothing to do in these cases, - * because scope and newscope were "locked" by the GC thread, so neither - * was actually locked. - */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - - /* - * Special case in js_LockObj and js_UnlockScope for locking the sealed - * scope of an object that owns that scope (the prototype or mutated obj - * for which OBJ_SCOPE(obj)->object == obj), and unlocking it. - */ - JS_ASSERT(cx->lockedSealedScope != newscope); - if (cx->lockedSealedScope == oldscope) { - JS_ASSERT(newscope->ownercx == cx || - (!newscope->ownercx && newscope->u.count == 1)); - cx->lockedSealedScope = NULL; - return; - } - - /* - * If oldscope is single-threaded, there's nothing to do. - */ - if (oldscope->ownercx) { - JS_ASSERT(oldscope->ownercx == cx); - JS_ASSERT(newscope->ownercx == cx || - (!newscope->ownercx && newscope->u.count == 1)); - return; - } - - /* - * We transfer oldscope->u.count only if newscope is not single-threaded. - * Flow unwinds from here through some number of JS_UNLOCK_SCOPE and/or - * JS_UNLOCK_OBJ macro calls, which will decrement newscope->u.count only - * if they find newscope->ownercx != cx. - */ - if (newscope->ownercx != cx) { - JS_ASSERT(!newscope->ownercx); - newscope->u.count = oldscope->u.count; - } - - /* - * Reset oldscope's lock state so that it is completely unlocked. - */ - LOGIT(oldscope, '0'); - oldscope->u.count = 0; - tl = &oldscope->lock; - me = CX_THINLOCK_ID(cx); - JS_UNLOCK0(tl, me); -} - -void -js_LockObj(JSContext *cx, JSObject *obj) -{ - JSScope *scope; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - - /* - * We must test whether the GC is calling and return without mutating any - * state, especially cx->lockedSealedScope. Note asymmetry with respect to - * js_UnlockObj, which is a thin-layer on top of js_UnlockScope. - */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return; - - for (;;) { - scope = OBJ_SCOPE(obj); - if (SCOPE_IS_SEALED(scope) && scope->object == obj && - !cx->lockedSealedScope) { - cx->lockedSealedScope = scope; - return; - } - - js_LockScope(cx, scope); - - /* If obj still has this scope, we're done. */ - if (scope == OBJ_SCOPE(obj)) - return; - - /* Lost a race with a mutator; retry with obj's new scope. */ - js_UnlockScope(cx, scope); - } -} - -void -js_UnlockObj(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(OBJ_IS_NATIVE(obj)); - js_UnlockScope(cx, OBJ_SCOPE(obj)); -} - -#ifdef DEBUG - -JSBool -js_IsRuntimeLocked(JSRuntime *rt) -{ - return js_CurrentThreadId() == rt->rtLockOwner; -} - -JSBool -js_IsObjLocked(JSContext *cx, JSObject *obj) -{ - JSScope *scope = OBJ_SCOPE(obj); - - return MAP_IS_NATIVE(&scope->map) && js_IsScopeLocked(cx, scope); -} - -JSBool -js_IsScopeLocked(JSContext *cx, JSScope *scope) -{ - /* Special case: the GC locking any object's scope, see js_LockScope. */ - if (CX_THREAD_IS_RUNNING_GC(cx)) - return JS_TRUE; - - /* Special case: locked object owning a sealed scope, see js_LockObj. */ - if (cx->lockedSealedScope == scope) - return JS_TRUE; - - /* - * General case: the scope is either exclusively owned (by cx), or it has - * a thin or fat lock to cope with shared (concurrent) ownership. - */ - if (scope->ownercx) { - JS_ASSERT(scope->ownercx == cx || scope->ownercx->thread == cx->thread); - return JS_TRUE; - } - return js_CurrentThreadId() == - ((JSThread *)Thin_RemoveWait(ReadWord(scope->lock.owner)))->id; -} - -#endif /* DEBUG */ -#endif /* JS_THREADSAFE */ diff --git a/spidermonkey/libjs/jslock.h b/spidermonkey/libjs/jslock.h deleted file mode 100644 index f9ed03d..0000000 --- a/spidermonkey/libjs/jslock.h +++ /dev/null @@ -1,266 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ -#ifndef jslock_h__ -#define jslock_h__ - -#ifdef JS_THREADSAFE - -#include "jstypes.h" -#include "pratom.h" -#include "prlock.h" -#include "prcvar.h" -#include "prthread.h" - -#include "jsprvtd.h" /* for JSScope, etc. */ -#include "jspubtd.h" /* for JSRuntime, etc. */ - -#define Thin_GetWait(W) ((jsword)(W) & 0x1) -#define Thin_SetWait(W) ((jsword)(W) | 0x1) -#define Thin_RemoveWait(W) ((jsword)(W) & ~0x1) - -typedef struct JSFatLock JSFatLock; - -struct JSFatLock { - int susp; - PRLock *slock; - PRCondVar *svar; - JSFatLock *next; - JSFatLock **prevp; -}; - -typedef struct JSThinLock { - jsword owner; - JSFatLock *fat; -} JSThinLock; - -#define CX_THINLOCK_ID(cx) ((jsword)(cx)->thread) -#define CURRENT_THREAD_IS_ME(me) (((JSThread *)me)->id == js_CurrentThreadId()) - -typedef PRLock JSLock; - -typedef struct JSFatLockTable { - JSFatLock *free; - JSFatLock *taken; -} JSFatLockTable; - -/* - * Atomic increment and decrement for a reference counter, given jsrefcount *p. - * NB: jsrefcount is int32, aka PRInt32, so that pratom.h functions work. - */ -#define JS_ATOMIC_INCREMENT(p) PR_AtomicIncrement((PRInt32 *)(p)) -#define JS_ATOMIC_DECREMENT(p) PR_AtomicDecrement((PRInt32 *)(p)) -#define JS_ATOMIC_ADD(p,v) PR_AtomicAdd((PRInt32 *)(p), (PRInt32)(v)) - -#define js_CurrentThreadId() (jsword)PR_GetCurrentThread() -#define JS_NEW_LOCK() PR_NewLock() -#define JS_DESTROY_LOCK(l) PR_DestroyLock(l) -#define JS_ACQUIRE_LOCK(l) PR_Lock(l) -#define JS_RELEASE_LOCK(l) PR_Unlock(l) -#define JS_LOCK0(P,M) js_Lock(P,M) -#define JS_UNLOCK0(P,M) js_Unlock(P,M) - -#define JS_NEW_CONDVAR(l) PR_NewCondVar(l) -#define JS_DESTROY_CONDVAR(cv) PR_DestroyCondVar(cv) -#define JS_WAIT_CONDVAR(cv,to) PR_WaitCondVar(cv,to) -#define JS_NO_TIMEOUT PR_INTERVAL_NO_TIMEOUT -#define JS_NOTIFY_CONDVAR(cv) PR_NotifyCondVar(cv) -#define JS_NOTIFY_ALL_CONDVAR(cv) PR_NotifyAllCondVar(cv) - -/* - * Include jsscope.h so JS_LOCK_OBJ macro callers don't have to include it. - * Since there is a JSThinLock member in JSScope, we can't nest this include - * much earlier (see JSThinLock's typedef, above). Yes, that means there is - * an #include cycle between jslock.h and jsscope.h: moderate-sized XXX here, - * to be fixed by moving JS_LOCK_SCOPE to jsscope.h, JS_LOCK_OBJ to jsobj.h, - * and so on. - */ -#include "jsscope.h" - -#define JS_LOCK_RUNTIME(rt) js_LockRuntime(rt) -#define JS_UNLOCK_RUNTIME(rt) js_UnlockRuntime(rt) - -/* - * NB: The JS_LOCK_OBJ and JS_UNLOCK_OBJ macros work *only* on native objects - * (objects for which OBJ_IS_NATIVE returns true). All uses of these macros in - * the engine are predicated on OBJ_IS_NATIVE or equivalent checks. These uses - * are for optimizations above the JSObjectOps layer, under which object locks - * normally hide. - */ -#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ - ? (void)0 \ - : (js_LockObj(cx, obj))) -#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->ownercx == (cx)) \ - ? (void)0 : js_UnlockObj(cx, obj)) - -#define JS_LOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ - : js_LockScope(cx, scope)) -#define JS_UNLOCK_SCOPE(cx,scope) ((scope)->ownercx == (cx) ? (void)0 \ - : js_UnlockScope(cx, scope)) -#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ - js_TransferScopeLock(cx, scope, newscope) - -extern void js_LockRuntime(JSRuntime *rt); -extern void js_UnlockRuntime(JSRuntime *rt); -extern void js_LockObj(JSContext *cx, JSObject *obj); -extern void js_UnlockObj(JSContext *cx, JSObject *obj); -extern void js_LockScope(JSContext *cx, JSScope *scope); -extern void js_UnlockScope(JSContext *cx, JSScope *scope); -extern int js_SetupLocks(int,int); -extern void js_CleanupLocks(); -extern void js_TransferScopeLock(JSContext *, JSScope *, JSScope *); -extern JS_FRIEND_API(jsval) -js_GetSlotThreadSafe(JSContext *, JSObject *, uint32); -extern void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval); -extern void js_InitLock(JSThinLock *); -extern void js_FinishLock(JSThinLock *); -extern void js_FinishSharingScope(JSRuntime *rt, JSScope *scope); - -#ifdef DEBUG - -#define JS_IS_RUNTIME_LOCKED(rt) js_IsRuntimeLocked(rt) -#define JS_IS_OBJ_LOCKED(cx,obj) js_IsObjLocked(cx,obj) -#define JS_IS_SCOPE_LOCKED(cx,scope) js_IsScopeLocked(cx,scope) - -extern JSBool js_IsRuntimeLocked(JSRuntime *rt); -extern JSBool js_IsObjLocked(JSContext *cx, JSObject *obj); -extern JSBool js_IsScopeLocked(JSContext *cx, JSScope *scope); - -#else - -#define JS_IS_RUNTIME_LOCKED(rt) 0 -#define JS_IS_OBJ_LOCKED(cx,obj) 1 -#define JS_IS_SCOPE_LOCKED(cx,scope) 1 - -#endif /* DEBUG */ - -#define JS_LOCK_OBJ_VOID(cx, obj, e) \ - JS_BEGIN_MACRO \ - JS_LOCK_OBJ(cx, obj); \ - e; \ - JS_UNLOCK_OBJ(cx, obj); \ - JS_END_MACRO - -#define JS_LOCK_VOID(cx, e) \ - JS_BEGIN_MACRO \ - JSRuntime *_rt = (cx)->runtime; \ - JS_LOCK_RUNTIME_VOID(_rt, e); \ - JS_END_MACRO - -/* FIXME: bug 353962 hackaround */ -#define JS_USE_ONLY_NSPR_LOCKS 1 - -#if defined(JS_USE_ONLY_NSPR_LOCKS) || \ - !( (defined(_WIN32) && defined(_M_IX86)) || \ - (defined(__GNUC__) && defined(__i386__)) || \ - ((defined(__USLC__) || defined(_SCO_DS)) && defined(i386)) || \ - (defined(SOLARIS) && defined(sparc) && defined(ULTRA_SPARC)) || \ - defined(AIX) ) - -#define NSPR_LOCK 1 - -#undef JS_LOCK0 -#undef JS_UNLOCK0 -#define JS_LOCK0(P,M) (JS_ACQUIRE_LOCK(((JSLock*)(P)->fat)), (P)->owner = (M)) -#define JS_UNLOCK0(P,M) ((P)->owner = 0, JS_RELEASE_LOCK(((JSLock*)(P)->fat))) - -#else /* arch-tests */ - -#undef NSPR_LOCK - -extern JS_INLINE void js_Lock(JSThinLock *tl, jsword me); -extern JS_INLINE void js_Unlock(JSThinLock *tl, jsword me); - -#endif /* arch-tests */ - -#else /* !JS_THREADSAFE */ - -#define JS_ATOMIC_INCREMENT(p) (++*(p)) -#define JS_ATOMIC_DECREMENT(p) (--*(p)) -#define JS_ATOMIC_ADD(p,v) (*(p) += (v)) - -#define JS_CurrentThreadId() 0 -#define JS_NEW_LOCK() NULL -#define JS_DESTROY_LOCK(l) ((void)0) -#define JS_ACQUIRE_LOCK(l) ((void)0) -#define JS_RELEASE_LOCK(l) ((void)0) -#define JS_LOCK0(P,M) ((void)0) -#define JS_UNLOCK0(P,M) ((void)0) - -#define JS_NEW_CONDVAR(l) NULL -#define JS_DESTROY_CONDVAR(cv) ((void)0) -#define JS_WAIT_CONDVAR(cv,to) ((void)0) -#define JS_NOTIFY_CONDVAR(cv) ((void)0) -#define JS_NOTIFY_ALL_CONDVAR(cv) ((void)0) - -#define JS_LOCK_RUNTIME(rt) ((void)0) -#define JS_UNLOCK_RUNTIME(rt) ((void)0) -#define JS_LOCK_OBJ(cx,obj) ((void)0) -#define JS_UNLOCK_OBJ(cx,obj) ((void)0) -#define JS_LOCK_OBJ_VOID(cx,obj,e) (e) -#define JS_LOCK_SCOPE(cx,scope) ((void)0) -#define JS_UNLOCK_SCOPE(cx,scope) ((void)0) -#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0) - -#define JS_IS_RUNTIME_LOCKED(rt) 1 -#define JS_IS_OBJ_LOCKED(cx,obj) 1 -#define JS_IS_SCOPE_LOCKED(cx,scope) 1 -#define JS_LOCK_VOID(cx, e) JS_LOCK_RUNTIME_VOID((cx)->runtime, e) - -#endif /* !JS_THREADSAFE */ - -#define JS_LOCK_RUNTIME_VOID(rt,e) \ - JS_BEGIN_MACRO \ - JS_LOCK_RUNTIME(rt); \ - e; \ - JS_UNLOCK_RUNTIME(rt); \ - JS_END_MACRO - -#define JS_LOCK_GC(rt) JS_ACQUIRE_LOCK((rt)->gcLock) -#define JS_UNLOCK_GC(rt) JS_RELEASE_LOCK((rt)->gcLock) -#define JS_LOCK_GC_VOID(rt,e) (JS_LOCK_GC(rt), (e), JS_UNLOCK_GC(rt)) -#define JS_AWAIT_GC_DONE(rt) JS_WAIT_CONDVAR((rt)->gcDone, JS_NO_TIMEOUT) -#define JS_NOTIFY_GC_DONE(rt) JS_NOTIFY_ALL_CONDVAR((rt)->gcDone) -#define JS_AWAIT_REQUEST_DONE(rt) JS_WAIT_CONDVAR((rt)->requestDone, \ - JS_NO_TIMEOUT) -#define JS_NOTIFY_REQUEST_DONE(rt) JS_NOTIFY_CONDVAR((rt)->requestDone) - -#define JS_LOCK(P,CX) JS_LOCK0(P, CX_THINLOCK_ID(CX)) -#define JS_UNLOCK(P,CX) JS_UNLOCK0(P, CX_THINLOCK_ID(CX)) - -#endif /* jslock_h___ */ diff --git a/spidermonkey/libjs/jslog2.c b/spidermonkey/libjs/jslog2.c deleted file mode 100644 index 876e528..0000000 --- a/spidermonkey/libjs/jslog2.c +++ /dev/null @@ -1,94 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsstddef.h" -#include "jsbit.h" -#include "jsutil.h" - -/* -** Compute the log of the least power of 2 greater than or equal to n -*/ -JS_PUBLIC_API(JSIntn) JS_CeilingLog2(JSUint32 n) -{ - JSIntn log2; - - JS_CEILING_LOG2(log2, n); - return log2; -} - -/* -** Compute the log of the greatest power of 2 less than or equal to n. -** This really just finds the highest set bit in the word. -*/ -JS_PUBLIC_API(JSIntn) JS_FloorLog2(JSUint32 n) -{ - JSIntn log2; - - JS_FLOOR_LOG2(log2, n); - return log2; -} - -/* - * js_FloorLog2wImpl has to be defined only for 64-bit non-GCC case. - */ -#if !defined(JS_HAS_GCC_BUILTIN_CLZ) && JS_BYTES_PER_WORD == 8 - -JSUword -js_FloorLog2wImpl(JSUword n) -{ - JSUword log2, m; - - JS_ASSERT(n != 0); - - log2 = 0; - m = n >> 32; - if (m != 0) { n = m; log2 = 32; } - m = n >> 16; - if (m != 0) { n = m; log2 |= 16; } - m = n >> 8; - if (m != 0) { n = m; log2 |= 8; } - m = n >> 4; - if (m != 0) { n = m; log2 |= 4; } - m = n >> 2; - if (m != 0) { n = m; log2 |= 2; } - log2 |= (n >> 1); - - return log2; -} - -#endif diff --git a/spidermonkey/libjs/jslong.c b/spidermonkey/libjs/jslong.c deleted file mode 100644 index 9a4a5b4..0000000 --- a/spidermonkey/libjs/jslong.c +++ /dev/null @@ -1,281 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsstddef.h" -#include "jstypes.h" -#include "jslong.h" - -static JSInt64 ll_zero = JSLL_INIT( 0x00000000,0x00000000 ); -static JSInt64 ll_maxint = JSLL_INIT( 0x7fffffff, 0xffffffff ); -static JSInt64 ll_minint = JSLL_INIT( 0x80000000, 0x00000000 ); - -#ifdef HAVE_WATCOM_BUG_2 -JSInt64 __pascal __loadds __export - JSLL_Zero(void) { return ll_zero; } -JSInt64 __pascal __loadds __export - JSLL_MaxInt(void) { return ll_maxint; } -JSInt64 __pascal __loadds __export - JSLL_MinInt(void) { return ll_minint; } -#else -JS_PUBLIC_API(JSInt64) JSLL_Zero(void) { return ll_zero; } -JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void) { return ll_maxint; } -JS_PUBLIC_API(JSInt64) JSLL_MinInt(void) { return ll_minint; } -#endif - -#ifndef JS_HAVE_LONG_LONG -/* -** Divide 64-bit a by 32-bit b, which must be normalized so its high bit is 1. -*/ -static void norm_udivmod32(JSUint32 *qp, JSUint32 *rp, JSUint64 a, JSUint32 b) -{ - JSUint32 d1, d0, q1, q0; - JSUint32 r1, r0, m; - - d1 = jshi16(b); - d0 = jslo16(b); - r1 = a.hi % d1; - q1 = a.hi / d1; - m = q1 * d0; - r1 = (r1 << 16) | jshi16(a.lo); - if (r1 < m) { - q1--, r1 += b; - if (r1 >= b /* i.e., we didn't get a carry when adding to r1 */ - && r1 < m) { - q1--, r1 += b; - } - } - r1 -= m; - r0 = r1 % d1; - q0 = r1 / d1; - m = q0 * d0; - r0 = (r0 << 16) | jslo16(a.lo); - if (r0 < m) { - q0--, r0 += b; - if (r0 >= b - && r0 < m) { - q0--, r0 += b; - } - } - *qp = (q1 << 16) | q0; - *rp = r0 - m; -} - -static JSUint32 CountLeadingZeros(JSUint32 a) -{ - JSUint32 t; - JSUint32 r = 32; - - if ((t = a >> 16) != 0) - r -= 16, a = t; - if ((t = a >> 8) != 0) - r -= 8, a = t; - if ((t = a >> 4) != 0) - r -= 4, a = t; - if ((t = a >> 2) != 0) - r -= 2, a = t; - if ((t = a >> 1) != 0) - r -= 1, a = t; - if (a & 1) - r--; - return r; -} - -JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b) -{ - JSUint32 n0, n1, n2; - JSUint32 q0, q1; - JSUint32 rsh, lsh; - - n0 = a.lo; - n1 = a.hi; - - if (b.hi == 0) { - if (b.lo > n1) { - /* (0 q0) = (n1 n0) / (0 D0) */ - - lsh = CountLeadingZeros(b.lo); - - if (lsh) { - /* - * Normalize, i.e. make the most significant bit of the - * denominator be set. - */ - b.lo = b.lo << lsh; - n1 = (n1 << lsh) | (n0 >> (32 - lsh)); - n0 = n0 << lsh; - } - - a.lo = n0, a.hi = n1; - norm_udivmod32(&q0, &n0, a, b.lo); - q1 = 0; - - /* remainder is in n0 >> lsh */ - } else { - /* (q1 q0) = (n1 n0) / (0 d0) */ - - if (b.lo == 0) /* user wants to divide by zero! */ - b.lo = 1 / b.lo; /* so go ahead and crash */ - - lsh = CountLeadingZeros(b.lo); - - if (lsh == 0) { - /* - * From (n1 >= b.lo) - * && (the most significant bit of b.lo is set), - * conclude that - * (the most significant bit of n1 is set) - * && (the leading quotient digit q1 = 1). - * - * This special case is necessary, not an optimization - * (Shifts counts of 32 are undefined). - */ - n1 -= b.lo; - q1 = 1; - } else { - /* - * Normalize. - */ - rsh = 32 - lsh; - - b.lo = b.lo << lsh; - n2 = n1 >> rsh; - n1 = (n1 << lsh) | (n0 >> rsh); - n0 = n0 << lsh; - - a.lo = n1, a.hi = n2; - norm_udivmod32(&q1, &n1, a, b.lo); - } - - /* n1 != b.lo... */ - - a.lo = n0, a.hi = n1; - norm_udivmod32(&q0, &n0, a, b.lo); - - /* remainder in n0 >> lsh */ - } - - if (rp) { - rp->lo = n0 >> lsh; - rp->hi = 0; - } - } else { - if (b.hi > n1) { - /* (0 0) = (n1 n0) / (D1 d0) */ - - q0 = 0; - q1 = 0; - - /* remainder in (n1 n0) */ - if (rp) { - rp->lo = n0; - rp->hi = n1; - } - } else { - /* (0 q0) = (n1 n0) / (d1 d0) */ - - lsh = CountLeadingZeros(b.hi); - if (lsh == 0) { - /* - * From (n1 >= b.hi) - * && (the most significant bit of b.hi is set), - * conclude that - * (the most significant bit of n1 is set) - * && (the quotient digit q0 = 0 or 1). - * - * This special case is necessary, not an optimization. - */ - - /* - * The condition on the next line takes advantage of that - * n1 >= b.hi (true due to control flow). - */ - if (n1 > b.hi || n0 >= b.lo) { - q0 = 1; - a.lo = n0, a.hi = n1; - JSLL_SUB(a, a, b); - } else { - q0 = 0; - } - q1 = 0; - - if (rp) { - rp->lo = n0; - rp->hi = n1; - } - } else { - JSInt64 m; - - /* - * Normalize. - */ - rsh = 32 - lsh; - - b.hi = (b.hi << lsh) | (b.lo >> rsh); - b.lo = b.lo << lsh; - n2 = n1 >> rsh; - n1 = (n1 << lsh) | (n0 >> rsh); - n0 = n0 << lsh; - - a.lo = n1, a.hi = n2; - norm_udivmod32(&q0, &n1, a, b.hi); - JSLL_MUL32(m, q0, b.lo); - - if ((m.hi > n1) || ((m.hi == n1) && (m.lo > n0))) { - q0--; - JSLL_SUB(m, m, b); - } - - q1 = 0; - - /* Remainder is ((n1 n0) - (m1 m0)) >> lsh */ - if (rp) { - a.lo = n0, a.hi = n1; - JSLL_SUB(a, a, m); - rp->lo = (a.hi << rsh) | (a.lo >> lsh); - rp->hi = a.hi >> lsh; - } - } - } - } - - if (qp) { - qp->lo = q0; - qp->hi = q1; - } -} -#endif /* !JS_HAVE_LONG_LONG */ diff --git a/spidermonkey/libjs/jslong.h b/spidermonkey/libjs/jslong.h deleted file mode 100644 index 059cf00..0000000 --- a/spidermonkey/libjs/jslong.h +++ /dev/null @@ -1,437 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -** File: jslong.h -** Description: Portable access to 64 bit numerics -** -** Long-long (64-bit signed integer type) support. Some C compilers -** don't support 64 bit integers yet, so we use these macros to -** support both machines that do and don't. -**/ -#ifndef jslong_h___ -#define jslong_h___ - -#include "jstypes.h" - -JS_BEGIN_EXTERN_C - -/*********************************************************************** -** DEFINES: JSLL_MaxInt -** JSLL_MinInt -** JSLL_Zero -** DESCRIPTION: -** Various interesting constants and static variable -** initializer -***********************************************************************/ -#ifdef HAVE_WATCOM_BUG_2 -JSInt64 __pascal __loadds __export - JSLL_MaxInt(void); -JSInt64 __pascal __loadds __export - JSLL_MinInt(void); -JSInt64 __pascal __loadds __export - JSLL_Zero(void); -#else -extern JS_PUBLIC_API(JSInt64) JSLL_MaxInt(void); -extern JS_PUBLIC_API(JSInt64) JSLL_MinInt(void); -extern JS_PUBLIC_API(JSInt64) JSLL_Zero(void); -#endif - -#define JSLL_MAXINT JSLL_MaxInt() -#define JSLL_MININT JSLL_MinInt() -#define JSLL_ZERO JSLL_Zero() - -#ifdef JS_HAVE_LONG_LONG - -#if JS_BYTES_PER_LONG == 8 -#define JSLL_INIT(hi, lo) ((hi ## L << 32) + lo ## L) -#elif (defined(WIN32) || defined(WIN16)) && !defined(__GNUC__) -#define JSLL_INIT(hi, lo) ((hi ## i64 << 32) + lo ## i64) -#else -#define JSLL_INIT(hi, lo) ((hi ## LL << 32) + lo ## LL) -#endif - -/*********************************************************************** -** MACROS: JSLL_* -** DESCRIPTION: -** The following macros define portable access to the 64 bit -** math facilities. -** -***********************************************************************/ - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_IS_ZERO Test for zero -** JSLL_EQ Test for equality -** JSLL_NE Test for inequality -** JSLL_GE_ZERO Test for zero or positive -** JSLL_CMP Compare two values -***********************************************************************/ -#define JSLL_IS_ZERO(a) ((a) == 0) -#define JSLL_EQ(a, b) ((a) == (b)) -#define JSLL_NE(a, b) ((a) != (b)) -#define JSLL_GE_ZERO(a) ((a) >= 0) -#define JSLL_CMP(a, op, b) ((JSInt64)(a) op (JSInt64)(b)) -#define JSLL_UCMP(a, op, b) ((JSUint64)(a) op (JSUint64)(b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_AND Logical and -** JSLL_OR Logical or -** JSLL_XOR Logical exclusion -** JSLL_OR2 A disgusting deviation -** JSLL_NOT Negation (one's compliment) -***********************************************************************/ -#define JSLL_AND(r, a, b) ((r) = (a) & (b)) -#define JSLL_OR(r, a, b) ((r) = (a) | (b)) -#define JSLL_XOR(r, a, b) ((r) = (a) ^ (b)) -#define JSLL_OR2(r, a) ((r) = (r) | (a)) -#define JSLL_NOT(r, a) ((r) = ~(a)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_NEG Negation (two's compliment) -** JSLL_ADD Summation (two's compliment) -** JSLL_SUB Difference (two's compliment) -***********************************************************************/ -#define JSLL_NEG(r, a) ((r) = -(a)) -#define JSLL_ADD(r, a, b) ((r) = (a) + (b)) -#define JSLL_SUB(r, a, b) ((r) = (a) - (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_MUL Product (two's compliment) -** JSLL_DIV Quotient (two's compliment) -** JSLL_MOD Modulus (two's compliment) -***********************************************************************/ -#define JSLL_MUL(r, a, b) ((r) = (a) * (b)) -#define JSLL_DIV(r, a, b) ((r) = (a) / (b)) -#define JSLL_MOD(r, a, b) ((r) = (a) % (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_SHL Shift left [0..64] bits -** JSLL_SHR Shift right [0..64] bits with sign extension -** JSLL_USHR Unsigned shift right [0..64] bits -** JSLL_ISHL Signed shift left [0..64] bits -***********************************************************************/ -#define JSLL_SHL(r, a, b) ((r) = (JSInt64)(a) << (b)) -#define JSLL_SHR(r, a, b) ((r) = (JSInt64)(a) >> (b)) -#define JSLL_USHR(r, a, b) ((r) = (JSUint64)(a) >> (b)) -#define JSLL_ISHL(r, a, b) ((r) = (JSInt64)(a) << (b)) - -/*********************************************************************** -** MACROS: JSLL_ -** -** JSLL_L2I Convert to signed 32 bit -** JSLL_L2UI Convert to unsigned 32 bit -** JSLL_L2F Convert to floating point -** JSLL_L2D Convert to floating point -** JSLL_I2L Convert signed to 64 bit -** JSLL_UI2L Convert unsigned to 64 bit -** JSLL_F2L Convert float to 64 bit -** JSLL_D2L Convert float to 64 bit -***********************************************************************/ -#define JSLL_L2I(i, l) ((i) = (JSInt32)(l)) -#define JSLL_L2UI(ui, l) ((ui) = (JSUint32)(l)) -#define JSLL_L2F(f, l) ((f) = (JSFloat64)(l)) -#define JSLL_L2D(d, l) ((d) = (JSFloat64)(l)) - -#define JSLL_I2L(l, i) ((l) = (JSInt64)(i)) -#define JSLL_UI2L(l, ui) ((l) = (JSInt64)(ui)) -#define JSLL_F2L(l, f) ((l) = (JSInt64)(f)) -#define JSLL_D2L(l, d) ((l) = (JSInt64)(d)) - -/*********************************************************************** -** MACROS: JSLL_UDIVMOD -** DESCRIPTION: -** Produce both a quotient and a remainder given an unsigned -** INPUTS: JSUint64 a: The dividend of the operation -** JSUint64 b: The quotient of the operation -** OUTPUTS: JSUint64 *qp: pointer to quotient -** JSUint64 *rp: pointer to remainder -***********************************************************************/ -#define JSLL_UDIVMOD(qp, rp, a, b) \ - (*(qp) = ((JSUint64)(a) / (b)), \ - *(rp) = ((JSUint64)(a) % (b))) - -#else /* !JS_HAVE_LONG_LONG */ - -#ifdef IS_LITTLE_ENDIAN -#define JSLL_INIT(hi, lo) {JS_INT32(lo), JS_INT32(hi)} -#else -#define JSLL_INIT(hi, lo) {JS_INT32(hi), JS_INT32(lo)} -#endif - -#define JSLL_IS_ZERO(a) (((a).hi == 0) && ((a).lo == 0)) -#define JSLL_EQ(a, b) (((a).hi == (b).hi) && ((a).lo == (b).lo)) -#define JSLL_NE(a, b) (((a).hi != (b).hi) || ((a).lo != (b).lo)) -#define JSLL_GE_ZERO(a) (((a).hi >> 31) == 0) - -#ifdef DEBUG -#define JSLL_CMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_CMP(a, op, b)) -#define JSLL_UCMP(a, op, b) (JS_ASSERT((#op)[1] != '='), JSLL_REAL_UCMP(a, op, b)) -#else -#define JSLL_CMP(a, op, b) JSLL_REAL_CMP(a, op, b) -#define JSLL_UCMP(a, op, b) JSLL_REAL_UCMP(a, op, b) -#endif - -#define JSLL_REAL_CMP(a,op,b) (((JSInt32)(a).hi op (JSInt32)(b).hi) || \ - (((a).hi == (b).hi) && ((a).lo op (b).lo))) -#define JSLL_REAL_UCMP(a,op,b) (((a).hi op (b).hi) || \ - (((a).hi == (b).hi) && ((a).lo op (b).lo))) - -#define JSLL_AND(r, a, b) ((r).lo = (a).lo & (b).lo, \ - (r).hi = (a).hi & (b).hi) -#define JSLL_OR(r, a, b) ((r).lo = (a).lo | (b).lo, \ - (r).hi = (a).hi | (b).hi) -#define JSLL_XOR(r, a, b) ((r).lo = (a).lo ^ (b).lo, \ - (r).hi = (a).hi ^ (b).hi) -#define JSLL_OR2(r, a) ((r).lo = (r).lo | (a).lo, \ - (r).hi = (r).hi | (a).hi) -#define JSLL_NOT(r, a) ((r).lo = ~(a).lo, \ - (r).hi = ~(a).hi) - -#define JSLL_NEG(r, a) ((r).lo = -(JSInt32)(a).lo, \ - (r).hi = -(JSInt32)(a).hi - ((r).lo != 0)) -#define JSLL_ADD(r, a, b) { \ - JSInt64 _a, _b; \ - _a = a; _b = b; \ - (r).lo = _a.lo + _b.lo; \ - (r).hi = _a.hi + _b.hi + ((r).lo < _b.lo); \ -} - -#define JSLL_SUB(r, a, b) { \ - JSInt64 _a, _b; \ - _a = a; _b = b; \ - (r).lo = _a.lo - _b.lo; \ - (r).hi = _a.hi - _b.hi - (_a.lo < _b.lo); \ -} - -#define JSLL_MUL(r, a, b) { \ - JSInt64 _a, _b; \ - _a = a; _b = b; \ - JSLL_MUL32(r, _a.lo, _b.lo); \ - (r).hi += _a.hi * _b.lo + _a.lo * _b.hi; \ -} - -#define jslo16(a) ((a) & JS_BITMASK(16)) -#define jshi16(a) ((a) >> 16) - -#define JSLL_MUL32(r, a, b) { \ - JSUint32 _a1, _a0, _b1, _b0, _y0, _y1, _y2, _y3; \ - _a1 = jshi16(a), _a0 = jslo16(a); \ - _b1 = jshi16(b), _b0 = jslo16(b); \ - _y0 = _a0 * _b0; \ - _y1 = _a0 * _b1; \ - _y2 = _a1 * _b0; \ - _y3 = _a1 * _b1; \ - _y1 += jshi16(_y0); /* can't carry */ \ - _y1 += _y2; /* might carry */ \ - if (_y1 < _y2) \ - _y3 += (JSUint32)(JS_BIT(16)); /* propagate */ \ - (r).lo = (jslo16(_y1) << 16) + jslo16(_y0); \ - (r).hi = _y3 + jshi16(_y1); \ -} - -#define JSLL_UDIVMOD(qp, rp, a, b) jsll_udivmod(qp, rp, a, b) - -extern JS_PUBLIC_API(void) jsll_udivmod(JSUint64 *qp, JSUint64 *rp, JSUint64 a, JSUint64 b); - -#define JSLL_DIV(r, a, b) { \ - JSInt64 _a, _b; \ - JSUint32 _negative = (JSInt32)(a).hi < 0; \ - if (_negative) { \ - JSLL_NEG(_a, a); \ - } else { \ - _a = a; \ - } \ - if ((JSInt32)(b).hi < 0) { \ - _negative ^= 1; \ - JSLL_NEG(_b, b); \ - } else { \ - _b = b; \ - } \ - JSLL_UDIVMOD(&(r), 0, _a, _b); \ - if (_negative) \ - JSLL_NEG(r, r); \ -} - -#define JSLL_MOD(r, a, b) { \ - JSInt64 _a, _b; \ - JSUint32 _negative = (JSInt32)(a).hi < 0; \ - if (_negative) { \ - JSLL_NEG(_a, a); \ - } else { \ - _a = a; \ - } \ - if ((JSInt32)(b).hi < 0) { \ - JSLL_NEG(_b, b); \ - } else { \ - _b = b; \ - } \ - JSLL_UDIVMOD(0, &(r), _a, _b); \ - if (_negative) \ - JSLL_NEG(r, r); \ -} - -#define JSLL_SHL(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a = a; \ - if ((b) < 32) { \ - (r).lo = _a.lo << ((b) & 31); \ - (r).hi = (_a.hi << ((b) & 31)) | (_a.lo >> (32 - (b))); \ - } else { \ - (r).lo = 0; \ - (r).hi = _a.lo << ((b) & 31); \ - } \ - } else { \ - (r) = (a); \ - } \ -} - -/* a is an JSInt32, b is JSInt32, r is JSInt64 */ -#define JSLL_ISHL(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a.lo = (a); \ - _a.hi = 0; \ - if ((b) < 32) { \ - (r).lo = (a) << ((b) & 31); \ - (r).hi = ((a) >> (32 - (b))); \ - } else { \ - (r).lo = 0; \ - (r).hi = (a) << ((b) & 31); \ - } \ - } else { \ - (r).lo = (a); \ - (r).hi = 0; \ - } \ -} - -#define JSLL_SHR(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a = a; \ - if ((b) < 32) { \ - (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ - (r).hi = (JSInt32)_a.hi >> ((b) & 31); \ - } else { \ - (r).lo = (JSInt32)_a.hi >> ((b) & 31); \ - (r).hi = (JSInt32)_a.hi >> 31; \ - } \ - } else { \ - (r) = (a); \ - } \ -} - -#define JSLL_USHR(r, a, b) { \ - if (b) { \ - JSInt64 _a; \ - _a = a; \ - if ((b) < 32) { \ - (r).lo = (_a.hi << (32 - (b))) | (_a.lo >> ((b) & 31)); \ - (r).hi = _a.hi >> ((b) & 31); \ - } else { \ - (r).lo = _a.hi >> ((b) & 31); \ - (r).hi = 0; \ - } \ - } else { \ - (r) = (a); \ - } \ -} - -#define JSLL_L2I(i, l) ((i) = (l).lo) -#define JSLL_L2UI(ui, l) ((ui) = (l).lo) -#define JSLL_L2F(f, l) { double _d; JSLL_L2D(_d, l); (f) = (JSFloat64)_d; } - -#define JSLL_L2D(d, l) { \ - int _negative; \ - JSInt64 _absval; \ - \ - _negative = (l).hi >> 31; \ - if (_negative) { \ - JSLL_NEG(_absval, l); \ - } else { \ - _absval = l; \ - } \ - (d) = (double)_absval.hi * 4.294967296e9 + _absval.lo; \ - if (_negative) \ - (d) = -(d); \ -} - -#define JSLL_I2L(l, i) { JSInt32 _i = (i) >> 31; (l).lo = (i); (l).hi = _i; } -#define JSLL_UI2L(l, ui) ((l).lo = (ui), (l).hi = 0) -#define JSLL_F2L(l, f) { double _d = (double)f; JSLL_D2L(l, _d); } - -#define JSLL_D2L(l, d) { \ - int _negative; \ - double _absval, _d_hi; \ - JSInt64 _lo_d; \ - \ - _negative = ((d) < 0); \ - _absval = _negative ? -(d) : (d); \ - \ - (l).hi = _absval / 4.294967296e9; \ - (l).lo = 0; \ - JSLL_L2D(_d_hi, l); \ - _absval -= _d_hi; \ - _lo_d.hi = 0; \ - if (_absval < 0) { \ - _lo_d.lo = -_absval; \ - JSLL_SUB(l, l, _lo_d); \ - } else { \ - _lo_d.lo = _absval; \ - JSLL_ADD(l, l, _lo_d); \ - } \ - \ - if (_negative) \ - JSLL_NEG(l, l); \ -} - -#endif /* !JS_HAVE_LONG_LONG */ - -JS_END_EXTERN_C - -#endif /* jslong_h___ */ diff --git a/spidermonkey/libjs/jsmath.c b/spidermonkey/libjs/jsmath.c deleted file mode 100644 index 2062916..0000000 --- a/spidermonkey/libjs/jsmath.c +++ /dev/null @@ -1,514 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS math package. - */ -#include "jsstddef.h" -#include "jslibmath.h" -#include -#include "jstypes.h" -#include "jslong.h" -#include "prmjtime.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jslock.h" -#include "jsmath.h" -#include "jsnum.h" -#include "jsobj.h" - -#ifndef M_E -#define M_E 2.7182818284590452354 -#endif -#ifndef M_LOG2E -#define M_LOG2E 1.4426950408889634074 -#endif -#ifndef M_LOG10E -#define M_LOG10E 0.43429448190325182765 -#endif -#ifndef M_LN2 -#define M_LN2 0.69314718055994530942 -#endif -#ifndef M_LN10 -#define M_LN10 2.30258509299404568402 -#endif -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 -#endif -#ifndef M_SQRT1_2 -#define M_SQRT1_2 0.70710678118654752440 -#endif - -static JSConstDoubleSpec math_constants[] = { - {M_E, "E", 0, {0,0,0}}, - {M_LOG2E, "LOG2E", 0, {0,0,0}}, - {M_LOG10E, "LOG10E", 0, {0,0,0}}, - {M_LN2, "LN2", 0, {0,0,0}}, - {M_LN10, "LN10", 0, {0,0,0}}, - {M_PI, "PI", 0, {0,0,0}}, - {M_SQRT2, "SQRT2", 0, {0,0,0}}, - {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}}, - {0,0,0,{0,0,0}} -}; - -JSClass js_MathClass = { - js_Math_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Math), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -math_abs(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_fabs(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_acos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_acos(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_asin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_asin(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_atan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_atan(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_atan2(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, y, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - if (!js_ValueToNumber(cx, argv[1], &y)) - return JS_FALSE; -#if !JS_USE_FDLIBM_MATH && defined(_MSC_VER) - /* - * MSVC's atan2 does not yield the result demanded by ECMA when both x - * and y are infinite. - * - The result is a multiple of pi/4. - * - The sign of x determines the sign of the result. - * - The sign of y determines the multiplicator, 1 or 3. - */ - if (JSDOUBLE_IS_INFINITE(x) && JSDOUBLE_IS_INFINITE(y)) { - z = fd_copysign(M_PI / 4, x); - if (y < 0) - z *= 3; - return js_NewDoubleValue(cx, z, rval); - } -#endif - z = fd_atan2(x, y); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_ceil(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_ceil(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_cos(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_cos(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_exp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; -#ifdef _WIN32 - if (!JSDOUBLE_IS_NaN(x)) { - if (x == *cx->runtime->jsPositiveInfinity) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); - return JS_TRUE; - } - if (x == *cx->runtime->jsNegativeInfinity) { - *rval = JSVAL_ZERO; - return JS_TRUE; - } - } -#endif - z = fd_exp(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_floor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_floor(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_log(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_max(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z = *cx->runtime->jsNegativeInfinity; - uintN i; - - if (argc == 0) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNegativeInfinity); - return JS_TRUE; - } - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &x)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(x)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - if (x == 0 && x == z && fd_copysign(1.0, z) == -1) - z = x; - else - z = (x > z) ? x : z; - } - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_min(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z = *cx->runtime->jsPositiveInfinity; - uintN i; - - if (argc == 0) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsPositiveInfinity); - return JS_TRUE; - } - for (i = 0; i < argc; i++) { - if (!js_ValueToNumber(cx, argv[i], &x)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(x)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - if (x == 0 && x == z && fd_copysign(1.0,x) == -1) - z = x; - else - z = (x < z) ? x : z; - } - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_pow(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, y, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - if (!js_ValueToNumber(cx, argv[1], &y)) - return JS_FALSE; -#if !JS_USE_FDLIBM_MATH - /* - * Because C99 and ECMA specify different behavior for pow(), - * we need to wrap the libm call to make it ECMA compliant. - */ - if (!JSDOUBLE_IS_FINITE(y) && (x == 1.0 || x == -1.0)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - /* pow(x, +-0) is always 1, even for x = NaN. */ - if (y == 0) { - *rval = JSVAL_ONE; - return JS_TRUE; - } -#endif - z = fd_pow(x, y); - return js_NewNumberValue(cx, z, rval); -} - -/* - * Math.random() support, lifted from java.util.Random.java. - */ -static void -random_setSeed(JSRuntime *rt, int64 seed) -{ - int64 tmp; - - JSLL_I2L(tmp, 1000); - JSLL_DIV(seed, seed, tmp); - JSLL_XOR(tmp, seed, rt->rngMultiplier); - JSLL_AND(rt->rngSeed, tmp, rt->rngMask); -} - -static void -random_init(JSRuntime *rt) -{ - int64 tmp, tmp2; - - /* Do at most once. */ - if (rt->rngInitialized) - return; - rt->rngInitialized = JS_TRUE; - - /* rt->rngMultiplier = 0x5DEECE66DL */ - JSLL_ISHL(tmp, 0x5, 32); - JSLL_UI2L(tmp2, 0xDEECE66DL); - JSLL_OR(rt->rngMultiplier, tmp, tmp2); - - /* rt->rngAddend = 0xBL */ - JSLL_I2L(rt->rngAddend, 0xBL); - - /* rt->rngMask = (1L << 48) - 1 */ - JSLL_I2L(tmp, 1); - JSLL_SHL(tmp2, tmp, 48); - JSLL_SUB(rt->rngMask, tmp2, tmp); - - /* rt->rngDscale = (jsdouble)(1L << 53) */ - JSLL_SHL(tmp2, tmp, 53); - JSLL_L2D(rt->rngDscale, tmp2); - - /* Finally, set the seed from current time. */ - random_setSeed(rt, PRMJ_Now()); -} - -static uint32 -random_next(JSRuntime *rt, int bits) -{ - int64 nextseed, tmp; - uint32 retval; - - JSLL_MUL(nextseed, rt->rngSeed, rt->rngMultiplier); - JSLL_ADD(nextseed, nextseed, rt->rngAddend); - JSLL_AND(nextseed, nextseed, rt->rngMask); - rt->rngSeed = nextseed; - JSLL_USHR(tmp, nextseed, 48 - bits); - JSLL_L2I(retval, tmp); - return retval; -} - -static jsdouble -random_nextDouble(JSRuntime *rt) -{ - int64 tmp, tmp2; - jsdouble d; - - JSLL_ISHL(tmp, random_next(rt, 26), 27); - JSLL_UI2L(tmp2, random_next(rt, 27)); - JSLL_ADD(tmp, tmp, tmp2); - JSLL_L2D(d, tmp); - return d / rt->rngDscale; -} - -static JSBool -math_random(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSRuntime *rt; - jsdouble z; - - rt = cx->runtime; - JS_LOCK_RUNTIME(rt); - random_init(rt); - z = random_nextDouble(rt); - JS_UNLOCK_RUNTIME(rt); - return js_NewNumberValue(cx, z, rval); -} - -#if defined _WIN32 && !defined WINCE && _MSC_VER < 1400 -/* Try to work around apparent _copysign bustage in VC6 and VC7. */ -double -js_copysign(double x, double y) -{ - jsdpun xu, yu; - - xu.d = x; - yu.d = y; - xu.s.hi &= ~JSDOUBLE_HI32_SIGNBIT; - xu.s.hi |= yu.s.hi & JSDOUBLE_HI32_SIGNBIT; - return xu.d; -} -#endif - -static JSBool -math_round(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_copysign(fd_floor(x + 0.5), x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_sin(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_sin(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_sqrt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_sqrt(x); - return js_NewNumberValue(cx, z, rval); -} - -static JSBool -math_tan(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x, z; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - z = fd_tan(x); - return js_NewNumberValue(cx, z, rval); -} - -#if JS_HAS_TOSOURCE -static JSBool -math_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = ATOM_KEY(CLASS_ATOM(cx, Math)); - return JS_TRUE; -} -#endif - -static JSFunctionSpec math_static_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, math_toSource, 0, 0, 0}, -#endif - {"abs", math_abs, 1, 0, 0}, - {"acos", math_acos, 1, 0, 0}, - {"asin", math_asin, 1, 0, 0}, - {"atan", math_atan, 1, 0, 0}, - {"atan2", math_atan2, 2, 0, 0}, - {"ceil", math_ceil, 1, 0, 0}, - {"cos", math_cos, 1, 0, 0}, - {"exp", math_exp, 1, 0, 0}, - {"floor", math_floor, 1, 0, 0}, - {"log", math_log, 1, 0, 0}, - {"max", math_max, 2, 0, 0}, - {"min", math_min, 2, 0, 0}, - {"pow", math_pow, 2, 0, 0}, - {"random", math_random, 0, 0, 0}, - {"round", math_round, 1, 0, 0}, - {"sin", math_sin, 1, 0, 0}, - {"sqrt", math_sqrt, 1, 0, 0}, - {"tan", math_tan, 1, 0, 0}, - {0,0,0,0,0} -}; - -JSObject * -js_InitMathClass(JSContext *cx, JSObject *obj) -{ - JSObject *Math; - - Math = JS_DefineObject(cx, obj, js_Math_str, &js_MathClass, NULL, 0); - if (!Math) - return NULL; - if (!JS_DefineFunctions(cx, Math, math_static_methods)) - return NULL; - if (!JS_DefineConstDoubles(cx, Math, math_constants)) - return NULL; - return Math; -} diff --git a/spidermonkey/libjs/jsmath.h b/spidermonkey/libjs/jsmath.h deleted file mode 100644 index 1f60630..0000000 --- a/spidermonkey/libjs/jsmath.h +++ /dev/null @@ -1,57 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998-1999 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -*- Mode: C; tab-width: 8 -*- - * Copyright (C) 1998-1999 Netscape Communications Corporation, All Rights Reserved. - */ - -#ifndef jsmath_h___ -#define jsmath_h___ -/* - * JS math functions. - */ - -JS_BEGIN_EXTERN_C - -extern JSClass js_MathClass; - -extern JSObject * -js_InitMathClass(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jsmath_h___ */ diff --git a/spidermonkey/libjs/jsnum.c b/spidermonkey/libjs/jsnum.c deleted file mode 100644 index 987619d..0000000 --- a/spidermonkey/libjs/jsnum.c +++ /dev/null @@ -1,1147 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS number type and wrapper class. - */ -#include "jsstddef.h" -#if defined(XP_WIN) || defined(XP_OS2) -#include -#endif -#include -#include -#include -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdtoa.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsprf.h" -#include "jsstr.h" - -static JSBool -num_isNaN(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_NaN(x)); - return JS_TRUE; -} - -static JSBool -num_isFinite(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble x; - - if (!js_ValueToNumber(cx, argv[0], &x)) - return JS_FALSE; - *rval = BOOLEAN_TO_JSVAL(JSDOUBLE_IS_FINITE(x)); - return JS_TRUE; -} - -static JSBool -num_parseFloat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - const jschar *bp, *ep; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - /* XXXbe js_strtod shouldn't require NUL termination */ - bp = js_UndependString(cx, str); - if (!bp) - return JS_FALSE; - if (!js_strtod(cx, bp, &ep, &d)) - return JS_FALSE; - if (ep == bp) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - return js_NewNumberValue(cx, d, rval); -} - -/* See ECMA 15.1.2.2. */ -static JSBool -num_parseInt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsint radix; - JSString *str; - jsdouble d; - const jschar *bp, *ep; - - if (argc > 1) { - if (!js_ValueToECMAInt32(cx, argv[1], &radix)) - return JS_FALSE; - } else { - radix = 0; - } - if (radix != 0 && (radix < 2 || radix > 36)) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - /* XXXbe js_strtointeger shouldn't require NUL termination */ - bp = js_UndependString(cx, str); - if (!bp) - return JS_FALSE; - if (!js_strtointeger(cx, bp, &ep, radix, &d)) - return JS_FALSE; - if (ep == bp) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - return js_NewNumberValue(cx, d, rval); -} - -const char js_Infinity_str[] = "Infinity"; -const char js_NaN_str[] = "NaN"; -const char js_isNaN_str[] = "isNaN"; -const char js_isFinite_str[] = "isFinite"; -const char js_parseFloat_str[] = "parseFloat"; -const char js_parseInt_str[] = "parseInt"; - -static JSFunctionSpec number_functions[] = { - {js_isNaN_str, num_isNaN, 1,0,0}, - {js_isFinite_str, num_isFinite, 1,0,0}, - {js_parseFloat_str, num_parseFloat, 1,0,0}, - {js_parseInt_str, num_parseInt, 2,0,0}, - {0,0,0,0,0} -}; - -JSClass js_NumberClass = { - js_Number_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Number), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static JSBool -Number(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsdouble d; - jsval v; - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - } else { - d = 0.0; - } - if (!js_NewNumberValue(cx, d, &v)) - return JS_FALSE; - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = v; - return JS_TRUE; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); - return JS_TRUE; -} - -#if JS_HAS_TOSOURCE -static JSBool -num_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - jsdouble d; - char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr; - char buf[64]; - JSString *str; - - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_NUMBER(v)); - } - d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); - numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - JS_snprintf(buf, sizeof buf, "(new %s(%s))", js_NumberClass.name, numStr); - str = JS_NewStringCopyZ(cx, buf); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -/* The buf must be big enough for MIN_INT to fit including '-' and '\0'. */ -static char * -IntToString(jsint i, char *buf, size_t bufSize) -{ - char *cp; - jsuint u; - - u = (i < 0) ? -i : i; - - cp = buf + bufSize; /* one past last buffer cell */ - *--cp = '\0'; /* null terminate the string to be */ - - /* - * Build the string from behind. We use multiply and subtraction - * instead of modulus because that's much faster. - */ - do { - jsuint newu = u / 10; - *--cp = (char)(u - newu * 10) + '0'; - u = newu; - } while (u != 0); - - if (i < 0) - *--cp = '-'; - - return cp; -} - -static JSBool -num_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - jsdouble d; - jsint base; - JSString *str; - - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_NUMBER(v)); - } - d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); - base = 10; - if (argc != 0) { - if (!js_ValueToECMAInt32(cx, argv[0], &base)) - return JS_FALSE; - if (base < 2 || base > 36) { - char numBuf[12]; - char *numStr = IntToString(base, numBuf, sizeof numBuf); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_RADIX, - numStr); - return JS_FALSE; - } - } - if (base == 10) { - str = js_NumberToString(cx, d); - } else { - char *dStr = JS_dtobasestr(base, d); - if (!dStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - str = JS_NewStringCopyZ(cx, dStr); - free(dStr); - } - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -num_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - char thousandsLength, decimalLength; - const char *numGrouping, *tmpGroup; - JSRuntime *rt; - JSString *numStr, *str; - char *num, *buf, *dec, *end, *tmpSrc, *tmpDest; - int digits, size, remainder, nrepeat; - - /* - * Create the string, move back to bytes to make string twiddling - * a bit easier and so we can insert platform charset seperators. - */ - if (!num_toString(cx, obj, 0, argv, rval)) - return JS_FALSE; - JS_ASSERT(JSVAL_IS_STRING(*rval)); - numStr = JSVAL_TO_STRING(*rval); - num = js_GetStringBytes(cx->runtime, numStr); - - /* Find bit before the decimal. */ - dec = strchr(num, '.'); - digits = dec ? dec - num : (int)strlen(num); - end = num + digits; - - rt = cx->runtime; - thousandsLength = strlen(rt->thousandsSeparator); - decimalLength = strlen(rt->decimalSeparator); - - /* Figure out how long resulting string will be. */ - size = digits + (dec ? decimalLength + strlen(dec + 1) : 0); - - numGrouping = tmpGroup = rt->numGrouping; - remainder = digits; - if (*num == '-') - remainder--; - - while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') { - if (*tmpGroup >= remainder) - break; - size += thousandsLength; - remainder -= *tmpGroup; - tmpGroup++; - } - if (*tmpGroup == '\0' && *numGrouping != '\0') { - nrepeat = (remainder - 1) / tmpGroup[-1]; - size += thousandsLength * nrepeat; - remainder -= nrepeat * tmpGroup[-1]; - } else { - nrepeat = 0; - } - tmpGroup--; - - buf = (char *)JS_malloc(cx, size + 1); - if (!buf) - return JS_FALSE; - - tmpDest = buf; - tmpSrc = num; - - while (*tmpSrc == '-' || remainder--) - *tmpDest++ = *tmpSrc++; - while (tmpSrc < end) { - strcpy(tmpDest, rt->thousandsSeparator); - tmpDest += thousandsLength; - memcpy(tmpDest, tmpSrc, *tmpGroup); - tmpDest += *tmpGroup; - tmpSrc += *tmpGroup; - if (--nrepeat < 0) - tmpGroup--; - } - - if (dec) { - strcpy(tmpDest, rt->decimalSeparator); - tmpDest += decimalLength; - strcpy(tmpDest, dec + 1); - } else { - *tmpDest++ = '\0'; - } - - if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) - return cx->localeCallbacks->localeToUnicode(cx, buf, rval); - - str = JS_NewString(cx, buf, size); - if (!str) { - JS_free(cx, buf); - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(str); - - return JS_TRUE; -} - -static JSBool -num_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (JSVAL_IS_NUMBER((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - return JS_TRUE; -} - - -#define MAX_PRECISION 100 - -static JSBool -num_to(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval, JSDToStrMode zeroArgMode, - JSDToStrMode oneArgMode, jsint precisionMin, jsint precisionMax, jsint precisionOffset) -{ - jsval v; - jsdouble d, precision; - JSString *str; - char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)], *numStr; /* Use MAX_PRECISION+1 because precisionOffset can be 1 */ - - if (JSVAL_IS_NUMBER((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_NumberClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_NUMBER(v)); - } - d = JSVAL_IS_INT(v) ? (jsdouble)JSVAL_TO_INT(v) : *JSVAL_TO_DOUBLE(v); - - if (JSVAL_IS_VOID(argv[0])) { - precision = 0.0; - oneArgMode = zeroArgMode; - } else { - if (!js_ValueToNumber(cx, argv[0], &precision)) - return JS_FALSE; - precision = js_DoubleToInteger(precision); - if (precision < precisionMin || precision > precisionMax) { - numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, precision); - if (!numStr) - JS_ReportOutOfMemory(cx); - else - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr); - return JS_FALSE; - } - } - - numStr = JS_dtostr(buf, sizeof buf, oneArgMode, (jsint)precision + precisionOffset, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - str = JS_NewStringCopyZ(cx, numStr); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -num_toFixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ - return num_to(cx, obj, argc, argv, rval, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0); -} - -static JSBool -num_toExponential(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ - return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1); -} - -static JSBool -num_toPrecision(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* We allow a larger range of precision than ECMA requires; this is permitted by ECMA. */ - return num_to(cx, obj, argc, argv, rval, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0); -} - -static JSFunctionSpec number_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, num_toSource, 0,JSFUN_THISP_NUMBER,0}, -#endif - {js_toString_str, num_toString, 0,JSFUN_THISP_NUMBER,0}, - {js_toLocaleString_str, num_toLocaleString, 0,JSFUN_THISP_NUMBER,0}, - {js_valueOf_str, num_valueOf, 0,JSFUN_THISP_NUMBER,0}, - {"toFixed", num_toFixed, 1,JSFUN_THISP_NUMBER,0}, - {"toExponential", num_toExponential, 1,JSFUN_THISP_NUMBER,0}, - {"toPrecision", num_toPrecision, 1,JSFUN_THISP_NUMBER,0}, - {0,0,0,0,0} -}; - -/* NB: Keep this in synch with number_constants[]. */ -enum nc_slot { - NC_NaN, - NC_POSITIVE_INFINITY, - NC_NEGATIVE_INFINITY, - NC_MAX_VALUE, - NC_MIN_VALUE, - NC_LIMIT -}; - -/* - * Some to most C compilers forbid spelling these at compile time, or barf - * if you try, so all but MAX_VALUE are set up by js_InitRuntimeNumberState - * using union jsdpun. - */ -static JSConstDoubleSpec number_constants[] = { - {0, js_NaN_str, 0,{0,0,0}}, - {0, "POSITIVE_INFINITY", 0,{0,0,0}}, - {0, "NEGATIVE_INFINITY", 0,{0,0,0}}, - {1.7976931348623157E+308, "MAX_VALUE", 0,{0,0,0}}, - {0, "MIN_VALUE", 0,{0,0,0}}, - {0,0,0,{0,0,0}} -}; - -static jsdouble NaN; - -#if (defined XP_WIN || defined XP_OS2) && \ - !defined WINCE && \ - !defined __MWERKS__ && \ - (defined _M_IX86 || \ - (defined __GNUC__ && !defined __MINGW32__)) - -/* - * Set the exception mask to mask all exceptions and set the FPU precision - * to 53 bit mantissa. - * On Alpha platform this is handled via Compiler option. - */ -#define FIX_FPU() _control87(MCW_EM | PC_53, MCW_EM | MCW_PC) - -#else - -#define FIX_FPU() ((void)0) - -#endif - -JSBool -js_InitRuntimeNumberState(JSContext *cx) -{ - JSRuntime *rt; - jsdpun u; - struct lconv *locale; - - rt = cx->runtime; - JS_ASSERT(!rt->jsNaN); - - FIX_FPU(); - - u.s.hi = JSDOUBLE_HI32_EXPMASK | JSDOUBLE_HI32_MANTMASK; - u.s.lo = 0xffffffff; - number_constants[NC_NaN].dval = NaN = u.d; - rt->jsNaN = js_NewDouble(cx, NaN, GCF_LOCK); - if (!rt->jsNaN) - return JS_FALSE; - - u.s.hi = JSDOUBLE_HI32_EXPMASK; - u.s.lo = 0x00000000; - number_constants[NC_POSITIVE_INFINITY].dval = u.d; - rt->jsPositiveInfinity = js_NewDouble(cx, u.d, GCF_LOCK); - if (!rt->jsPositiveInfinity) - return JS_FALSE; - - u.s.hi = JSDOUBLE_HI32_SIGNBIT | JSDOUBLE_HI32_EXPMASK; - u.s.lo = 0x00000000; - number_constants[NC_NEGATIVE_INFINITY].dval = u.d; - rt->jsNegativeInfinity = js_NewDouble(cx, u.d, GCF_LOCK); - if (!rt->jsNegativeInfinity) - return JS_FALSE; - - u.s.hi = 0; - u.s.lo = 1; - number_constants[NC_MIN_VALUE].dval = u.d; - - locale = localeconv(); - rt->thousandsSeparator = - JS_strdup(cx, locale->thousands_sep ? locale->thousands_sep : "'"); - rt->decimalSeparator = - JS_strdup(cx, locale->decimal_point ? locale->decimal_point : "."); - rt->numGrouping = - JS_strdup(cx, locale->grouping ? locale->grouping : "\3\0"); - - return rt->thousandsSeparator && rt->decimalSeparator && rt->numGrouping; -} - -void -js_FinishRuntimeNumberState(JSContext *cx) -{ - JSRuntime *rt = cx->runtime; - - js_UnlockGCThingRT(rt, rt->jsNaN); - js_UnlockGCThingRT(rt, rt->jsNegativeInfinity); - js_UnlockGCThingRT(rt, rt->jsPositiveInfinity); - - rt->jsNaN = NULL; - rt->jsNegativeInfinity = NULL; - rt->jsPositiveInfinity = NULL; - - JS_free(cx, (void *)rt->thousandsSeparator); - JS_free(cx, (void *)rt->decimalSeparator); - JS_free(cx, (void *)rt->numGrouping); - rt->thousandsSeparator = rt->decimalSeparator = rt->numGrouping = NULL; -} - -JSObject * -js_InitNumberClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - JSRuntime *rt; - - /* XXX must do at least once per new thread, so do it per JSContext... */ - FIX_FPU(); - - if (!JS_DefineFunctions(cx, obj, number_functions)) - return NULL; - - proto = JS_InitClass(cx, obj, NULL, &js_NumberClass, Number, 1, - NULL, number_methods, NULL, NULL); - if (!proto || !(ctor = JS_GetConstructor(cx, proto))) - return NULL; - OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, JSVAL_ZERO); - if (!JS_DefineConstDoubles(cx, ctor, number_constants)) - return NULL; - - /* ECMA 15.1.1.1 */ - rt = cx->runtime; - if (!JS_DefineProperty(cx, obj, js_NaN_str, DOUBLE_TO_JSVAL(rt->jsNaN), - NULL, NULL, JSPROP_PERMANENT)) { - return NULL; - } - - /* ECMA 15.1.1.2 */ - if (!JS_DefineProperty(cx, obj, js_Infinity_str, - DOUBLE_TO_JSVAL(rt->jsPositiveInfinity), - NULL, NULL, JSPROP_PERMANENT)) { - return NULL; - } - return proto; -} - -jsdouble * -js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag) -{ - jsdouble *dp; - - dp = (jsdouble *) js_NewGCThing(cx, gcflag | GCX_DOUBLE, sizeof(jsdouble)); - if (!dp) - return NULL; - *dp = d; - return dp; -} - -void -js_FinalizeDouble(JSContext *cx, jsdouble *dp) -{ - *dp = NaN; -} - -JSBool -js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval) -{ - jsdouble *dp; - - dp = js_NewDouble(cx, d, 0); - if (!dp) - return JS_FALSE; - *rval = DOUBLE_TO_JSVAL(dp); - return JS_TRUE; -} - -JSBool -js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval) -{ - jsint i; - - if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) { - *rval = INT_TO_JSVAL(i); - } else { - if (!js_NewDoubleValue(cx, d, rval)) - return JS_FALSE; - } - return JS_TRUE; -} - -JSObject * -js_NumberToObject(JSContext *cx, jsdouble d) -{ - JSObject *obj; - jsval v; - - obj = js_NewObject(cx, &js_NumberClass, NULL, NULL); - if (!obj) - return NULL; - if (!js_NewNumberValue(cx, d, &v)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, v); - return obj; -} - -JSString * -js_NumberToString(JSContext *cx, jsdouble d) -{ - jsint i; - char buf[DTOSTR_STANDARD_BUFFER_SIZE]; - char *numStr; - - if (JSDOUBLE_IS_INT(d, i)) { - numStr = IntToString(i, buf, sizeof buf); - } else { - numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - return JS_NewStringCopyZ(cx, numStr); -} - -JSBool -js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp) -{ - JSObject *obj; - JSString *str; - const jschar *bp, *ep; - - if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!obj) { - *dp = 0; - return JS_TRUE; - } - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_NUMBER, &v)) - return JS_FALSE; - } - if (JSVAL_IS_INT(v)) { - *dp = (jsdouble)JSVAL_TO_INT(v); - } else if (JSVAL_IS_DOUBLE(v)) { - *dp = *JSVAL_TO_DOUBLE(v); - } else if (JSVAL_IS_STRING(v)) { - str = JSVAL_TO_STRING(v); - /* - * Note that ECMA doesn't treat a string beginning with a '0' as an - * octal number here. This works because all such numbers will be - * interpreted as decimal by js_strtod and will never get passed to - * js_strtointeger (which would interpret them as octal). - */ - /* XXXbe js_strtod shouldn't require NUL termination */ - bp = js_UndependString(cx, str); - if (!bp) - return JS_FALSE; - if ((!js_strtod(cx, bp, &ep, dp) || - js_SkipWhiteSpace(ep) != bp + str->length) && - (!js_strtointeger(cx, bp, &ep, 0, dp) || - js_SkipWhiteSpace(ep) != bp + str->length)) { - goto badstr; - } - } else if (JSVAL_IS_BOOLEAN(v)) { - *dp = JSVAL_TO_BOOLEAN(v) ? 1 : 0; - } else { -badstr: - *dp = *cx->runtime->jsNaN; - } - return JS_TRUE; -} - -JSBool -js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip) -{ - jsdouble d; - - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - return js_DoubleToECMAInt32(cx, d, ip); -} - -JSBool -js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip) -{ - jsdouble two32 = 4294967296.0; - jsdouble two31 = 2147483648.0; - - if (!JSDOUBLE_IS_FINITE(d) || d == 0) { - *ip = 0; - return JS_TRUE; - } - d = fmod(d, two32); - d = (d >= 0) ? floor(d) : ceil(d) + two32; - if (d >= two31) - *ip = (int32)(d - two32); - else - *ip = (int32)d; - return JS_TRUE; -} - -JSBool -js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip) -{ - jsdouble d; - - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - return js_DoubleToECMAUint32(cx, d, ip); -} - -JSBool -js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip) -{ - JSBool neg; - jsdouble two32 = 4294967296.0; - - if (!JSDOUBLE_IS_FINITE(d) || d == 0) { - *ip = 0; - return JS_TRUE; - } - - neg = (d < 0); - d = floor(neg ? -d : d); - d = neg ? -d : d; - - d = fmod(d, two32); - - d = (d >= 0) ? d : d + two32; - *ip = (uint32)d; - return JS_TRUE; -} - -JSBool -js_ValueToInt32(JSContext *cx, jsval v, int32 *ip) -{ - jsdouble d; - JSString *str; - - if (JSVAL_IS_INT(v)) { - *ip = JSVAL_TO_INT(v); - return JS_TRUE; - } - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(d) || d <= -2147483649.0 || 2147483648.0 <= d) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_CONVERT, JS_GetStringBytes(str)); - - } - return JS_FALSE; - } - *ip = (int32)floor(d + 0.5); /* Round to nearest */ - return JS_TRUE; -} - -JSBool -js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip) -{ - jsdouble d; - jsuint i, m; - JSBool neg; - - if (!js_ValueToNumber(cx, v, &d)) - return JS_FALSE; - if (d == 0 || !JSDOUBLE_IS_FINITE(d)) { - *ip = 0; - return JS_TRUE; - } - i = (jsuint)d; - if ((jsdouble)i == d) { - *ip = (uint16)i; - return JS_TRUE; - } - neg = (d < 0); - d = floor(neg ? -d : d); - d = neg ? -d : d; - m = JS_BIT(16); - d = fmod(d, (double)m); - if (d < 0) - d += m; - *ip = (uint16) d; - return JS_TRUE; -} - -jsdouble -js_DoubleToInteger(jsdouble d) -{ - JSBool neg; - - if (d == 0) - return d; - if (!JSDOUBLE_IS_FINITE(d)) { - if (JSDOUBLE_IS_NaN(d)) - return 0; - return d; - } - neg = (d < 0); - d = floor(neg ? -d : d); - return neg ? -d : d; -} - - -JSBool -js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp) -{ - char cbuf[32]; - size_t i; - char *cstr, *istr, *estr; - JSBool negative; - jsdouble d; - const jschar *s1 = js_SkipWhiteSpace(s); - size_t length = js_strlen(s1); - - /* Use cbuf to avoid malloc */ - if (length >= sizeof cbuf) { - cstr = (char *) JS_malloc(cx, length + 1); - if (!cstr) - return JS_FALSE; - } else { - cstr = cbuf; - } - - for (i = 0; i <= length; i++) { - if (s1[i] >> 8) { - cstr[i] = 0; - break; - } - cstr[i] = (char)s1[i]; - } - - istr = cstr; - if ((negative = (*istr == '-')) != 0 || *istr == '+') - istr++; - if (!strncmp(istr, js_Infinity_str, sizeof js_Infinity_str - 1)) { - d = *(negative ? cx->runtime->jsNegativeInfinity : cx->runtime->jsPositiveInfinity); - estr = istr + 8; - } else { - int err; - d = JS_strtod(cstr, &estr, &err); - if (err == JS_DTOA_ENOMEM) { - JS_ReportOutOfMemory(cx); - if (cstr != cbuf) - JS_free(cx, cstr); - return JS_FALSE; - } - if (err == JS_DTOA_ERANGE) { - if (d == HUGE_VAL) - d = *cx->runtime->jsPositiveInfinity; - else if (d == -HUGE_VAL) - d = *cx->runtime->jsNegativeInfinity; - } -#ifdef HPUX - if (d == 0.0 && negative) { - /* - * "-0", "-1e-2000" come out as positive zero - * here on HPUX. Force a negative zero instead. - */ - JSDOUBLE_HI32(d) = JSDOUBLE_HI32_SIGNBIT; - JSDOUBLE_LO32(d) = 0; - } -#endif - } - - i = estr - cstr; - if (cstr != cbuf) - JS_free(cx, cstr); - *ep = i ? s1 + i : s; - *dp = d; - return JS_TRUE; -} - -struct BinaryDigitReader -{ - uintN base; /* Base of number; must be a power of 2 */ - uintN digit; /* Current digit value in radix given by base */ - uintN digitMask; /* Mask to extract the next bit from digit */ - const jschar *digits; /* Pointer to the remaining digits */ - const jschar *end; /* Pointer to first non-digit */ -}; - -/* Return the next binary digit from the number or -1 if done */ -static intN GetNextBinaryDigit(struct BinaryDigitReader *bdr) -{ - intN bit; - - if (bdr->digitMask == 0) { - uintN c; - - if (bdr->digits == bdr->end) - return -1; - - c = *bdr->digits++; - if ('0' <= c && c <= '9') - bdr->digit = c - '0'; - else if ('a' <= c && c <= 'z') - bdr->digit = c - 'a' + 10; - else bdr->digit = c - 'A' + 10; - bdr->digitMask = bdr->base >> 1; - } - bit = (bdr->digit & bdr->digitMask) != 0; - bdr->digitMask >>= 1; - return bit; -} - -JSBool -js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint base, jsdouble *dp) -{ - JSBool negative; - jsdouble value; - const jschar *start; - const jschar *s1 = js_SkipWhiteSpace(s); - - if ((negative = (*s1 == '-')) != 0 || *s1 == '+') - s1++; - - if (base == 0) { - /* No base supplied, or some base that evaluated to 0. */ - if (*s1 == '0') { - /* It's either hex or octal; only increment char if str isn't '0' */ - if (s1[1] == 'X' || s1[1] == 'x') { /* Hex */ - s1 += 2; - base = 16; - } else { /* Octal */ - base = 8; - } - } else { - base = 10; /* Default to decimal. */ - } - } else if (base == 16 && *s1 == '0' && (s1[1] == 'X' || s1[1] == 'x')) { - /* If base is 16, ignore hex prefix. */ - s1 += 2; - } - - /* - * Done with the preliminaries; find some prefix of the string that's - * a number in the given base. - */ - start = s1; /* Mark - if string is empty, we return NaN. */ - value = 0.0; - for (;;) { - uintN digit; - jschar c = *s1; - if ('0' <= c && c <= '9') - digit = c - '0'; - else if ('a' <= c && c <= 'z') - digit = c - 'a' + 10; - else if ('A' <= c && c <= 'Z') - digit = c - 'A' + 10; - else - break; - if (digit >= (uintN)base) - break; - value = value * base + digit; - s1++; - } - - if (value >= 9007199254740992.0) { - if (base == 10) { - /* - * If we're accumulating a decimal number and the number is >= - * 2^53, then the result from the repeated multiply-add above may - * be inaccurate. Call JS_strtod to get the correct answer. - */ - size_t i; - size_t length = s1 - start; - char *cstr = (char *) JS_malloc(cx, length + 1); - char *estr; - int err=0; - - if (!cstr) - return JS_FALSE; - for (i = 0; i != length; i++) - cstr[i] = (char)start[i]; - cstr[length] = 0; - - value = JS_strtod(cstr, &estr, &err); - if (err == JS_DTOA_ENOMEM) { - JS_ReportOutOfMemory(cx); - JS_free(cx, cstr); - return JS_FALSE; - } - if (err == JS_DTOA_ERANGE && value == HUGE_VAL) - value = *cx->runtime->jsPositiveInfinity; - JS_free(cx, cstr); - } else if ((base & (base - 1)) == 0) { - /* - * The number may also be inaccurate for power-of-two bases. This - * happens if the addition in value * base + digit causes a round- - * down to an even least significant mantissa bit when the first - * dropped bit is a one. If any of the following digits in the - * number (which haven't been added in yet) are nonzero, then the - * correct action would have been to round up instead of down. An - * example occurs when reading the number 0x1000000000000081, which - * rounds to 0x1000000000000000 instead of 0x1000000000000100. - */ - struct BinaryDigitReader bdr; - intN bit, bit2; - intN j; - - bdr.base = base; - bdr.digitMask = 0; - bdr.digits = start; - bdr.end = s1; - value = 0.0; - - /* Skip leading zeros. */ - do { - bit = GetNextBinaryDigit(&bdr); - } while (bit == 0); - - if (bit == 1) { - /* Gather the 53 significant bits (including the leading 1) */ - value = 1.0; - for (j = 52; j; j--) { - bit = GetNextBinaryDigit(&bdr); - if (bit < 0) - goto done; - value = value*2 + bit; - } - /* bit2 is the 54th bit (the first dropped from the mantissa) */ - bit2 = GetNextBinaryDigit(&bdr); - if (bit2 >= 0) { - jsdouble factor = 2.0; - intN sticky = 0; /* sticky is 1 if any bit beyond the 54th is 1 */ - intN bit3; - - while ((bit3 = GetNextBinaryDigit(&bdr)) >= 0) { - sticky |= bit3; - factor *= 2; - } - value += bit2 & (bit | sticky); - value *= factor; - } - done:; - } - } - } - /* We don't worry about inaccurate numbers for any other base. */ - - if (s1 == start) { - *dp = 0.0; - *ep = s; - } else { - *dp = negative ? -value : value; - *ep = s1; - } - return JS_TRUE; -} diff --git a/spidermonkey/libjs/jsnum.h b/spidermonkey/libjs/jsnum.h deleted file mode 100644 index cd99501..0000000 --- a/spidermonkey/libjs/jsnum.h +++ /dev/null @@ -1,268 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsnum_h___ -#define jsnum_h___ -/* - * JS number (IEEE double) interface. - * - * JS numbers are optimistically stored in the top 31 bits of 32-bit integers, - * but floating point literals, results that overflow 31 bits, and division and - * modulus operands and results require a 64-bit IEEE double. These are GC'ed - * and pointed to by 32-bit jsvals on the stack and in object properties. - * - * When a JS number is treated as an object (followed by . or []), the runtime - * wraps it with a JSObject whose valueOf method returns the unwrapped number. - */ - -JS_BEGIN_EXTERN_C - -/* - * Stefan Hanske reports: - * ARM is a little endian architecture but 64 bit double words are stored - * differently: the 32 bit words are in little endian byte order, the two words - * are stored in big endian`s way. - */ - -#if defined(__arm) || defined(__arm32__) || defined(__arm26__) || defined(__arm__) -#define CPU_IS_ARM -#endif - -typedef union jsdpun { - struct { -#if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) - uint32 lo, hi; -#else - uint32 hi, lo; -#endif - } s; - jsdouble d; -} jsdpun; - -#if (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || __GNUC__ > 2 -/* - * This version of the macros is safe for the alias optimizations that gcc - * does, but uses gcc-specific extensions. - */ - -#define JSDOUBLE_HI32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.hi; })) -#define JSDOUBLE_LO32(x) (__extension__ ({ jsdpun u; u.d = (x); u.s.lo; })) -#define JSDOUBLE_SET_HI32(x, y) \ - (__extension__ ({ jsdpun u; u.d = (x); u.s.hi = (y); (x) = u.d; })) -#define JSDOUBLE_SET_LO32(x, y) \ - (__extension__ ({ jsdpun u; u.d = (x); u.s.lo = (y); (x) = u.d; })) - -#else /* not or old GNUC */ - -/* - * We don't know of any non-gcc compilers that perform alias optimization, - * so this code should work. - */ - -#if defined(IS_LITTLE_ENDIAN) && !defined(CPU_IS_ARM) -#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1]) -#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0]) -#else -#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0]) -#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1]) -#endif - -#define JSDOUBLE_SET_HI32(x, y) (JSDOUBLE_HI32(x)=(y)) -#define JSDOUBLE_SET_LO32(x, y) (JSDOUBLE_LO32(x)=(y)) - -#endif /* not or old GNUC */ - -#define JSDOUBLE_HI32_SIGNBIT 0x80000000 -#define JSDOUBLE_HI32_EXPMASK 0x7ff00000 -#define JSDOUBLE_HI32_MANTMASK 0x000fffff - -#define JSDOUBLE_IS_NaN(x) \ - ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \ - (JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK))) - -#define JSDOUBLE_IS_INFINITE(x) \ - ((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \ - !JSDOUBLE_LO32(x)) - -#define JSDOUBLE_IS_FINITE(x) \ - ((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) != JSDOUBLE_HI32_EXPMASK) - -#define JSDOUBLE_IS_NEGZERO(d) (JSDOUBLE_HI32(d) == JSDOUBLE_HI32_SIGNBIT && \ - JSDOUBLE_LO32(d) == 0) - -/* - * JSDOUBLE_IS_INT first checks that d is neither NaN nor infinite, to avoid - * raising SIGFPE on platforms such as Alpha Linux, then (only if the cast is - * safe) leaves i as (jsint)d. This also avoid anomalous NaN floating point - * comparisons under MSVC. - */ -#define JSDOUBLE_IS_INT(d, i) (JSDOUBLE_IS_FINITE(d) \ - && !JSDOUBLE_IS_NEGZERO(d) \ - && ((d) == (i = (jsint)(d)))) - -#if defined(XP_WIN) -#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) \ - ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL)) \ - ? (IFNAN) \ - : (LVAL) OP (RVAL)) -#else -#define JSDOUBLE_COMPARE(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL)) -#endif - -/* Initialize number constants and runtime state for the first context. */ -extern JSBool -js_InitRuntimeNumberState(JSContext *cx); - -extern void -js_FinishRuntimeNumberState(JSContext *cx); - -/* Initialize the Number class, returning its prototype object. */ -extern JSClass js_NumberClass; - -extern JSObject * -js_InitNumberClass(JSContext *cx, JSObject *obj); - -/* - * String constants for global function names, used in jsapi.c and jsnum.c. - */ -extern const char js_Infinity_str[]; -extern const char js_NaN_str[]; -extern const char js_isNaN_str[]; -extern const char js_isFinite_str[]; -extern const char js_parseFloat_str[]; -extern const char js_parseInt_str[]; - -/* GC-allocate a new JS number. */ -extern jsdouble * -js_NewDouble(JSContext *cx, jsdouble d, uintN gcflag); - -extern void -js_FinalizeDouble(JSContext *cx, jsdouble *dp); - -extern JSBool -js_NewDoubleValue(JSContext *cx, jsdouble d, jsval *rval); - -extern JSBool -js_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval); - -/* Construct a Number instance that wraps around d. */ -extern JSObject * -js_NumberToObject(JSContext *cx, jsdouble d); - -/* Convert a number to a GC'ed string. */ -extern JSString * -js_NumberToString(JSContext *cx, jsdouble d); - -/* - * Convert a value to a number, returning false after reporting any error, - * otherwise returning true with *dp set. - */ -extern JSBool -js_ValueToNumber(JSContext *cx, jsval v, jsdouble *dp); - -/* - * Convert a value or a double to an int32, according to the ECMA rules - * for ToInt32. - */ -extern JSBool -js_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip); - -extern JSBool -js_DoubleToECMAInt32(JSContext *cx, jsdouble d, int32 *ip); - -/* - * Convert a value or a double to a uint32, according to the ECMA rules - * for ToUint32. - */ -extern JSBool -js_ValueToECMAUint32(JSContext *cx, jsval v, uint32 *ip); - -extern JSBool -js_DoubleToECMAUint32(JSContext *cx, jsdouble d, uint32 *ip); - -/* - * Convert a value to a number, then to an int32 if it fits by rounding to - * nearest; but failing with an error report if the double is out of range - * or unordered. - */ -extern JSBool -js_ValueToInt32(JSContext *cx, jsval v, int32 *ip); - -/* - * Convert a value to a number, then to a uint16 according to the ECMA rules - * for ToUint16. - */ -extern JSBool -js_ValueToUint16(JSContext *cx, jsval v, uint16 *ip); - -/* - * Convert a jsdouble to an integral number, stored in a jsdouble. - * If d is NaN, return 0. If d is an infinity, return it without conversion. - */ -extern jsdouble -js_DoubleToInteger(jsdouble d); - -/* - * Similar to strtod except that it replaces overflows with infinities of the - * correct sign, and underflows with zeros of the correct sign. Guaranteed to - * return the closest double number to the given input in dp. - * - * Also allows inputs of the form [+|-]Infinity, which produce an infinity of - * the appropriate sign. The case of the "Infinity" string must match exactly. - * If the string does not contain a number, set *ep to s and return 0.0 in dp. - * Return false if out of memory. - */ -extern JSBool -js_strtod(JSContext *cx, const jschar *s, const jschar **ep, jsdouble *dp); - -/* - * Similar to strtol except that it handles integers of arbitrary size. - * Guaranteed to return the closest double number to the given input when radix - * is 10 or a power of 2. Callers may see round-off errors for very large - * numbers of a different radix than 10 or a power of 2. - * - * If the string does not contain a number, set *ep to s and return 0.0 in dp. - * Return false if out of memory. - */ -extern JSBool -js_strtointeger(JSContext *cx, const jschar *s, const jschar **ep, jsint radix, jsdouble *dp); - -JS_END_EXTERN_C - -#endif /* jsnum_h___ */ diff --git a/spidermonkey/libjs/jsobj.c b/spidermonkey/libjs/jsobj.c deleted file mode 100644 index b552aca..0000000 --- a/spidermonkey/libjs/jsobj.c +++ /dev/null @@ -1,5035 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS object implementation. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsbit.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsopcode.h" - -#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ - -#if JS_HAS_GENERATORS -#include "jsiter.h" -#endif - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif - -#ifdef JS_THREADSAFE -#define NATIVE_DROP_PROPERTY js_DropProperty - -extern void -js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); -#else -#define NATIVE_DROP_PROPERTY NULL -#endif - -JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, - js_LookupProperty, js_DefineProperty, - js_GetProperty, js_SetProperty, - js_GetAttributes, js_SetAttributes, - js_DeleteProperty, js_DefaultValue, - js_Enumerate, js_CheckAccess, - NULL, NATIVE_DROP_PROPERTY, - js_Call, js_Construct, - NULL, js_HasInstance, - js_SetProtoOrParent, js_SetProtoOrParent, - js_Mark, js_Clear, - js_GetRequiredSlot, js_SetRequiredSlot -}; - -JSClass js_ObjectClass = { - js_Object_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Object), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#if JS_HAS_OBJ_PROTO_PROP - -static JSBool -obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSBool -obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSBool -obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -static JSPropertySpec object_props[] = { - /* These two must come first; see object_props[slot].name usage below. */ - {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED, - obj_getSlot, obj_setSlot}, - {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, - obj_getSlot, obj_setSlot}, - {js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount}, - {0,0,0,0,0} -}; - -/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */ -#define JSSLOT_COUNT 2 - -static JSBool -ReportStrictSlot(JSContext *cx, uint32 slot) -{ - if (slot == JSSLOT_PROTO) - return JS_TRUE; - return JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_DEPRECATED_USAGE, - object_props[slot].name); -} - -static JSBool -obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - uint32 slot; - jsid propid; - JSAccessMode mode; - uintN attrs; - JSObject *pobj; - JSClass *clasp; - JSExtendedClass *xclasp; - - slot = (uint32) JSVAL_TO_INT(id); - if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { - propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); - mode = JSACC_PROTO; - } else { - propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); - mode = JSACC_PARENT; - } - - /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ - if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) - return JS_FALSE; - - pobj = JSVAL_TO_OBJECT(*vp); - if (pobj) { - clasp = OBJ_GET_CLASS(cx, pobj); - if (clasp == &js_CallClass || clasp == &js_BlockClass) { - /* Censor activations and lexical scopes per ECMA-262. */ - *vp = JSVAL_NULL; - } else if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass *) clasp; - if (xclasp->outerObject) { - pobj = xclasp->outerObject(cx, pobj); - if (!pobj) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(pobj); - } - } - } - return JS_TRUE; -} - -static JSBool -obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSObject *pobj; - uint32 slot; - jsid propid; - uintN attrs; - - if (!JSVAL_IS_OBJECT(*vp)) - return JS_TRUE; - pobj = JSVAL_TO_OBJECT(*vp); - - if (pobj) { - /* - * Innerize pobj here to avoid sticking unwanted properties on the - * outer object. This ensures that any with statements only grant - * access to the inner object. - */ - OBJ_TO_INNER_OBJECT(cx, pobj); - if (!pobj) - return JS_FALSE; - } - slot = (uint32) JSVAL_TO_INT(id); - if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) - return JS_FALSE; - - /* __parent__ is readonly and permanent, only __proto__ may be set. */ - propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); - if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs)) - return JS_FALSE; - - return js_SetProtoOrParent(cx, obj, slot, pobj); -} - -static JSBool -obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsval iter_state; - jsid num_properties; - JSBool ok; - - if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) - return JS_FALSE; - - /* Get the number of properties to enumerate. */ - iter_state = JSVAL_NULL; - ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); - if (!ok) - goto out; - - if (!JSVAL_IS_INT(num_properties)) { - JS_ASSERT(0); - *vp = JSVAL_ZERO; - goto out; - } - *vp = num_properties; - -out: - if (iter_state != JSVAL_NULL) - ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); - return ok; -} - -#else /* !JS_HAS_OBJ_PROTO_PROP */ - -#define object_props NULL - -#endif /* !JS_HAS_OBJ_PROTO_PROP */ - -JSBool -js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj) -{ - JSRuntime *rt; - JSObject *obj2, *oldproto; - JSScope *scope, *newscope; - - /* - * Serialize all proto and parent setting in order to detect cycles. - * We nest locks in this function, and only here, in the following orders: - * - * (1) rt->setSlotLock < pobj's scope lock; - * rt->setSlotLock < pobj's proto-or-parent's scope lock; - * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock; - * etc... - * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock. - * - * We avoid AB-BA deadlock by restricting obj from being on pobj's parent - * or proto chain (pobj may already be on obj's parent or proto chain; it - * could be moving up or down). We finally order obj with respect to pobj - * at the bottom of this routine (just before releasing rt->setSlotLock), - * by making pobj be obj's prototype or parent. - * - * After we have set the slot and released rt->setSlotLock, another call - * to js_SetProtoOrParent could nest locks according to the first order - * list above, but it cannot deadlock with any other thread. For there - * to be a deadlock, other parts of the engine would have to nest scope - * locks in the opposite order. XXXbe ensure they don't! - */ - rt = cx->runtime; -#ifdef JS_THREADSAFE - - JS_ACQUIRE_LOCK(rt->setSlotLock); - while (rt->setSlotBusy) { - jsrefcount saveDepth; - - /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */ - JS_RELEASE_LOCK(rt->setSlotLock); - saveDepth = JS_SuspendRequest(cx); - JS_ACQUIRE_LOCK(rt->setSlotLock); - if (rt->setSlotBusy) - JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT); - JS_RELEASE_LOCK(rt->setSlotLock); - JS_ResumeRequest(cx, saveDepth); - JS_ACQUIRE_LOCK(rt->setSlotLock); - } - rt->setSlotBusy = JS_TRUE; - JS_RELEASE_LOCK(rt->setSlotLock); - -#define SET_SLOT_DONE(rt) \ - JS_BEGIN_MACRO \ - JS_ACQUIRE_LOCK((rt)->setSlotLock); \ - (rt)->setSlotBusy = JS_FALSE; \ - JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \ - JS_RELEASE_LOCK((rt)->setSlotLock); \ - JS_END_MACRO - -#else - -#define SET_SLOT_DONE(rt) /* nothing */ - -#endif - - obj2 = pobj; - while (obj2) { - if (obj2 == obj) { - SET_SLOT_DONE(rt); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CYCLIC_VALUE, -#if JS_HAS_OBJ_PROTO_PROP - object_props[slot].name -#else - (slot == JSSLOT_PROTO) ? js_proto_str - : js_parent_str -#endif - ); - return JS_FALSE; - } - obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot)); - } - - if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) { - /* Check to see whether obj shares its prototype's scope. */ - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)); - if (oldproto && OBJ_SCOPE(oldproto) == scope) { - /* Either obj needs a new empty scope, or it should share pobj's. */ - if (!pobj || - !OBJ_IS_NATIVE(pobj) || - OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) { - /* - * With no proto and no scope of its own, obj is truly empty. - * - * If pobj is not native, obj needs its own empty scope -- it - * should not continue to share oldproto's scope once oldproto - * is not on obj's prototype chain. That would put properties - * from oldproto's scope ahead of properties defined by pobj, - * in lookup order. - * - * If pobj's class differs from oldproto's, we may need a new - * scope to handle differences in private and reserved slots, - * so we suboptimally but safely make one. - */ - scope = js_GetMutableScope(cx, obj); - if (!scope) { - JS_UNLOCK_OBJ(cx, obj); - SET_SLOT_DONE(rt); - return JS_FALSE; - } - } else if (OBJ_SCOPE(pobj) != scope) { -#ifdef JS_THREADSAFE - /* - * We are about to nest scope locks. Help jslock.c:ShareScope - * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while - * avoiding deadlock, by recording scope in rt->setSlotScope. - */ - if (scope->ownercx) { - JS_ASSERT(scope->ownercx == cx); - rt->setSlotScope = scope; - } -#endif - - /* We can't deadlock because we checked for cycles above (2). */ - JS_LOCK_OBJ(cx, pobj); - newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map); - obj->map = &newscope->map; - js_DropObjectMap(cx, &scope->map, obj); - JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); - scope = newscope; -#ifdef JS_THREADSAFE - rt->setSlotScope = NULL; -#endif - } - } - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj)); - JS_UNLOCK_SCOPE(cx, scope); - } else { - OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj)); - } - - SET_SLOT_DONE(rt); - return JS_TRUE; - -#undef SET_SLOT_DONE -} - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_object(const void *key) -{ - return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; -} - -static JSHashEntry * -MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap) -{ - JSSharpObjectMap *map; - JSHashTable *table; - JSHashNumber hash; - JSHashEntry **hep, *he; - jsatomid sharpid; - JSIdArray *ida; - JSBool ok; - jsint i, length; - jsid id; -#if JS_HAS_GETTER_SETTER - JSObject *obj2; - JSProperty *prop; - uintN attrs; -#endif - jsval val; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return NULL; - } - - map = &cx->sharpObjectMap; - table = map->table; - hash = js_hash_object(obj); - hep = JS_HashTableRawLookup(table, hash, obj); - he = *hep; - if (!he) { - sharpid = 0; - he = JS_HashTableRawAdd(table, hep, hash, obj, - JS_UINT32_TO_PTR(sharpid)); - if (!he) { - JS_ReportOutOfMemory(cx); - return NULL; - } - - /* - * Increment map->depth to protect js_EnterSharpObject from reentering - * itself badly. Without this fix, if we reenter the basis case where - * map->depth == 0, when unwinding the inner call we will destroy the - * newly-created hash table and crash. - */ - ++map->depth; - ida = JS_Enumerate(cx, obj); - --map->depth; - if (!ida) - return NULL; - - ok = JS_TRUE; - for (i = 0, length = ida->length; i < length; i++) { - id = ida->vector[i]; -#if JS_HAS_GETTER_SETTER - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - break; - if (!prop) - continue; - ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); - if (ok) { - if (OBJ_IS_NATIVE(obj2) && - (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - val = JSVAL_NULL; - if (attrs & JSPROP_GETTER) - val = (jsval) ((JSScopeProperty*)prop)->getter; - if (attrs & JSPROP_SETTER) { - if (val != JSVAL_NULL) { - /* Mark the getter, then set val to setter. */ - ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), - NULL) - != NULL); - } - val = (jsval) ((JSScopeProperty*)prop)->setter; - } - } else { - ok = OBJ_GET_PROPERTY(cx, obj, id, &val); - } - } - OBJ_DROP_PROPERTY(cx, obj2, prop); -#else - ok = OBJ_GET_PROPERTY(cx, obj, id, &val); -#endif - if (!ok) - break; - if (!JSVAL_IS_PRIMITIVE(val) && - !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) { - ok = JS_FALSE; - break; - } - } - if (!ok || !idap) - JS_DestroyIdArray(cx, ida); - if (!ok) - return NULL; - } else { - sharpid = JS_PTR_TO_UINT32(he->value); - if (sharpid == 0) { - sharpid = ++map->sharpgen << SHARP_ID_SHIFT; - he->value = JS_UINT32_TO_PTR(sharpid); - } - ida = NULL; - } - if (idap) - *idap = ida; - return he; -} - -JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp) -{ - JSSharpObjectMap *map; - JSHashTable *table; - JSIdArray *ida; - JSHashNumber hash; - JSHashEntry *he, **hep; - jsatomid sharpid; - char buf[20]; - size_t len; - - if (JS_HAS_NATIVE_BRANCH_CALLBACK_OPTION(cx) && - cx->branchCallback && - !cx->branchCallback(cx, NULL)) { - return NULL; - } - - /* Set to null in case we return an early error. */ - *sp = NULL; - map = &cx->sharpObjectMap; - table = map->table; - if (!table) { - table = JS_NewHashTable(8, js_hash_object, JS_CompareValues, - JS_CompareValues, NULL, NULL); - if (!table) { - JS_ReportOutOfMemory(cx); - return NULL; - } - map->table = table; - JS_KEEP_ATOMS(cx->runtime); - } - - /* From this point the control must flow either through out: or bad:. */ - ida = NULL; - if (map->depth == 0) { - he = MarkSharpObjects(cx, obj, &ida); - if (!he) - goto bad; - JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); - if (!idap) { - JS_DestroyIdArray(cx, ida); - ida = NULL; - } - } else { - hash = js_hash_object(obj); - hep = JS_HashTableRawLookup(table, hash, obj); - he = *hep; - - /* - * It's possible that the value of a property has changed from the - * first time the object's properties are traversed (when the property - * ids are entered into the hash table) to the second (when they are - * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not - * idempotent. - */ - if (!he) { - he = JS_HashTableRawAdd(table, hep, hash, obj, NULL); - if (!he) { - JS_ReportOutOfMemory(cx); - goto bad; - } - sharpid = 0; - goto out; - } - } - - sharpid = JS_PTR_TO_UINT32(he->value); - if (sharpid != 0) { - len = JS_snprintf(buf, sizeof buf, "#%u%c", - sharpid >> SHARP_ID_SHIFT, - (sharpid & SHARP_BIT) ? '#' : '='); - *sp = js_InflateString(cx, buf, &len); - if (!*sp) { - if (ida) - JS_DestroyIdArray(cx, ida); - goto bad; - } - } - -out: - JS_ASSERT(he); - if ((sharpid & SHARP_BIT) == 0) { - if (idap && !ida) { - ida = JS_Enumerate(cx, obj); - if (!ida) { - if (*sp) { - JS_free(cx, *sp); - *sp = NULL; - } - goto bad; - } - } - map->depth++; - } - - if (idap) - *idap = ida; - return he; - -bad: - /* Clean up the sharpObjectMap table on outermost error. */ - if (map->depth == 0) { - JS_UNKEEP_ATOMS(cx->runtime); - map->sharpgen = 0; - JS_HashTableDestroy(map->table); - map->table = NULL; - } - return NULL; -} - -void -js_LeaveSharpObject(JSContext *cx, JSIdArray **idap) -{ - JSSharpObjectMap *map; - JSIdArray *ida; - - map = &cx->sharpObjectMap; - JS_ASSERT(map->depth > 0); - if (--map->depth == 0) { - JS_UNKEEP_ATOMS(cx->runtime); - map->sharpgen = 0; - JS_HashTableDestroy(map->table); - map->table = NULL; - } - if (idap) { - ida = *idap; - if (ida) { - JS_DestroyIdArray(cx, ida); - *idap = NULL; - } - } -} - -JS_STATIC_DLL_CALLBACK(intN) -gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg) -{ - GC_MARK((JSContext *)arg, (JSObject *)he->key, "sharp table entry"); - return JS_DHASH_NEXT; -} - -void -js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map) -{ - JS_ASSERT(map->depth > 0); - JS_ASSERT(map->table); - - /* - * During recursive calls to MarkSharpObjects a non-native object or - * object with a custom getProperty method can potentially return an - * unrooted value or even cut from the object graph an argument of one of - * MarkSharpObjects recursive invocations. So we must protect map->table - * entries against GC. - * - * We can not simply use JSTempValueRooter to mark the obj argument of - * MarkSharpObjects during recursion as we have to protect *all* entries - * in JSSharpObjectMap including those that contains otherwise unreachable - * objects just allocated through custom getProperty. Otherwise newer - * allocations can re-use the address of an object stored in the hashtable - * confusing js_EnterSharpObject. So to address the problem we simply - * mark all objects from map->table. - * - * An alternative "proper" solution is to use JSTempValueRooter in - * MarkSharpObjects with code to remove during finalization entries - * with otherwise unreachable objects. But this is way too complex - * to justify spending efforts. - */ - JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, cx); -} - -#define OBJ_TOSTRING_EXTRA 4 /* for 4 local GC roots */ - -#if JS_HAS_TOSOURCE -JSBool -js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSBool ok, outermost; - JSHashEntry *he; - JSIdArray *ida; - jschar *chars, *ochars, *vsharp; - const jschar *idstrchars, *vchars; - size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen; - char *comma; - jsint i, j, length, valcnt; - jsid id; -#if JS_HAS_GETTER_SETTER - JSObject *obj2; - JSProperty *prop; - uintN attrs; -#endif - jsval *val; - JSString *gsopold[2]; - JSString *gsop[2]; - JSAtom *atom; - JSString *idstr, *valstr, *str; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - /* If outermost, we need parentheses to be an expression, not a block. */ - outermost = (cx->sharpObjectMap.depth == 0); - he = js_EnterSharpObject(cx, obj, &ida, &chars); - if (!he) - return JS_FALSE; - if (IS_SHARP(he)) { - /* - * We didn't enter -- obj is already "sharp", meaning we've visited it - * already in our depth first search, and therefore chars contains a - * string of the form "#n#". - */ - JS_ASSERT(!ida); -#if JS_HAS_SHARP_VARS - nchars = js_strlen(chars); -#else - chars[0] = '{'; - chars[1] = '}'; - chars[2] = 0; - nchars = 2; -#endif - goto make_string; - } - JS_ASSERT(ida); - ok = JS_TRUE; - - if (!chars) { - /* If outermost, allocate 4 + 1 for "({})" and the terminator. */ - chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar)); - nchars = 0; - if (!chars) - goto error; - if (outermost) - chars[nchars++] = '('; - } else { - /* js_EnterSharpObject returned a string of the form "#n=" in chars. */ - MAKE_SHARP(he); - nchars = js_strlen(chars); - chars = (jschar *) - realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar)); - if (!chars) { - free(ochars); - goto error; - } - if (outermost) { - /* - * No need for parentheses around the whole shebang, because #n= - * unambiguously begins an object initializer, and never a block - * statement. - */ - outermost = JS_FALSE; - } - } - -#ifdef DUMP_CALL_TABLE - if (cx->options & JSOPTION_LOGCALL_TOSOURCE) { - const char *classname = OBJ_GET_CLASS(cx, obj)->name; - size_t classnchars = strlen(classname); - static const char classpropid[] = "C"; - const char *cp; - size_t onchars = nchars; - - /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */ - classnchars += sizeof classpropid - 1 + 2 + 2; - if (ida->length) - classnchars += 2; - - /* 2 for the braces, 1 for the terminator */ - chars = (jschar *) - realloc((ochars = chars), - (nchars + classnchars + 2 + 1) * sizeof(jschar)); - if (!chars) { - free(ochars); - goto error; - } - - chars[nchars++] = '{'; /* 1 from the 2 braces */ - for (cp = classpropid; *cp; cp++) - chars[nchars++] = (jschar) *cp; - chars[nchars++] = ':'; - chars[nchars++] = ' '; /* 2 for ': ' */ - chars[nchars++] = '"'; - for (cp = classname; *cp; cp++) - chars[nchars++] = (jschar) *cp; - chars[nchars++] = '"'; /* 2 quotes */ - if (ida->length) { - chars[nchars++] = ','; - chars[nchars++] = ' '; /* 2 for ', ' */ - } - - JS_ASSERT(nchars - onchars == 1 + classnchars); - } else -#endif - chars[nchars++] = '{'; - - comma = NULL; - - /* - * We have four local roots for cooked and raw value GC safety. Hoist the - * "argv + 2" out of the loop using the val local, which refers to the raw - * (unconverted, "uncooked") values. - */ - val = argv + 2; - - for (i = 0, length = ida->length; i < length; i++) { - JSBool idIsLexicalIdentifier, needOldStyleGetterSetter; - - /* Get strings for id and value and GC-root them via argv. */ - id = ida->vector[i]; - -#if JS_HAS_GETTER_SETTER - ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); - if (!ok) - goto error; -#endif - - /* - * Convert id to a jsval and then to a string. Decide early whether we - * prefer get/set or old getter/setter syntax. - */ - atom = JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL; - idstr = js_ValueToString(cx, ID_TO_VALUE(id)); - if (!idstr) { - ok = JS_FALSE; - OBJ_DROP_PROPERTY(cx, obj2, prop); - goto error; - } - *rval = STRING_TO_JSVAL(idstr); /* local root */ - idIsLexicalIdentifier = js_IsIdentifier(idstr); - needOldStyleGetterSetter = - !idIsLexicalIdentifier || - js_CheckKeyword(JSSTRING_CHARS(idstr), - JSSTRING_LENGTH(idstr)) != TOK_EOF; - -#if JS_HAS_GETTER_SETTER - - valcnt = 0; - if (prop) { - ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); - if (!ok) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - goto error; - } - if (OBJ_IS_NATIVE(obj2) && - (attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - if (attrs & JSPROP_GETTER) { - val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; - gsopold[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.getterAtom); - gsop[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.getAtom); - valcnt++; - } - if (attrs & JSPROP_SETTER) { - val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; - gsopold[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.setterAtom); - gsop[valcnt] = - ATOM_TO_STRING(cx->runtime->atomState.setAtom); - valcnt++; - } - } else { - valcnt = 1; - gsop[0] = NULL; - gsopold[0] = NULL; - ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); - } - OBJ_DROP_PROPERTY(cx, obj2, prop); - } - -#else /* !JS_HAS_GETTER_SETTER */ - - /* - * We simplify the source code at the price of minor dead code bloat in - * the ECMA version (for testing only, see jsconfig.h). The null - * default values in gsop[j] suffice to disable non-ECMA getter and - * setter code. - */ - valcnt = 1; - gsop[0] = NULL; - gsopold[0] = NULL; - ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]); - -#endif /* !JS_HAS_GETTER_SETTER */ - - if (!ok) - goto error; - - /* - * If id is a string that's not an identifier, then it needs to be - * quoted. Also, negative integer ids must be quoted. - */ - if (atom - ? !idIsLexicalIdentifier - : (JSID_IS_OBJECT(id) || JSID_TO_INT(id) < 0)) { - idstr = js_QuoteString(cx, idstr, (jschar)'\''); - if (!idstr) { - ok = JS_FALSE; - goto error; - } - *rval = STRING_TO_JSVAL(idstr); /* local root */ - } - idstrchars = JSSTRING_CHARS(idstr); - idstrlength = JSSTRING_LENGTH(idstr); - - for (j = 0; j < valcnt; j++) { - /* Convert val[j] to its canonical source form. */ - valstr = js_ValueToSource(cx, val[j]); - if (!valstr) { - ok = JS_FALSE; - goto error; - } - argv[j] = STRING_TO_JSVAL(valstr); /* local root */ - vchars = JSSTRING_CHARS(valstr); - vlength = JSSTRING_LENGTH(valstr); - - if (vchars[0] == '#') - needOldStyleGetterSetter = JS_TRUE; - - if (needOldStyleGetterSetter) - gsop[j] = gsopold[j]; - -#ifndef OLD_GETTER_SETTER - /* - * Remove '(function ' from the beginning of valstr and ')' from the - * end so that we can put "get" in front of the function definition. - */ - if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) && - !needOldStyleGetterSetter) { - const jschar *start = vchars; - if (vchars[0] == '(') - vchars++; - vchars = js_strchr_limit(vchars, '(', vchars + vlength); - if (vchars) { - vlength -= vchars - start + 1; - } else { - gsop[j] = NULL; - vchars = start; - } - } -#else - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; -#endif - - /* If val[j] is a non-sharp object, consider sharpening it. */ - vsharp = NULL; - vsharplength = 0; -#if JS_HAS_SHARP_VARS - if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') { - he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL, - &vsharp); - if (!he) { - ok = JS_FALSE; - goto error; - } - if (IS_SHARP(he)) { - vchars = vsharp; - vlength = js_strlen(vchars); - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; - } else { - if (vsharp) { - vsharplength = js_strlen(vsharp); - MAKE_SHARP(he); - needOldStyleGetterSetter = JS_TRUE; - gsop[j] = gsopold[j]; - } - js_LeaveSharpObject(cx, NULL); - } - } -#endif - -#define SAFE_ADD(n) \ - JS_BEGIN_MACRO \ - size_t n_ = (n); \ - curlen += n_; \ - if (curlen < n_) \ - goto overflow; \ - JS_END_MACRO - - curlen = nchars; - if (comma) - SAFE_ADD(2); - SAFE_ADD(idstrlength + 1); - if (gsop[j]) - SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1); - SAFE_ADD(vsharplength); - SAFE_ADD(vlength); - /* Account for the trailing null. */ - SAFE_ADD((outermost ? 2 : 1) + 1); -#undef SAFE_ADD - - if (curlen > (size_t)-1 / sizeof(jschar)) - goto overflow; - - /* Allocate 1 + 1 at end for closing brace and terminating 0. */ - chars = (jschar *) - realloc((ochars = chars), curlen * sizeof(jschar)); - if (!chars) { - /* Save code space on error: let JS_free ignore null vsharp. */ - JS_free(cx, vsharp); - free(ochars); - goto error; - } - - if (comma) { - chars[nchars++] = comma[0]; - chars[nchars++] = comma[1]; - } - comma = ", "; - - if (needOldStyleGetterSetter) { - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - if (gsop[j]) { - chars[nchars++] = ' '; - gsoplength = JSSTRING_LENGTH(gsop[j]); - js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), - gsoplength); - nchars += gsoplength; - } - chars[nchars++] = ':'; - } else { /* New style "decompilation" */ - if (gsop[j]) { - gsoplength = JSSTRING_LENGTH(gsop[j]); - js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), - gsoplength); - nchars += gsoplength; - chars[nchars++] = ' '; - } - js_strncpy(&chars[nchars], idstrchars, idstrlength); - nchars += idstrlength; - /* Extraneous space after id here will be extracted later */ - chars[nchars++] = gsop[j] ? ' ' : ':'; - } - - if (vsharplength) { - js_strncpy(&chars[nchars], vsharp, vsharplength); - nchars += vsharplength; - } - js_strncpy(&chars[nchars], vchars, vlength); - nchars += vlength; - - if (vsharp) - JS_free(cx, vsharp); -#ifdef DUMP_CALL_TABLE - if (outermost && nchars >= js_LogCallToSourceLimit) - break; -#endif - } - } - - chars[nchars++] = '}'; - if (outermost) - chars[nchars++] = ')'; - chars[nchars] = 0; - - error: - js_LeaveSharpObject(cx, &ida); - - if (!ok) { - if (chars) - free(chars); - return ok; - } - - if (!chars) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - make_string: - str = js_NewString(cx, chars, nchars, 0); - if (!str) { - free(chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - - overflow: - JS_free(cx, vsharp); - free(chars); - chars = NULL; - goto error; -} -#endif /* JS_HAS_TOSOURCE */ - -JSBool -js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jschar *chars; - size_t nchars; - const char *clazz, *prefix; - JSString *str; - - clazz = OBJ_GET_CLASS(cx, obj)->name; - nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ - chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - - prefix = "[object "; - nchars = 0; - while ((chars[nchars] = (jschar)*prefix) != 0) - nchars++, prefix++; - while ((chars[nchars] = (jschar)*clazz) != 0) - nchars++, clazz++; - chars[nchars++] = ']'; - chars[nchars] = 0; - - str = js_NewString(cx, chars, nchars, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -js_obj_toLocaleString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[-1]); - if (!str) - return JS_FALSE; - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Check whether principals subsumes scopeobj's principals, and return true - * if so (or if scopeobj has no principals, for backward compatibility with - * the JS API, which does not require principals), and false otherwise. - */ -JSBool -js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, - JSPrincipals *principals, JSAtom *caller) -{ - JSRuntime *rt; - JSPrincipals *scopePrincipals; - const char *callerstr; - - rt = cx->runtime; - if (rt->findObjectPrincipals) { - scopePrincipals = rt->findObjectPrincipals(cx, scopeobj); - if (!principals || !scopePrincipals || - !principals->subsume(principals, scopePrincipals)) { - callerstr = js_AtomToPrintableString(cx, caller); - if (!callerstr) - return JS_FALSE; - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, callerstr); - return JS_FALSE; - } - } - return JS_TRUE; -} - -JSObject * -js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller) -{ - JSClass *clasp; - JSExtendedClass *xclasp; - JSObject *inner; - - if (!scopeobj) - goto bad; - - OBJ_TO_INNER_OBJECT(cx, scopeobj); - if (!scopeobj) - return NULL; - - inner = scopeobj; - - /* XXX This is an awful gross hack. */ - while (scopeobj) { - clasp = OBJ_GET_CLASS(cx, scopeobj); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - xclasp = (JSExtendedClass*)clasp; - if (xclasp->innerObject && - xclasp->innerObject(cx, scopeobj) != scopeobj) { - goto bad; - } - } - - scopeobj = OBJ_GET_PARENT(cx, scopeobj); - } - - return inner; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, caller); - return NULL; -} - -static JSBool -obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSStackFrame *fp, *caller; - JSBool indirectCall; - JSObject *scopeobj; - JSString *str; - const char *file; - uintN line; - JSPrincipals *principals; - JSScript *script; - JSBool ok; -#if JS_HAS_EVAL_THIS_SCOPE - JSObject *callerScopeChain = NULL, *callerVarObj = NULL; - JSObject *setCallerScopeChain = NULL; - JSBool setCallerVarObj = JS_FALSE; -#endif - - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - JS_ASSERT(!caller || caller->pc); - indirectCall = (caller && *caller->pc != JSOP_EVAL); - - if (indirectCall && - !JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_BAD_INDIRECT_CALL, - js_eval_str)) { - return JS_FALSE; - } - - if (!JSVAL_IS_STRING(argv[0])) { - *rval = argv[0]; - return JS_TRUE; - } - - /* - * If the caller is a lightweight function and doesn't have a variables - * object, then we need to provide one for the compiler to stick any - * declared (var) variables into. - */ - if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) - return JS_FALSE; - -#if JS_HAS_SCRIPT_OBJECT - /* - * Script.prototype.compile/exec and Object.prototype.eval all take an - * optional trailing argument that overrides the scope object. - */ - scopeobj = NULL; - if (argc >= 2) { - if (!js_ValueToObject(cx, argv[1], &scopeobj)) - return JS_FALSE; - argv[1] = OBJECT_TO_JSVAL(scopeobj); - } - if (!scopeobj) -#endif - { -#if JS_HAS_EVAL_THIS_SCOPE - /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */ - if (indirectCall) { - callerScopeChain = js_GetScopeChain(cx, caller); - if (!callerScopeChain) - return JS_FALSE; - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - if (obj != callerScopeChain) { - if (!js_CheckPrincipalsAccess(cx, obj, - caller->script->principals, - cx->runtime->atomState.evalAtom)) - { - return JS_FALSE; - } - - scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1); - if (!scopeobj) - return JS_FALSE; - - /* Set fp->scopeChain too, for the compiler. */ - caller->scopeChain = fp->scopeChain = scopeobj; - - /* Remember scopeobj so we can null its private when done. */ - setCallerScopeChain = scopeobj; - } - - callerVarObj = caller->varobj; - if (obj != callerVarObj) { - /* Set fp->varobj too, for the compiler. */ - caller->varobj = fp->varobj = obj; - setCallerVarObj = JS_TRUE; - } - } - /* From here on, control must exit through label out with ok set. */ -#endif - - /* Compile using caller's current scope object. */ - if (caller) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) { - ok = JS_FALSE; - goto out; - } - } - } - - /* Ensure we compile this eval with the right object in the scope chain. */ - scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str); - if (!scopeobj) - return JS_FALSE; - - str = JSVAL_TO_STRING(argv[0]); - if (caller) { - principals = JS_EvalFramePrincipals(cx, fp, caller); - if (principals == caller->script->principals) { - file = caller->script->filename; - line = js_PCToLineNumber(cx, caller->script, caller->pc); - } else { - file = principals->codebase; - line = 0; - } - } else { - file = NULL; - line = 0; - principals = NULL; - } - - /* - * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was - * invoked) between fp and its scripted caller, to help the compiler easily - * find the same caller whose scope and var obj we've set. - * - * XXX this nonsense could, and perhaps should, go away with a better way - * to pass params to the compiler than via the top-most frame. - */ - do { - fp->flags |= JSFRAME_EVAL; - } while ((fp = fp->down) != caller); - - script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, - JSSTRING_CHARS(str), - JSSTRING_LENGTH(str), - file, line); - if (!script) { - ok = JS_FALSE; - goto out; - } - -#if JS_HAS_SCRIPT_OBJECT - if (argc < 2) -#endif - { - /* Execute using caller's new scope object (might be a Call object). */ - if (caller) - scopeobj = caller->scopeChain; - } - - /* - * Belt-and-braces: check that the lesser of eval's principals and the - * caller's principals has access to scopeobj. - */ - ok = js_CheckPrincipalsAccess(cx, scopeobj, principals, - cx->runtime->atomState.evalAtom); - if (ok) - ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); - - JS_DestroyScript(cx, script); - -out: -#if JS_HAS_EVAL_THIS_SCOPE - /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */ - if (setCallerScopeChain) { - caller->scopeChain = callerScopeChain; - JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass); - JS_SetPrivate(cx, setCallerScopeChain, NULL); - } - if (setCallerVarObj) - caller->varobj = callerVarObj; -#endif - return ok; -} - -#if JS_HAS_OBJ_WATCHPOINT - -static JSBool -obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp, - void *closure) -{ - JSObject *callable; - JSRuntime *rt; - JSStackFrame *caller; - JSPrincipals *subject, *watcher; - JSResolvingKey key; - JSResolvingEntry *entry; - uint32 generation; - jsval argv[3]; - JSBool ok; - - callable = (JSObject *) closure; - - rt = cx->runtime; - if (rt->findObjectPrincipals) { - /* Skip over any obj_watch_* frames between us and the real subject. */ - caller = JS_GetScriptedCaller(cx, cx->fp); - if (caller) { - /* - * Only call the watch handler if the watcher is allowed to watch - * the currently executing script. - */ - watcher = rt->findObjectPrincipals(cx, callable); - subject = JS_StackFramePrincipals(cx, caller); - - if (watcher && subject && !watcher->subsume(watcher, subject)) { - /* Silently don't call the watch handler. */ - return JS_TRUE; - } - } - } - - /* Avoid recursion on (obj, id) already being watched on cx. */ - key.obj = obj; - key.id = id; - if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry)) - return JS_FALSE; - if (!entry) - return JS_TRUE; - generation = cx->resolvingTable->generation; - - argv[0] = id; - argv[1] = old; - argv[2] = *nvp; - ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp); - js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation); - return ok; -} - -static JSBool -obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *callable; - jsval userid, value; - jsid propid; - uintN attrs; - - callable = js_ValueToCallableObject(cx, &argv[1], 0); - if (!callable) - return JS_FALSE; - - /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ - userid = argv[0]; - if (!JS_ValueToId(cx, userid, &propid)) - return JS_FALSE; - - if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs)) - return JS_FALSE; - if (attrs & JSPROP_READONLY) - return JS_TRUE; - return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable); -} - -static JSBool -obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL); -} - -#endif /* JS_HAS_OBJ_WATCHPOINT */ - -/* - * Prototype and property query methods, to complement the 'in' and - * 'instanceof' operators. - */ - -/* Proposed ECMA 15.2.4.5. */ -static JSBool -obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return js_HasOwnPropertyHelper(cx, obj, obj->map->ops->lookupProperty, - argc, argv, rval); -} - -JSBool -js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, - uintN argc, jsval *argv, jsval *rval) -{ - jsid id; - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!lookup(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - *rval = JSVAL_FALSE; - } else if (obj2 == obj) { - *rval = JSVAL_TRUE; - } else { - JSClass *clasp; - JSExtendedClass *xclasp; - - clasp = OBJ_GET_CLASS(cx, obj); - xclasp = (clasp->flags & JSCLASS_IS_EXTENDED) - ? (JSExtendedClass *)clasp - : NULL; - if (xclasp && xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj) { - *rval = JSVAL_TRUE; - } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj2) == clasp) { - /* - * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a - * delegated property makes that property appear to be direct in - * all delegating instances of the same native class. This hack - * avoids bloating every function instance with its own 'length' - * (AKA 'arity') property. But it must not extend across class - * boundaries, to avoid making hasOwnProperty lie (bug 320854). - * - * It's not really a hack, of course: a permanent property can't - * be deleted, and JSPROP_SHARED means "don't allocate a slot in - * any instance, prototype or delegating". Without a slot, and - * without the ability to remove and recreate (with differences) - * the property, there is no way to tell whether it is directly - * owned, or indirectly delegated. - */ - sprop = (JSScopeProperty *)prop; - *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop)); - } else { - *rval = JSVAL_FALSE; - } - } - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - return JS_TRUE; -} - -/* Proposed ECMA 15.2.4.6. */ -static JSBool -obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSBool b; - - if (!js_IsDelegate(cx, obj, *argv, &b)) - return JS_FALSE; - *rval = BOOLEAN_TO_JSVAL(b); - return JS_TRUE; -} - -/* Proposed ECMA 15.2.4.7. */ -static JSBool -obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsid id; - uintN attrs; - JSObject *obj2; - JSProperty *prop; - JSBool ok; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - - if (!prop) { - *rval = JSVAL_FALSE; - return JS_TRUE; - } - - /* - * XXX ECMA spec error compatible: return false unless hasOwnProperty. - * The ECMA spec really should be fixed so propertyIsEnumerable and the - * for..in loop agree on whether prototype properties are enumerable, - * obviously by fixing this method (not by breaking the for..in loop!). - * - * We check here for shared permanent prototype properties, which should - * be treated as if they are local to obj. They are an implementation - * technique used to satisfy ECMA requirements; users should not be able - * to distinguish a shared permanent proto-property from a local one. - */ - if (obj2 != obj && - !(OBJ_IS_NATIVE(obj2) && - SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - *rval = JSVAL_FALSE; - return JS_TRUE; - } - - ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs); - OBJ_DROP_PROPERTY(cx, obj2, prop); - if (ok) - *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0); - return ok; -} - -#if JS_HAS_GETTER_SETTER -static JSBool -obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval fval, junk; - jsid id; - uintN attrs; - - fval = argv[1]; - if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - js_getter_str); - return JS_FALSE; - } - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL)) - return JS_FALSE; - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) - return JS_FALSE; - return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, - (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL, - JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, - NULL); -} - -static JSBool -obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval fval, junk; - jsid id; - uintN attrs; - - fval = argv[1]; - if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_GETTER_OR_SETTER, - js_setter_str); - return JS_FALSE; - } - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL)) - return JS_FALSE; - /* - * Getters and setters are just like watchpoints from an access - * control point of view. - */ - if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs)) - return JS_FALSE; - return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, - NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval), - JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, - NULL); -} - -static JSBool -obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsid id; - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - if (OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *) prop; - if (sprop->attrs & JSPROP_GETTER) - *rval = OBJECT_TO_JSVAL(sprop->getter); - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} - -static JSBool -obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsid id; - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - - if (!JS_ValueToId(cx, argv[0], &id)) - return JS_FALSE; - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - if (OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *) prop; - if (sprop->attrs & JSPROP_SETTER) - *rval = OBJECT_TO_JSVAL(sprop->setter); - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - return JS_TRUE; -} -#endif /* JS_HAS_GETTER_SETTER */ - -#if JS_HAS_OBJ_WATCHPOINT -const char js_watch_str[] = "watch"; -const char js_unwatch_str[] = "unwatch"; -#endif -const char js_hasOwnProperty_str[] = "hasOwnProperty"; -const char js_isPrototypeOf_str[] = "isPrototypeOf"; -const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable"; -#if JS_HAS_GETTER_SETTER -const char js_defineGetter_str[] = "__defineGetter__"; -const char js_defineSetter_str[] = "__defineSetter__"; -const char js_lookupGetter_str[] = "__lookupGetter__"; -const char js_lookupSetter_str[] = "__lookupSetter__"; -#endif - -static JSFunctionSpec object_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA}, -#endif - {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA}, - {js_toLocaleString_str, js_obj_toLocaleString, 0, 0, OBJ_TOSTRING_EXTRA}, - {js_valueOf_str, obj_valueOf, 0,0,0}, - {js_eval_str, obj_eval, 1,0,0}, -#if JS_HAS_OBJ_WATCHPOINT - {js_watch_str, obj_watch, 2,0,0}, - {js_unwatch_str, obj_unwatch, 1,0,0}, -#endif - {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0}, - {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0}, - {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0}, -#if JS_HAS_GETTER_SETTER - {js_defineGetter_str, obj_defineGetter, 2,0,0}, - {js_defineSetter_str, obj_defineSetter, 2,0,0}, - {js_lookupGetter_str, obj_lookupGetter, 1,0,0}, - {js_lookupSetter_str, obj_lookupSetter, 1,0,0}, -#endif - {0,0,0,0,0} -}; - -static JSBool -Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (argc == 0) { - /* Trigger logic below to construct a blank object. */ - obj = NULL; - } else { - /* If argv[0] is null or undefined, obj comes back null. */ - if (!js_ValueToObject(cx, argv[0], &obj)) - return JS_FALSE; - } - if (!obj) { - JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); - if (cx->fp->flags & JSFRAME_CONSTRUCTING) - return JS_TRUE; - obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL); - if (!obj) - return JS_FALSE; - } - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * ObjectOps and Class for with-statement stack objects. - */ -static JSBool -with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_LookupProperty(cx, obj, id, objp, propp); - return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); -} - -static JSBool -with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_GetProperty(cx, obj, id, vp); - return OBJ_GET_PROPERTY(cx, proto, id, vp); -} - -static JSBool -with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_SetProperty(cx, obj, id, vp); - return OBJ_SET_PROPERTY(cx, proto, id, vp); -} - -static JSBool -with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_GetAttributes(cx, obj, id, prop, attrsp); - return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp); -} - -static JSBool -with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_SetAttributes(cx, obj, id, prop, attrsp); - return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp); -} - -static JSBool -with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_DeleteProperty(cx, obj, id, rval); - return OBJ_DELETE_PROPERTY(cx, proto, id, rval); -} - -static JSBool -with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_DefaultValue(cx, obj, hint, vp); - return OBJ_DEFAULT_VALUE(cx, proto, hint, vp); -} - -static JSBool -with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_Enumerate(cx, obj, enum_op, statep, idp); - return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp); -} - -static JSBool -with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return js_CheckAccess(cx, obj, id, mode, vp, attrsp); - return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp); -} - -static JSObject * -with_ThisObject(JSContext *cx, JSObject *obj) -{ - JSObject *proto = OBJ_GET_PROTO(cx, obj); - if (!proto) - return obj; - return OBJ_THIS_OBJECT(cx, proto); -} - -JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, - with_LookupProperty, js_DefineProperty, - with_GetProperty, with_SetProperty, - with_GetAttributes, with_SetAttributes, - with_DeleteProperty, with_DefaultValue, - with_Enumerate, with_CheckAccess, - with_ThisObject, NATIVE_DROP_PROPERTY, - NULL, NULL, - NULL, NULL, - js_SetProtoOrParent, js_SetProtoOrParent, - js_Mark, js_Clear, - NULL, NULL -}; - -static JSObjectOps * -with_getObjectOps(JSContext *cx, JSClass *clasp) -{ - return &js_WithObjectOps; -} - -JSClass js_WithClass = { - "With", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - with_getObjectOps, - 0,0,0,0,0,0,0 -}; - -JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_WithClass, proto, parent); - if (!obj) - return NULL; - obj->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(cx->fp); - OBJ_SET_BLOCK_DEPTH(cx, obj, depth); - return obj; -} - -JSObject * -js_NewBlockObject(JSContext *cx) -{ - JSObject *obj; - - /* - * Null obj's proto slot so that Object.prototype.* does not pollute block - * scopes. Make sure obj has its own scope too, since clearing proto does - * not affect OBJ_SCOPE(obj). - */ - obj = js_NewObject(cx, &js_BlockClass, NULL, NULL); - if (!obj || !js_GetMutableScope(cx, obj)) - return NULL; - OBJ_SET_PROTO(cx, obj, NULL); - return obj; -} - -JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, - JSStackFrame *fp) -{ - JSObject *clone; - - clone = js_NewObject(cx, &js_BlockClass, proto, parent); - if (!clone) - return NULL; - clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp); - clone->slots[JSSLOT_BLOCK_DEPTH] = - OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH); - return clone; -} - -/* - * XXXblock this reverses a path in the property tree -- try to share - * the prototype's scope harder! - */ -JSBool -js_PutBlockObject(JSContext *cx, JSObject *obj) -{ - JSStackFrame *fp; - uintN depth, slot; - JSScopeProperty *sprop; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - JS_ASSERT(fp); - depth = OBJ_BLOCK_DEPTH(cx, obj); - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { - if (sprop->getter != js_BlockClass.getProperty) - continue; - if (!(sprop->flags & SPROP_HAS_SHORTID)) - continue; - slot = depth + (uintN)sprop->shortid; - JS_ASSERT(slot < fp->script->depth); - if (!js_DefineNativeProperty(cx, obj, sprop->id, - fp->spbase[slot], NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, sprop->shortid, - NULL)) { - JS_SetPrivate(cx, obj, NULL); - return JS_FALSE; - } - } - - return JS_SetPrivate(cx, obj, NULL); -} - -static JSBool -block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); - JS_ASSERT((uintN)slot < fp->script->depth); - *vp = fp->spbase[slot]; - return JS_TRUE; -} - -static JSBool -block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSStackFrame *fp; - jsint slot; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL)); - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); - if (!fp) - return JS_TRUE; - - slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id); - JS_ASSERT((uintN)slot < fp->script->depth); - fp->spbase[slot] = *vp; - return JS_TRUE; -} - -#if JS_HAS_XDR - -#define NO_PARENT_INDEX (jsatomid)-1 - -jsatomid -FindObjectAtomIndex(JSAtomMap *map, JSObject *obj) -{ - size_t i; - JSAtom *atom; - - for (i = 0; i < map->length; i++) { - atom = map->vector[i]; - if (ATOM_KEY(atom) == OBJECT_TO_JSVAL(obj)) - return i; - } - - return NO_PARENT_INDEX; -} - -static JSBool -block_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - jsatomid parentId; - JSAtomMap *atomMap; - JSObject *obj, *parent; - uint16 depth, count, i; - uint32 tmp; - JSTempValueRooter tvr; - JSScopeProperty *sprop; - jsid propid; - JSAtom *atom; - int16 shortid; - JSBool ok; - - cx = xdr->cx; -#ifdef __GNUC__ - obj = NULL; /* quell GCC overwarning */ -#endif - - atomMap = &xdr->script->atomMap; - if (xdr->mode == JSXDR_ENCODE) { - obj = *objp; - parent = OBJ_GET_PARENT(cx, obj); - parentId = FindObjectAtomIndex(atomMap, parent); - depth = OBJ_BLOCK_DEPTH(cx, obj); - count = OBJ_BLOCK_COUNT(cx, obj); - tmp = (uint32)(depth << 16) | count; - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else count = 0; -#endif - - /* First, XDR the parent atomid. */ - if (!JS_XDRUint32(xdr, &parentId)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - obj = js_NewBlockObject(cx); - if (!obj) - return JS_FALSE; - *objp = obj; - - /* - * If there's a parent id, then get the parent out of our script's - * atomMap. We know that we XDR block object in outer-to-inner order, - * which means that getting the parent now will work. - */ - if (parentId == NO_PARENT_INDEX) { - parent = NULL; - } else { - atom = js_GetAtom(cx, atomMap, parentId); - JS_ASSERT(ATOM_IS_OBJECT(atom)); - parent = ATOM_TO_OBJECT(atom); - } - obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); - } - - JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr); - - if (!JS_XDRUint32(xdr, &tmp)) { - JS_POP_TEMP_ROOT(cx, &tvr); - return JS_FALSE; - } - - if (xdr->mode == JSXDR_DECODE) { - depth = (uint16)(tmp >> 16); - count = (uint16)tmp; - obj->slots[JSSLOT_BLOCK_DEPTH] = INT_TO_JSVAL(depth); - } - - /* - * XDR the block object's properties. We know that there are 'count' - * properties to XDR, stored as id/shortid pairs. We do not XDR any - * non-native properties, only those that the compiler created. - */ - sprop = NULL; - ok = JS_TRUE; - for (i = 0; i < count; i++) { - if (xdr->mode == JSXDR_ENCODE) { - /* Find a property to XDR. */ - do { - /* If sprop is NULL, this is the first property. */ - sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp; - } while (!(sprop->flags & SPROP_HAS_SHORTID)); - - JS_ASSERT(sprop->getter == js_BlockClass.getProperty); - propid = sprop->id; - JS_ASSERT(JSID_IS_ATOM(propid)); - atom = JSID_TO_ATOM(propid); - shortid = sprop->shortid; - JS_ASSERT(shortid >= 0); - } - - /* XDR the real id, then the shortid. */ - if (!js_XDRStringAtom(xdr, &atom) || - !JS_XDRUint16(xdr, (uint16 *)&shortid)) { - ok = JS_FALSE; - break; - } - - if (xdr->mode == JSXDR_DECODE) { - if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, shortid, NULL)) { - ok = JS_FALSE; - break; - } - } - } - - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -#else -# define block_xdrObject NULL -#endif - -JSClass js_BlockClass = { - "Block", - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block), - JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, NULL -}; - -JSObject* -js_InitBlockClass(JSContext *cx, JSObject* obj) -{ - JSObject *proto; - - proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL, - NULL, NULL, NULL); - if (!proto) - return NULL; - - OBJ_SET_PROTO(cx, proto, NULL); - return proto; -} - -JSObject * -js_InitObjectClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - jsval eval; - - proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1, - object_props, object_methods, NULL, NULL); - if (!proto) - return NULL; - - /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */ - if (!OBJ_GET_PROPERTY(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.evalAtom), - &eval)) { - return NULL; - } - if (!OBJ_DEFINE_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState.evalAtom), - eval, NULL, NULL, 0, NULL)) { - return NULL; - } - - return proto; -} - -void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp) -{ - map->nrefs = nrefs; - map->ops = ops; - map->nslots = JS_INITIAL_NSLOTS; - map->freeslot = JSSLOT_FREE(clasp); -} - -JSObjectMap * -js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ - return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); -} - -void -js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - js_DestroyScope(cx, (JSScope *)map); -} - -JSObjectMap * -js_HoldObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_ASSERT(map->nrefs >= 0); - JS_ATOMIC_INCREMENT(&map->nrefs); - return map; -} - -JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) -{ - JS_ASSERT(map->nrefs > 0); - JS_ATOMIC_DECREMENT(&map->nrefs); - if (map->nrefs == 0) { - map->ops->destroyObjectMap(cx, map); - return NULL; - } - if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) - ((JSScope *)map)->object = NULL; - return map; -} - -static jsval * -AllocSlots(JSContext *cx, jsval *slots, uint32 nslots) -{ - size_t nbytes, obytes, minbytes; - uint32 i, oslots; - jsval *newslots; - - nbytes = (nslots + 1) * sizeof(jsval); - if (slots) { - oslots = slots[-1]; - obytes = (oslots + 1) * sizeof(jsval); - } else { - oslots = 0; - obytes = 0; - } - - if (nbytes <= GC_NBYTES_MAX) { - newslots = (jsval *) js_NewGCThing(cx, GCX_PRIVATE, nbytes); - } else { - newslots = (jsval *) - JS_realloc(cx, - (obytes <= GC_NBYTES_MAX) ? NULL : slots - 1, - nbytes); - } - if (!newslots) - return NULL; - - if (obytes != 0) { - /* If either nbytes or obytes fit in a GC-thing, we must copy. */ - minbytes = JS_MIN(nbytes, obytes); - if (minbytes <= GC_NBYTES_MAX) - memcpy(newslots + 1, slots, minbytes - sizeof(jsval)); - - /* If nbytes are in a GC-thing but obytes aren't, free obytes. */ - if (nbytes <= GC_NBYTES_MAX && obytes > GC_NBYTES_MAX) - JS_free(cx, slots - 1); - - /* If we're extending an allocation, initialize free slots. */ - if (nslots > oslots) { - for (i = 1 + oslots; i <= nslots; i++) - newslots[i] = JSVAL_VOID; - } - } - - newslots[0] = nslots; - return ++newslots; -} - -static void -FreeSlots(JSContext *cx, jsval *slots) -{ - size_t nbytes; - - /* - * NB: We count on smaller GC-things being finalized before larger things - * that become garbage during the same GC. Without this assumption, we - * couldn't load slots[-1] here without possibly loading a gcFreeList link - * (see struct JSGCThing in jsgc.h). - */ - nbytes = (slots[-1] + 1) * sizeof(jsval); - if (nbytes > GC_NBYTES_MAX) - JS_free(cx, slots - 1); -} - -extern JSBool -js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) -{ - JSProtoKey key; - JSAtom *atom; - - key = JSCLASS_CACHED_PROTO_KEY(clasp); - if (key != JSProto_Null) { - *idp = INT_TO_JSID(key); - } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) { - *idp = INT_TO_JSID(JSProto_Object); - } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return JS_FALSE; - *idp = ATOM_TO_JSID(atom); - } - return JS_TRUE; -} - -JSObject * -js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent) -{ - jsid id; - JSObject *obj; - JSObjectOps *ops; - JSObjectMap *map; - JSClass *protoclasp; - uint32 nslots, i; - jsval *newslots; - JSTempValueRooter tvr; - - /* Bootstrap the ur-object, and make it the default prototype object. */ - if (!proto) { - if (!js_GetClassId(cx, clasp, &id)) - return NULL; - if (!js_GetClassPrototype(cx, parent, id, &proto)) - return NULL; - if (!proto && - !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object), - &proto)) { - return NULL; - } - } - - /* Always call the class's getObjectOps hook if it has one. */ - ops = clasp->getObjectOps - ? clasp->getObjectOps(cx, clasp) - : &js_ObjectOps; - - /* - * Allocate a zeroed object from the GC heap. Do this *after* any other - * GC-thing allocations under js_GetClassPrototype or clasp->getObjectOps, - * to avoid displacing the newborn root for obj. - */ - obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); - if (!obj) - return NULL; - - /* - * Root obj to prevent it from being collected out from under this call. - * to js_NewObject. AllocSlots can trigger a finalizer from a last-ditch - * GC calling JS_ClearNewbornRoots. There's also the possibilty of things - * happening under the objectHook call-out further below. - */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - /* - * Share proto's map only if it has the same JSObjectOps, and only if - * proto's class has the same private and reserved slots as obj's map - * and class have. We assume that if prototype and object are of the - * same class, they always have the same number of computed reserved - * slots (returned via clasp->reserveSlots); otherwise, prototype and - * object classes must have the same (null or not) reserveSlots hook. - */ - if (proto && - (map = proto->map)->ops == ops && - ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || - (!((protoclasp->flags ^ clasp->flags) & - (JSCLASS_HAS_PRIVATE | - (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && - protoclasp->reserveSlots == clasp->reserveSlots))) - { - /* - * Default parent to the parent of the prototype, which was set from - * the parent of the prototype's constructor. - */ - if (!parent) - parent = OBJ_GET_PARENT(cx, proto); - - /* Share the given prototype's map. */ - obj->map = js_HoldObjectMap(cx, map); - - /* Ensure that obj starts with the minimum slots for clasp. */ - nslots = JS_INITIAL_NSLOTS; - } else { - /* Leave parent alone. Allocate a new map for obj. */ - map = ops->newObjectMap(cx, 1, ops, clasp, obj); - if (!map) - goto bad; - obj->map = map; - - /* Let ops->newObjectMap set nslots so as to reserve slots. */ - nslots = map->nslots; - } - - /* Allocate a slots vector, with a -1'st element telling its length. */ - newslots = AllocSlots(cx, NULL, nslots); - if (!newslots) { - js_DropObjectMap(cx, obj->map, obj); - obj->map = NULL; - goto bad; - } - - /* Set the proto, parent, and class properties. */ - newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); - newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); - newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp); - - /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */ - for (i = JSSLOT_CLASS + 1; i < nslots; i++) - newslots[i] = JSVAL_VOID; - - /* Store newslots after initializing all of 'em, just in case. */ - obj->slots = newslots; - - if (cx->runtime->objectHook) { - JS_KEEP_ATOMS(cx->runtime); - cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData); - JS_UNKEEP_ATOMS(cx->runtime); - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - cx->weakRoots.newborn[GCX_OBJECT] = (JSGCThing *) obj; - return obj; - -bad: - obj = NULL; - goto out; -} - -JS_STATIC_DLL_CALLBACK(JSObject *) -js_InitNullClass(JSContext *cx, JSObject *obj) -{ - JS_ASSERT(0); - return NULL; -} - -#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *); -#include "jsproto.tbl" -#undef JS_PROTO - -static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = { -#define JS_PROTO(name,code,init) init, -#include "jsproto.tbl" -#undef JS_PROTO -}; - -JSBool -js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp) -{ - JSBool ok; - JSObject *tmp, *cobj; - JSResolvingKey rkey; - JSResolvingEntry *rentry; - uint32 generation; - JSObjectOp init; - jsval v; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) { - *objp = NULL; - return JS_TRUE; - } - - ok = JS_GetReservedSlot(cx, obj, key, &v); - if (!ok) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - *objp = JSVAL_TO_OBJECT(v); - return JS_TRUE; - } - - rkey.obj = obj; - rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry)) - return JS_FALSE; - if (!rentry) { - /* Already caching key in obj -- suppress recursion. */ - *objp = NULL; - return JS_TRUE; - } - generation = cx->resolvingTable->generation; - - cobj = NULL; - init = lazy_prototype_init[key]; - if (init) { - if (!init(cx, obj)) { - ok = JS_FALSE; - } else { - ok = JS_GetReservedSlot(cx, obj, key, &v); - if (ok && !JSVAL_IS_PRIMITIVE(v)) - cobj = JSVAL_TO_OBJECT(v); - } - } - - js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation); - *objp = cobj; - return ok; -} - -JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj) -{ - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) - return JS_TRUE; - - return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj)); -} - -JSBool -js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) -{ - JSObject *obj, *cobj, *pobj; - JSProtoKey key; - JSProperty *prop; - JSScopeProperty *sprop; - - if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { - /* Find the topmost object in the scope chain. */ - do { - obj = start; - start = OBJ_GET_PARENT(cx, obj); - } while (start); - } else { - obj = cx->globalObject; - if (!obj) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - } - - OBJ_TO_INNER_OBJECT(cx, obj); - if (!obj) - return JS_FALSE; - - if (JSID_IS_INT(id)) { - key = JSID_TO_INT(id); - JS_ASSERT(key != JSProto_Null); - if (!js_GetClassObject(cx, obj, key, &cobj)) - return JS_FALSE; - if (cobj) { - *vp = OBJECT_TO_JSVAL(cobj); - return JS_TRUE; - } - id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]); - } - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, - &pobj, &prop)) { - return JS_FALSE; - } - if (!prop) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - JS_ASSERT(OBJ_IS_NATIVE(pobj)); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); - *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot); - OBJ_DROP_PROPERTY(cx, pobj, prop); - return JS_TRUE; -} - -JSObject * -js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv) -{ - jsid id; - jsval cval, rval; - JSTempValueRooter argtvr, tvr; - JSObject *obj, *ctor; - - JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr); - - if (!js_GetClassId(cx, clasp, &id) || - !js_FindClassObject(cx, parent, id, &cval)) { - JS_POP_TEMP_ROOT(cx, &argtvr); - return NULL; - } - - if (JSVAL_IS_PRIMITIVE(cval)) { - js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK); - JS_POP_TEMP_ROOT(cx, &argtvr); - return NULL; - } - - /* - * Protect cval in case a crazy getter for .prototype uproots it. After - * this point, all control flow must exit through label out with obj set. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr); - - /* - * If proto or parent are NULL, set them to Constructor.prototype and/or - * Constructor.__parent__, just like JSOP_NEW does. - */ - ctor = JSVAL_TO_OBJECT(cval); - if (!parent) - parent = OBJ_GET_PARENT(cx, ctor); - if (!proto) { - if (!OBJ_GET_PROPERTY(cx, ctor, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &rval)) { - obj = NULL; - goto out; - } - if (JSVAL_IS_OBJECT(rval)) - proto = JSVAL_TO_OBJECT(rval); - } - - obj = js_NewObject(cx, clasp, proto, parent); - if (!obj) - goto out; - - if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval)) - goto bad; - - if (JSVAL_IS_PRIMITIVE(rval)) - goto out; - obj = JSVAL_TO_OBJECT(rval); - - /* - * If the instance's class differs from what was requested, throw a type - * error. If the given class has both the JSCLASS_HAS_PRIVATE and the - * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its - * private data set at this point, then the constructor was replaced and - * we should throw a type error. - */ - if (OBJ_GET_CLASS(cx, obj) != clasp || - (!(~clasp->flags & (JSCLASS_HAS_PRIVATE | - JSCLASS_CONSTRUCT_PROTOTYPE)) && - !JS_GetPrivate(cx, obj))) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_WRONG_CONSTRUCTOR, clasp->name); - goto bad; - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - JS_POP_TEMP_ROOT(cx, &argtvr); - return obj; - -bad: - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - obj = NULL; - goto out; -} - -void -js_FinalizeObject(JSContext *cx, JSObject *obj) -{ - JSObjectMap *map; - - /* Cope with stillborn objects that have no map. */ - map = obj->map; - if (!map) - return; - JS_ASSERT(obj->slots); - - if (cx->runtime->objectHook) - cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData); - - /* Remove all watchpoints with weak links to obj. */ - JS_ClearWatchPointsForObject(cx, obj); - - /* - * Finalize obj first, in case it needs map and slots. Optimized to use - * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting" - * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when - * we're called from the GC. Only the GC should call js_FinalizeObject, - * and no other threads run JS (and possibly racing to update obj->slots) - * while the GC is running. - */ - LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj); - - /* Drop map and free slots. */ - js_DropObjectMap(cx, map, obj); - obj->map = NULL; - FreeSlots(cx, obj->slots); - obj->slots = NULL; -} - -/* XXXbe if one adds props, deletes earlier props, adds more, the last added - won't recycle the deleted props' slots. */ -JSBool -js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) -{ - JSObjectMap *map; - JSClass *clasp; - uint32 nslots; - jsval *newslots; - - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (map->freeslot == JSSLOT_FREE(clasp)) { - /* Adjust map->freeslot to include computed reserved slots, if any. */ - if (clasp->reserveSlots) - map->freeslot += clasp->reserveSlots(cx, obj); - } - nslots = map->nslots; - if (map->freeslot >= nslots) { - nslots = map->freeslot; - JS_ASSERT(nslots >= JS_INITIAL_NSLOTS); - nslots += (nslots + 1) / 2; - - newslots = AllocSlots(cx, obj->slots, nslots); - if (!newslots) - return JS_FALSE; - map->nslots = nslots; - obj->slots = newslots; - } - -#ifdef TOO_MUCH_GC - obj->slots[map->freeslot] = JSVAL_VOID; -#endif - *slotp = map->freeslot++; - return JS_TRUE; -} - -void -js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) -{ - JSObjectMap *map; - uint32 nslots; - jsval *newslots; - - OBJ_CHECK_SLOT(obj, slot); - obj->slots[slot] = JSVAL_VOID; - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); - if (map->freeslot == slot + 1) - map->freeslot = slot; - nslots = map->nslots; - if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) { - nslots = map->freeslot; - nslots += nslots / 2; - if (nslots < JS_INITIAL_NSLOTS) - nslots = JS_INITIAL_NSLOTS; - - newslots = AllocSlots(cx, obj->slots, nslots); - if (!newslots) - return; - map->nslots = nslots; - obj->slots = newslots; - } -} - -/* JSVAL_INT_MAX as a string */ -#define JSVAL_INT_MAX_STRING "1073741823" - -#define CHECK_FOR_STRING_INDEX(id) \ - JS_BEGIN_MACRO \ - if (JSID_IS_ATOM(id)) { \ - JSAtom *atom_ = JSID_TO_ATOM(id); \ - JSString *str_ = ATOM_TO_STRING(atom_); \ - const jschar *cp_ = str_->chars; \ - JSBool negative_ = (*cp_ == '-'); \ - if (negative_) cp_++; \ - if (JS7_ISDEC(*cp_)) { \ - size_t n_ = str_->length - negative_; \ - if (n_ <= sizeof(JSVAL_INT_MAX_STRING) - 1) \ - id = CheckForStringIndex(id, cp_, cp_ + n_, negative_); \ - } \ - } \ - JS_END_MACRO - -static jsid -CheckForStringIndex(jsid id, const jschar *cp, const jschar *end, - JSBool negative) -{ - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; - - if (index != 0) { - while (JS7_ISDEC(*cp)) { - oldIndex = index; - c = JS7_UNDEC(*cp); - index = 10 * index + c; - cp++; - } - } - if (cp == end && - (oldIndex < (JSVAL_INT_MAX / 10) || - (oldIndex == (JSVAL_INT_MAX / 10) && - c <= (JSVAL_INT_MAX % 10)))) { - if (negative) - index = 0 - index; - id = INT_TO_JSID((jsint)index); - } - return id; -} - -static JSBool -HidePropertyName(JSContext *cx, jsid *idp) -{ - jsid id; - JSAtom *atom, *hidden; - - id = *idp; - JS_ASSERT(JSID_IS_ATOM(id)); - - atom = JSID_TO_ATOM(id); - JS_ASSERT(!(atom->flags & ATOM_HIDDEN)); - JS_ASSERT(ATOM_IS_STRING(atom)); - - hidden = js_AtomizeString(cx, ATOM_TO_STRING(atom), ATOM_HIDDEN); - if (!hidden) - return JS_FALSE; - - /* - * Link hidden to unhidden atom to optimize call_enumerate -- this means - * the GC must mark a hidden atom's unhidden counterpart (see js_MarkAtom - * in jsgc.c). It uses the atom's entry.value member for this linkage. - */ - hidden->entry.value = atom; - *idp = ATOM_TO_JSID(hidden); - return JS_TRUE; -} - -JSScopeProperty * -js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid) -{ - if (!HidePropertyName(cx, &id)) - return NULL; - - flags |= SPROP_IS_HIDDEN; - return js_AddNativeProperty(cx, obj, id, getter, setter, slot, attrs, - flags, shortid); -} - -JSBool -js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - return HidePropertyName(cx, &id) && - js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_HIDDEN, - objp, propp); -} - -JSScopeProperty * -js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid) -{ - JSScope *scope; - JSScopeProperty *sprop; - - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (!scope) { - sprop = NULL; - } else { - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs, - flags, shortid); - } - JS_UNLOCK_OBJ(cx, obj); - return sprop; -} - -JSScopeProperty * -js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter) -{ - JSScope *scope; - - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (!scope) { - sprop = NULL; - } else { - sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask, - getter, setter); - if (sprop) { - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id, - sprop); - } - } - JS_UNLOCK_OBJ(cx, obj); - return sprop; -} - -JSBool -js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp) -{ - return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs, - 0, 0, propp); -} - -/* - * Backward compatibility requires allowing addProperty hooks to mutate the - * nominal initial value of a slot-full property, while GC safety wants that - * value to be stored before the call-out through the hook. Optimize to do - * both while saving cycles for classes that stub their addProperty hook. - */ -#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ - JS_BEGIN_MACRO \ - if ((clasp)->addProperty != JS_PropertyStub) { \ - jsval nominal_ = *(vp); \ - if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \ - cleanup; \ - } \ - if (*(vp) != nominal_) { \ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) \ - LOCKED_OBJ_SET_SLOT(obj, (sprop)->slot, *(vp)); \ - } \ - } \ - JS_END_MACRO - -JSBool -js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN shortid, JSProperty **propp) -{ - JSClass *clasp; - JSScope *scope; - JSScopeProperty *sprop; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - -#if JS_HAS_GETTER_SETTER - /* - * If defining a getter or setter, we must check for its counterpart and - * update the attributes and property ops. A getter or setter is really - * only half of a property. - */ - if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { - JSObject *pobj; - JSProperty *prop; - - /* - * If JS_THREADSAFE and id is found, js_LookupProperty returns with - * sprop non-null and pobj locked. If pobj == obj, the property is - * already in obj and obj has its own (mutable) scope. So if we are - * defining a getter whose setter was already defined, or vice versa, - * finish the job via js_ChangeScopePropertyAttributes, and refresh - * the property cache line for (obj, id) to map sprop. - */ - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - sprop = (JSScopeProperty *) prop; - if (sprop && - pobj == obj && - (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) { - sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop, - attrs, sprop->attrs, - (attrs & JSPROP_GETTER) - ? getter - : sprop->getter, - (attrs & JSPROP_SETTER) - ? setter - : sprop->setter); - - /* NB: obj == pobj, so we can share unlock code at the bottom. */ - if (!sprop) - goto bad; - goto out; - } - - if (prop) { - /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - } -#endif /* JS_HAS_GETTER_SETTER */ - - /* Lock if object locking is required by this implementation. */ - JS_LOCK_OBJ(cx, obj); - - /* Use the object's class getter and setter by default. */ - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (!getter) - getter = clasp->getProperty; - if (!setter) - setter = clasp->setProperty; - - /* Get obj's own scope if it has one, or create a new one for obj. */ - scope = js_GetMutableScope(cx, obj); - if (!scope) - goto bad; - - /* Add the property to scope, or replace an existing one of the same id. */ - if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) - attrs |= JSPROP_SHARED; - sprop = js_AddScopeProperty(cx, scope, id, getter, setter, - SPROP_INVALID_SLOT, attrs, flags, shortid); - if (!sprop) - goto bad; - - /* Store value before calling addProperty, in case the latter GC's. */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) - LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value); - - /* XXXbe called with lock held */ - ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, - js_RemoveScopeProperty(cx, scope, id); - goto bad); - -#if JS_HAS_GETTER_SETTER -out: -#endif - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); - if (propp) - *propp = (JSProperty *) sprop; - else - JS_UNLOCK_OBJ(cx, obj); - return JS_TRUE; - -bad: - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; -} - -/* - * Given pc pointing after a property accessing bytecode, return true if the - * access is "object-detecting" in the sense used by web scripts, e.g., when - * checking whether document.all is defined. - */ -static JSBool -Detecting(JSContext *cx, jsbytecode *pc) -{ - JSScript *script; - jsbytecode *endpc; - JSOp op; - JSAtom *atom; - - if (!cx->fp) - return JS_FALSE; - script = cx->fp->script; - for (endpc = script->code + script->length; pc < endpc; pc++) { - /* General case: a branch or equality op follows the access. */ - op = (JSOp) *pc; - if (js_CodeSpec[op].format & JOF_DETECTING) - return JS_TRUE; - - /* - * Special case #1: handle (document.all == null). Don't sweat about - * JS1.2's revision of the equality operators here. - */ - if (op == JSOP_NULL) { - if (++pc < endpc) - return *pc == JSOP_EQ || *pc == JSOP_NE; - break; - } - - /* - * Special case #2: handle (document.all == undefined). Don't worry - * about someone redefining undefined, which was added by Edition 3, - * so is read/write for backward compatibility. - */ - if (op == JSOP_NAME) { - atom = GET_ATOM(cx, script, pc); - if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && - (pc += js_CodeSpec[op].length) < endpc) { - op = (JSOp) *pc; - return op == JSOP_EQ || op == JSOP_NE || - op == JSOP_NEW_EQ || op == JSOP_NEW_NE; - } - break; - } - - /* At this point, anything but grouping means we're not detecting. */ - if (op != JSOP_GROUP) - break; - } - return JS_FALSE; -} - -JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp); -} - -JSBool -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp) -{ - JSObject *start, *obj2, *proto; - JSScope *scope; - JSScopeProperty *sprop; - JSClass *clasp; - JSResolveOp resolve; - JSResolvingKey key; - JSResolvingEntry *entry; - uint32 generation; - JSNewResolveOp newresolve; - jsbytecode *pc; - const JSCodeSpec *cs; - uint32 format; - JSBool ok; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - /* Search scopes starting with obj and following the prototype link. */ - start = obj; - for (;;) { - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - if (scope->object == obj) { - sprop = SCOPE_GET_PROPERTY(scope, id); - } else { - /* Shared prototype scope: try resolve before lookup. */ - sprop = NULL; - } - - /* Try obj's class resolve hook if id was not found in obj's scope. */ - if (!sprop) { - clasp = LOCKED_OBJ_GET_CLASS(obj); - resolve = clasp->resolve; - if (resolve != JS_ResolveStub) { - /* Avoid recursion on (obj, id) already being resolved on cx. */ - key.obj = obj; - key.id = id; - - /* - * Once we have successfully added an entry for (obj, key) to - * cx->resolvingTable, control must go through cleanup: before - * returning. But note that JS_DHASH_ADD may find an existing - * entry, in which case we bail to suppress runaway recursion. - */ - if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - if (!entry) { - /* Already resolving id in obj -- suppress recursion. */ - JS_UNLOCK_OBJ(cx, obj); - goto out; - } - generation = cx->resolvingTable->generation; - - /* Null *propp here so we can test it at cleanup: safely. */ - *propp = NULL; - - if (clasp->flags & JSCLASS_NEW_RESOLVE) { - newresolve = (JSNewResolveOp)resolve; - if (!(flags & JSRESOLVE_CLASSNAME) && - cx->fp && - (pc = cx->fp->pc)) { - cs = &js_CodeSpec[*pc]; - format = cs->format; - if ((format & JOF_MODEMASK) != JOF_NAME) - flags |= JSRESOLVE_QUALIFIED; - if ((format & JOF_ASSIGNING) || - (cx->fp->flags & JSFRAME_ASSIGNING)) { - flags |= JSRESOLVE_ASSIGNING; - } else { - pc += cs->length; - if (Detecting(cx, pc)) - flags |= JSRESOLVE_DETECTING; - } - if (format & JOF_DECLARING) - flags |= JSRESOLVE_DECLARING; - } - obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) - ? start - : NULL; - JS_UNLOCK_OBJ(cx, obj); - - /* Protect id and all atoms from a GC nested in resolve. */ - JS_KEEP_ATOMS(cx->runtime); - ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2); - JS_UNKEEP_ATOMS(cx->runtime); - if (!ok) - goto cleanup; - - JS_LOCK_OBJ(cx, obj); - if (obj2) { - /* Resolved: juggle locks and lookup id again. */ - if (obj2 != obj) { - JS_UNLOCK_OBJ(cx, obj); - JS_LOCK_OBJ(cx, obj2); - } - scope = OBJ_SCOPE(obj2); - if (!MAP_IS_NATIVE(&scope->map)) { - /* Whoops, newresolve handed back a foreign obj2. */ - JS_ASSERT(obj2 != obj); - JS_UNLOCK_OBJ(cx, obj2); - ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); - if (!ok || *propp) - goto cleanup; - JS_LOCK_OBJ(cx, obj2); - } else { - /* - * Require that obj2 have its own scope now, as we - * do for old-style resolve. If it doesn't, then - * id was not truly resolved, and we'll find it in - * the proto chain, or miss it if obj2's proto is - * not on obj's proto chain. That last case is a - * "too bad!" case. - */ - if (scope->object == obj2) - sprop = SCOPE_GET_PROPERTY(scope, id); - } - if (sprop) { - JS_ASSERT(obj2 == scope->object); - obj = obj2; - } else if (obj2 != obj) { - JS_UNLOCK_OBJ(cx, obj2); - JS_LOCK_OBJ(cx, obj); - } - } - } else { - /* - * Old resolve always requires id re-lookup if obj owns - * its scope after resolve returns. - */ - JS_UNLOCK_OBJ(cx, obj); - ok = resolve(cx, obj, ID_TO_VALUE(id)); - if (!ok) - goto cleanup; - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - JS_ASSERT(MAP_IS_NATIVE(&scope->map)); - if (scope->object == obj) - sprop = SCOPE_GET_PROPERTY(scope, id); - } - - cleanup: - js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation); - if (!ok || *propp) - return ok; - } - } - - if (sprop) { - JS_ASSERT(OBJ_SCOPE(obj) == scope); - *objp = scope->object; /* XXXbe hide in jsscope.[ch] */ - - *propp = (JSProperty *) sprop; - return JS_TRUE; - } - - proto = LOCKED_OBJ_GET_PROTO(obj); - JS_UNLOCK_OBJ(cx, obj); - if (!proto) - break; - if (!OBJ_IS_NATIVE(proto)) - return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp); - obj = proto; - } - -out: - *objp = NULL; - *propp = NULL; - return JS_TRUE; -} - -JS_FRIEND_API(JSBool) -js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp) -{ - JSRuntime *rt; - JSObject *obj, *pobj, *lastobj; - JSScopeProperty *sprop; - JSProperty *prop; - - rt = cx->runtime; - obj = cx->fp->scopeChain; - do { - /* Try the property cache and return immediately on cache hit. */ - if (OBJ_IS_NATIVE(obj)) { - JS_LOCK_OBJ(cx, obj); - PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); - if (sprop) { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - *objp = obj; - *pobjp = obj; - *propp = (JSProperty *) sprop; - return JS_TRUE; - } - JS_UNLOCK_OBJ(cx, obj); - } - - /* If cache miss, take the slow path. */ - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - if (OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *) prop; - PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop); - } - *objp = obj; - *pobjp = pobj; - *propp = prop; - return JS_TRUE; - } - lastobj = obj; - } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); - - *objp = lastobj; - *pobjp = NULL; - *propp = NULL; - return JS_TRUE; -} - -JSObject * -js_FindIdentifierBase(JSContext *cx, jsid id) -{ - JSObject *obj, *pobj; - JSProperty *prop; - - /* - * Look for id's property along the "with" statement chain and the - * statically-linked scope chain. - */ - if (!js_FindProperty(cx, id, &obj, &pobj, &prop)) - return NULL; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - return obj; - } - - /* - * Use the top-level scope from the scope chain, which won't end in the - * same scope as cx->globalObject for cross-context function calls. - */ - JS_ASSERT(obj); - - /* - * Property not found. Give a strict warning if binding an undeclared - * top-level variable. - */ - if (JS_HAS_STRICT_OPTION(cx)) { - JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id)); - if (!JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_UNDECLARED_VAR, - JS_GetStringBytes(str))) { - return NULL; - } - } - return obj; -} - -JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, - JSScopeProperty *sprop, jsval *vp) -{ - JSScope *scope; - uint32 slot; - int32 sample; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJ_IS_NATIVE(pobj)); - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj)); - scope = OBJ_SCOPE(pobj); - JS_ASSERT(scope->object == pobj); - - slot = sprop->slot; - *vp = (slot != SPROP_INVALID_SLOT) - ? LOCKED_OBJ_GET_SLOT(pobj, slot) - : JSVAL_VOID; - if (SPROP_HAS_STUB_GETTER(sprop)) - return JS_TRUE; - - sample = cx->runtime->propertyRemovals; - JS_UNLOCK_SCOPE(cx, scope); - JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); - ok = SPROP_GET(cx, sprop, obj, pobj, vp); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - - JS_LOCK_SCOPE(cx, scope); - JS_ASSERT(scope->object == pobj); - if (SLOT_IN_SCOPE(slot, scope) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { - LOCKED_OBJ_SET_SLOT(pobj, slot, *vp); - } - - return JS_TRUE; -} - -JSBool -js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) -{ - JSScope *scope; - uint32 slot; - jsval pval; - int32 sample; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - scope = OBJ_SCOPE(obj); - JS_ASSERT(scope->object == obj); - - slot = sprop->slot; - if (slot != SPROP_INVALID_SLOT) { - pval = LOCKED_OBJ_GET_SLOT(obj, slot); - - /* If sprop has a stub setter, keep scope locked and just store *vp. */ - if (SPROP_HAS_STUB_SETTER(sprop)) - goto set_slot; - } else { - /* - * Allow API consumers to create shared properties with stub setters. - * Such properties lack value storage, so setting them is like writing - * to /dev/null. - */ - if (SPROP_HAS_STUB_SETTER(sprop)) - return JS_TRUE; - pval = JSVAL_VOID; - } - - sample = cx->runtime->propertyRemovals; - JS_UNLOCK_SCOPE(cx, scope); - JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); - ok = SPROP_SET(cx, sprop, obj, obj, vp); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!ok) - return JS_FALSE; - - JS_LOCK_SCOPE(cx, scope); - JS_ASSERT(scope->object == obj); - if (SLOT_IN_SCOPE(slot, scope) && - (JS_LIKELY(cx->runtime->propertyRemovals == sample) || - SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { - set_slot: - GC_POKE(cx, pval); - LOCKED_OBJ_SET_SLOT(obj, slot, *vp); - } - - return JS_TRUE; -} - -JSBool -js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *obj2; - JSProperty *prop; - JSScopeProperty *sprop; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - if (!js_LookupProperty(cx, obj, id, &obj2, &prop)) - return JS_FALSE; - if (!prop) { - jsbytecode *pc; - - *vp = JSVAL_VOID; - - if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) - return JS_FALSE; - - /* - * Give a strict warning if foo.bar is evaluated by a script for an - * object foo with no property named 'bar'. - */ - if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) { - JSOp op; - uintN flags; - JSString *str; - - op = *pc; - if (op == JSOP_GETXPROP || op == JSOP_GETXELEM) { - flags = JSREPORT_ERROR; - } else { - if (!JS_HAS_STRICT_OPTION(cx) || - (op != JSOP_GETPROP && op != JSOP_GETELEM)) { - return JS_TRUE; - } - - /* - * XXX do not warn about missing __iterator__ as the function - * may be called from JS_GetMethodById. See bug 355145. - */ - if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) - return JS_TRUE; - - /* Kludge to allow (typeof foo == "undefined") tests. */ - JS_ASSERT(cx->fp->script); - pc += js_CodeSpec[op].length; - if (Detecting(cx, pc)) - return JS_TRUE; - - flags = JSREPORT_WARNING | JSREPORT_STRICT; - } - - /* Ok, bad undefined property reference: whine about it. */ - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (!str || - !JS_ReportErrorFlagsAndNumber(cx, flags, - js_GetErrorMessage, NULL, - JSMSG_UNDEFINED_PROP, - JS_GetStringBytes(str))) { - return JS_FALSE; - } - } - return JS_TRUE; - } - - if (!OBJ_IS_NATIVE(obj2)) { - OBJ_DROP_PROPERTY(cx, obj2, prop); - return OBJ_GET_PROPERTY(cx, obj2, id, vp); - } - - sprop = (JSScopeProperty *) prop; - if (!js_NativeGet(cx, obj, obj2, sprop, vp)) - return JS_FALSE; - - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop); - JS_UNLOCK_OBJ(cx, obj2); - return JS_TRUE; -} - -JSBool -js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *pobj; - JSProperty *prop; - JSScopeProperty *sprop; - JSScope *scope; - uintN attrs, flags; - intN shortid; - JSClass *clasp; - JSPropertyOp getter, setter; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - - if (prop && !OBJ_IS_NATIVE(pobj)) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - sprop = (JSScopeProperty *) prop; - - /* - * Now either sprop is null, meaning id was not found in obj or one of its - * prototypes; or sprop is non-null, meaning id was found in pobj's scope. - * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop - * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return - * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE - * because it is cheaper). - */ - attrs = JSPROP_ENUMERATE; - flags = 0; - shortid = 0; - clasp = OBJ_GET_CLASS(cx, obj); - getter = clasp->getProperty; - setter = clasp->setProperty; - - if (sprop) { - /* - * Set scope for use below. It was locked by js_LookupProperty, and - * we know pobj owns it (i.e., scope->object == pobj). Therefore we - * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope). - */ - scope = OBJ_SCOPE(pobj); - - attrs = sprop->attrs; - if ((attrs & JSPROP_READONLY) || - (SCOPE_IS_SEALED(scope) && pobj == obj)) { - JS_UNLOCK_SCOPE(cx, scope); - - /* - * Here, we'll either return true or goto read_only_error, which - * reports a strict warning or throws an error. So we redefine - * the |flags| local variable to be JSREPORT_* flags to pass to - * JS_ReportErrorFlagsAndNumberUC at label read_only_error. We - * must likewise re-task flags further below for the other 'goto - * read_only_error;' case. - */ - flags = JSREPORT_ERROR; - if ((attrs & JSPROP_READONLY) && JS_VERSION_IS_ECMA(cx)) { - if (!JS_HAS_STRICT_OPTION(cx)) { - /* Just return true per ECMA if not in strict mode. */ - return JS_TRUE; - } - - /* Strict mode: report a read-only strict warning. */ - flags = JSREPORT_STRICT | JSREPORT_WARNING; - } - goto read_only_error; - } - - if (pobj != obj) { - /* - * We found id in a prototype object: prepare to share or shadow. - * NB: Thanks to the immutable, garbage-collected property tree - * maintained by jsscope.c in cx->runtime, we needn't worry about - * sprop going away behind our back after we've unlocked scope. - */ - JS_UNLOCK_SCOPE(cx, scope); - - /* Don't clone a shared prototype property. */ - if (attrs & JSPROP_SHARED) { - if (SPROP_HAS_STUB_SETTER(sprop) && - !(sprop->attrs & JSPROP_GETTER)) { - return JS_TRUE; - } - return SPROP_SET(cx, sprop, obj, pobj, vp); - } - - /* Restore attrs to the ECMA default for new properties. */ - attrs = JSPROP_ENUMERATE; - - /* - * Preserve the shortid, getter, and setter when shadowing any - * property that has a shortid. An old API convention requires - * that the property's getter and setter functions receive the - * shortid, not id, when they are called on the shadow we are - * about to create in obj's scope. - */ - if (sprop->flags & SPROP_HAS_SHORTID) { - flags = SPROP_HAS_SHORTID; - shortid = sprop->shortid; - getter = sprop->getter; - setter = sprop->setter; - } - - /* - * Forget we found the proto-property now that we've copied any - * needed member values. - */ - sprop = NULL; - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - } else { - scope = NULL; -#endif - } - - if (!sprop) { - if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { - flags = JSREPORT_ERROR; - goto read_only_error; - } - - /* Find or make a property descriptor with the right heritage. */ - JS_LOCK_OBJ(cx, obj); - scope = js_GetMutableScope(cx, obj); - if (!scope) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) - attrs |= JSPROP_SHARED; - sprop = js_AddScopeProperty(cx, scope, id, getter, setter, - SPROP_INVALID_SLOT, attrs, flags, shortid); - if (!sprop) { - JS_UNLOCK_SCOPE(cx, scope); - return JS_FALSE; - } - - /* - * Initialize the new property value (passed to setter) to undefined. - * Note that we store before calling addProperty, to match the order - * in js_DefineNativeProperty. - */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) - LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID); - - /* XXXbe called with obj locked */ - ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, vp, - js_RemoveScopeProperty(cx, scope, id); - JS_UNLOCK_SCOPE(cx, scope); - return JS_FALSE); - - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop); - } - - if (!js_NativeSet(cx, obj, sprop, vp)) - return JS_FALSE; - JS_UNLOCK_SCOPE(cx, scope); - return JS_TRUE; - - read_only_error: { - JSString *str = js_DecompileValueGenerator(cx, - JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), - NULL); - if (!str) - return JS_FALSE; - return JS_ReportErrorFlagsAndNumberUC(cx, flags, js_GetErrorMessage, - NULL, JSMSG_READ_ONLY, - JS_GetStringChars(str)); - } -} - -JSBool -js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool noprop, ok; - JSScopeProperty *sprop; - - noprop = !prop; - if (noprop) { - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) - return JS_FALSE; - if (!prop) { - *attrsp = 0; - return JS_TRUE; - } - if (!OBJ_IS_NATIVE(obj)) { - ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; - } - } - sprop = (JSScopeProperty *)prop; - *attrsp = sprop->attrs; - if (noprop) - OBJ_DROP_PROPERTY(cx, obj, prop); - return JS_TRUE; -} - -JSBool -js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool noprop, ok; - JSScopeProperty *sprop; - - noprop = !prop; - if (noprop) { - if (!js_LookupProperty(cx, obj, id, &obj, &prop)) - return JS_FALSE; - if (!prop) - return JS_TRUE; - if (!OBJ_IS_NATIVE(obj)) { - ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; - } - } - sprop = (JSScopeProperty *)prop; - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, *attrsp, 0, - sprop->getter, sprop->setter); - if (noprop) - OBJ_DROP_PROPERTY(cx, obj, prop); - return (sprop != NULL); -} - -JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - JSObject *proto; - JSProperty *prop; - JSScopeProperty *sprop; - JSString *str; - JSScope *scope; - JSBool ok; - - *rval = JSVAL_TRUE; - - /* - * Handle old bug that took empty string as zero index. Also convert - * string indices to integers if appropriate. - */ - CHECK_FOR_STRING_INDEX(id); - - if (!js_LookupProperty(cx, obj, id, &proto, &prop)) - return JS_FALSE; - if (!prop || proto != obj) { - /* - * If the property was found in a native prototype, check whether it's - * shared and permanent. Such a property stands for direct properties - * in all delegating objects, matching ECMA semantics without bloating - * each delegating object. - */ - if (prop) { - if (OBJ_IS_NATIVE(proto)) { - sprop = (JSScopeProperty *)prop; - if (SPROP_IS_SHARED_PERMANENT(sprop)) - *rval = JSVAL_FALSE; - } - OBJ_DROP_PROPERTY(cx, proto, prop); - if (*rval == JSVAL_FALSE) - return JS_TRUE; - } - - /* - * If no property, or the property comes unshared or impermanent from - * a prototype, call the class's delProperty hook, passing rval as the - * result parameter. - */ - return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id), - rval); - } - - sprop = (JSScopeProperty *)prop; - if (sprop->attrs & JSPROP_PERMANENT) { - OBJ_DROP_PROPERTY(cx, obj, prop); - if (JS_VERSION_IS_ECMA(cx)) { - *rval = JSVAL_FALSE; - return JS_TRUE; - } - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, - ID_TO_VALUE(id), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_PERMANENT, JS_GetStringBytes(str)); - } - return JS_FALSE; - } - - /* XXXbe called with obj locked */ - if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop), - rval)) { - OBJ_DROP_PROPERTY(cx, obj, prop); - return JS_FALSE; - } - - scope = OBJ_SCOPE(obj); - if (SPROP_HAS_VALID_SLOT(sprop, scope)) - GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot)); - - PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL); - ok = js_RemoveScopeProperty(cx, scope, id); - OBJ_DROP_PROPERTY(cx, obj, prop); - return ok; -} - -JSBool -js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - jsval v, save; - JSString *str; - - v = save = OBJECT_TO_JSVAL(obj); - switch (hint) { - case JSTYPE_STRING: - /* - * Propagate the exception if js_TryMethod finds an appropriate - * method, and calling that method returned failure. - */ - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, - &v)) { - return JS_FALSE; - } - - if (!JSVAL_IS_PRIMITIVE(v)) { - if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) - return JS_FALSE; - } - break; - - default: - if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v)) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - JSType type = JS_TypeOfValue(cx, v); - if (type == hint || - (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) { - goto out; - } - if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, - NULL, &v)) { - return JS_FALSE; - } - } - break; - } - if (!JSVAL_IS_PRIMITIVE(v)) { - /* Avoid recursive death through js_DecompileValueGenerator. */ - if (hint == JSTYPE_STRING) { - str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name); - if (!str) - return JS_FALSE; - } else { - str = NULL; - } - *vp = OBJECT_TO_JSVAL(obj); - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, save, str); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_CONVERT_TO, - JS_GetStringBytes(str), - (hint == JSTYPE_VOID) - ? "primitive type" - : js_type_strs[hint]); - } - return JS_FALSE; - } -out: - *vp = v; - return JS_TRUE; -} - -JSIdArray * -js_NewIdArray(JSContext *cx, jsint length) -{ - JSIdArray *ida; - - ida = (JSIdArray *) - JS_malloc(cx, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); - if (ida) - ida->length = length; - return ida; -} - -JSIdArray * -js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length) -{ - JSIdArray *rida; - - rida = (JSIdArray *) - JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval)); - if (!rida) - JS_DestroyIdArray(cx, ida); - else - rida->length = length; - return rida; -} - -/* Private type used to iterate over all properties of a native JS object */ -struct JSNativeIteratorState { - jsint next_index; /* index into jsid array */ - JSIdArray *ida; /* all property ids in enumeration */ - JSNativeIteratorState *next; /* double-linked list support */ - JSNativeIteratorState **prevp; -}; - -/* - * This function is used to enumerate the properties of native JSObjects - * and those host objects that do not define a JSNewEnumerateOp-style iterator - * function. - */ -JSBool -js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSRuntime *rt; - JSObject *proto; - JSClass *clasp; - JSEnumerateOp enumerate; - JSScopeProperty *sprop, *lastProp; - jsint i, length; - JSScope *scope; - JSIdArray *ida; - JSNativeIteratorState *state; - - rt = cx->runtime; - clasp = OBJ_GET_CLASS(cx, obj); - enumerate = clasp->enumerate; - if (clasp->flags & JSCLASS_NEW_ENUMERATE) - return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (!enumerate(cx, obj)) - return JS_FALSE; - length = 0; - - /* - * The set of all property ids is pre-computed when the iterator - * is initialized so as to avoid problems with properties being - * deleted during the iteration. - */ - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - - /* - * If this object shares a scope with its prototype, don't enumerate - * its properties. Otherwise they will be enumerated a second time - * when the prototype object is enumerated. - */ - proto = OBJ_GET_PROTO(cx, obj); - if (proto && scope == OBJ_SCOPE(proto)) { - ida = js_NewIdArray(cx, 0); - if (!ida) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - } else { - /* Object has a private scope; Enumerate all props in scope. */ - for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop; - sprop = sprop->parent) { - if (( -#ifdef DUMP_CALL_TABLE - (cx->options & JSOPTION_LOGCALL_TOSOURCE) || -#endif - (sprop->attrs & JSPROP_ENUMERATE)) && - !(sprop->flags & SPROP_IS_ALIAS) && - (!SCOPE_HAD_MIDDLE_DELETE(scope) || - SCOPE_HAS_PROPERTY(scope, sprop))) { - length++; - } - } - ida = js_NewIdArray(cx, length); - if (!ida) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - i = length; - for (sprop = lastProp; sprop; sprop = sprop->parent) { - if (( -#ifdef DUMP_CALL_TABLE - (cx->options & JSOPTION_LOGCALL_TOSOURCE) || -#endif - (sprop->attrs & JSPROP_ENUMERATE)) && - !(sprop->flags & SPROP_IS_ALIAS) && - (!SCOPE_HAD_MIDDLE_DELETE(scope) || - SCOPE_HAS_PROPERTY(scope, sprop))) { - JS_ASSERT(i > 0); - ida->vector[--i] = sprop->id; - } - } - } - JS_UNLOCK_OBJ(cx, obj); - - state = (JSNativeIteratorState *) - JS_malloc(cx, sizeof(JSNativeIteratorState)); - if (!state) { - JS_DestroyIdArray(cx, ida); - return JS_FALSE; - } - state->ida = ida; - state->next_index = 0; - - JS_LOCK_RUNTIME(rt); - state->next = rt->nativeIteratorStates; - if (state->next) - state->next->prevp = &state->next; - state->prevp = &rt->nativeIteratorStates; - *state->prevp = state; - JS_UNLOCK_RUNTIME(rt); - - *statep = PRIVATE_TO_JSVAL(state); - if (idp) - *idp = INT_TO_JSVAL(length); - break; - - case JSENUMERATE_NEXT: - state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); - ida = state->ida; - length = ida->length; - if (state->next_index != length) { - *idp = ida->vector[state->next_index++]; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep); - - JS_LOCK_RUNTIME(rt); - JS_ASSERT(rt->nativeIteratorStates); - JS_ASSERT(*state->prevp == state); - if (state->next) { - JS_ASSERT(state->next->prevp == &state->next); - state->next->prevp = state->prevp; - } - *state->prevp = state->next; - JS_UNLOCK_RUNTIME(rt); - - JS_DestroyIdArray(cx, state->ida); - JS_free(cx, state); - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -void -js_MarkNativeIteratorStates(JSContext *cx) -{ - JSNativeIteratorState *state; - jsid *cursor, *end, id; - - state = cx->runtime->nativeIteratorStates; - if (!state) - return; - - do { - JS_ASSERT(*state->prevp == state); - cursor = state->ida->vector; - end = cursor + state->ida->length; - for (; cursor != end; ++cursor) { - id = *cursor; - MARK_ID(cx, id); - } - } while ((state = state->next) != NULL); -} - -JSBool -js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp) -{ - JSBool writing; - JSObject *pobj; - JSProperty *prop; - JSClass *clasp; - JSScopeProperty *sprop; - JSCheckAccessOp check; - - writing = (mode & JSACC_WRITE) != 0; - switch (mode & JSACC_TYPEMASK) { - case JSACC_PROTO: - pobj = obj; - if (!writing) - *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO); - *attrsp = JSPROP_PERMANENT; - break; - - case JSACC_PARENT: - JS_ASSERT(!writing); - pobj = obj; - *vp = OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT); - *attrsp = JSPROP_READONLY | JSPROP_PERMANENT; - break; - - default: - if (!js_LookupProperty(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (!prop) { - if (!writing) - *vp = JSVAL_VOID; - *attrsp = 0; - clasp = OBJ_GET_CLASS(cx, obj); - return !clasp->checkAccess || - clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp); - } - if (!OBJ_IS_NATIVE(pobj)) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp); - } - - sprop = (JSScopeProperty *)prop; - *attrsp = sprop->attrs; - if (!writing) { - *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) - ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) - : JSVAL_VOID; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } - - /* - * If obj's class has a stub (null) checkAccess hook, use the per-runtime - * checkObjectAccess callback, if configured. - * - * We don't want to require all classes to supply a checkAccess hook; we - * need that hook only for certain classes used when precompiling scripts - * and functions ("brutal sharing"). But for general safety of built-in - * magic properties such as __proto__ and __parent__, we route all access - * checks, even for classes that stub out checkAccess, through the global - * checkObjectAccess hook. This covers precompilation-based sharing and - * (possibly unintended) runtime sharing across trust boundaries. - */ - clasp = OBJ_GET_CLASS(cx, pobj); - check = clasp->checkAccess; - if (!check) - check = cx->runtime->checkObjectAccess; - return !check || check(cx, pobj, ID_TO_VALUE(id), mode, vp); -} - -#ifdef JS_THREADSAFE -void -js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop) -{ - JS_UNLOCK_OBJ(cx, obj); -} -#endif - -static void -ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags) -{ - /* - * The decompiler may need to access the args of the function in - * progress rather than the one we had hoped to call. - * So we switch the cx->fp to the frame below us. We stick the - * current frame in the dormantFrameChain to protect it from gc. - */ - - JSStackFrame *fp = cx->fp; - if (fp->down) { - JS_ASSERT(!fp->dormantNext); - fp->dormantNext = cx->dormantFrameChain; - cx->dormantFrameChain = fp; - cx->fp = fp->down; - } - - js_ReportIsNotFunction(cx, vp, flags); - - if (fp->down) { - JS_ASSERT(cx->dormantFrameChain == fp); - cx->dormantFrameChain = fp->dormantNext; - fp->dormantNext = NULL; - cx->fp = fp; - } -} - -#ifdef NARCISSUS -static JSBool -GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval) -{ - JSObject *tmp; - jsval xcval; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .ExecutionContextAtom), - &xcval)) { - return JS_FALSE; - } - if (JSVAL_IS_PRIMITIVE(xcval)) { - JS_ReportError(cx, "invalid ExecutionContext in global object"); - return JS_FALSE; - } - if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval), - ATOM_TO_JSID(cx->runtime->atomState.currentAtom), - rval)) { - return JS_FALSE; - } - return JS_TRUE; -} -#endif - -JSBool -js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); - if (!clasp->call) { -#ifdef NARCISSUS - JSObject *callee, *args; - jsval fval, nargv[3]; - JSBool ok; - - callee = JSVAL_TO_OBJECT(argv[-2]); - if (!OBJ_GET_PROPERTY(cx, callee, - ATOM_TO_JSID(cx->runtime->atomState.callAtom), - &fval)) { - return JS_FALSE; - } - if (VALUE_IS_FUNCTION(cx, fval)) { - if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) - return JS_FALSE; - args = js_GetArgsObject(cx, cx->fp); - if (!args) - return JS_FALSE; - nargv[0] = OBJECT_TO_JSVAL(obj); - nargv[1] = OBJECT_TO_JSVAL(args); - return js_InternalCall(cx, callee, fval, 3, nargv, rval); - } - if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) { - argv[-2] = fval; - ok = js_Call(cx, obj, argc, argv, rval); - argv[-2] = OBJECT_TO_JSVAL(callee); - return ok; - } -#endif - ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR); - return JS_FALSE; - } - return clasp->call(cx, obj, argc, argv, rval); -} - -JSBool -js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSClass *clasp; - - clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2])); - if (!clasp->construct) { -#ifdef NARCISSUS - JSObject *callee, *args; - jsval cval, nargv[2]; - JSBool ok; - - callee = JSVAL_TO_OBJECT(argv[-2]); - if (!OBJ_GET_PROPERTY(cx, callee, - ATOM_TO_JSID(cx->runtime->atomState - .constructAtom), - &cval)) { - return JS_FALSE; - } - if (VALUE_IS_FUNCTION(cx, cval)) { - if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) - return JS_FALSE; - args = js_GetArgsObject(cx, cx->fp); - if (!args) - return JS_FALSE; - nargv[0] = OBJECT_TO_JSVAL(args); - return js_InternalCall(cx, callee, cval, 2, nargv, rval); - } - if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) { - argv[-2] = cval; - ok = js_Call(cx, obj, argc, argv, rval); - argv[-2] = OBJECT_TO_JSVAL(callee); - return ok; - } -#endif - ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT); - return JS_FALSE; - } - return clasp->construct(cx, obj, argc, argv, rval); -} - -JSBool -js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSClass *clasp; - JSString *str; - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->hasInstance) - return clasp->hasInstance(cx, obj, v, bp); -#ifdef NARCISSUS - { - jsval fval, rval; - - if (!OBJ_GET_PROPERTY(cx, obj, - ATOM_TO_JSID(cx->runtime->atomState - .hasInstanceAtom), - &fval)) { - return JS_FALSE; - } - if (VALUE_IS_FUNCTION(cx, fval)) { - return js_InternalCall(cx, obj, fval, 1, &v, &rval) && - js_ValueToBoolean(cx, rval, bp); - } - } -#endif - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, - OBJECT_TO_JSVAL(obj), NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_INSTANCEOF_RHS, - JS_GetStringBytes(str)); - } - return JS_FALSE; -} - -JSBool -js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSObject *obj2; - - *bp = JS_FALSE; - if (JSVAL_IS_PRIMITIVE(v)) - return JS_TRUE; - obj2 = JSVAL_TO_OBJECT(v); - while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) { - if (obj2 == obj) { - *bp = JS_TRUE; - break; - } - } - return JS_TRUE; -} - -JSBool -js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, - JSObject **protop) -{ - jsval v; - JSObject *ctor; - - if (!js_FindClassObject(cx, scope, id, &v)) - return JS_FALSE; - if (VALUE_IS_FUNCTION(cx, v)) { - ctor = JSVAL_TO_OBJECT(v); - if (!OBJ_GET_PROPERTY(cx, ctor, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - &v)) { - return JS_FALSE; - } - if (!JSVAL_IS_PRIMITIVE(v)) { - /* - * Set the newborn root in case v is otherwise unreferenced. - * It's ok to overwrite newborn roots here, since the getter - * called just above could have. Unlike the common GC rooting - * model, our callers do not have to protect protop thanks to - * this newborn root, since they all immediately create a new - * instance that delegates to this object, or just query the - * prototype for its class. - */ - cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(v); - } - } - *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL; - return JS_TRUE; -} - -/* - * For shared precompilation of function objects, we support cloning on entry - * to an execution context in which the function declaration or expression - * should be processed as if it were not precompiled, where the precompiled - * function's scope chain does not match the execution context's. The cloned - * function object carries its execution-context scope in its parent slot; it - * links to the precompiled function (the "clone-parent") via its proto slot. - * - * Note that this prototype-based delegation leaves an unchecked access path - * from the clone to the clone-parent's 'constructor' property. If the clone - * lives in a less privileged or shared scope than the clone-parent, this is - * a security hole, a sharing hazard, or both. Therefore we check all such - * accesses with the following getter/setter pair, which we use when defining - * 'constructor' in f.prototype for all function objects f. - */ -static JSBool -CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSAtom *atom; - uintN attrs; - - atom = cx->runtime->atomState.constructorAtom; - JS_ASSERT(id == ATOM_KEY(atom)); - return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, - vp, &attrs); -} - -static JSBool -CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSAtom *atom; - uintN attrs; - - atom = cx->runtime->atomState.constructorAtom; - JS_ASSERT(id == ATOM_KEY(atom)); - return OBJ_CHECK_ACCESS(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, - vp, &attrs); -} - -JSBool -js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, - uintN attrs) -{ - /* - * Use the given attributes for the prototype property of the constructor, - * as user-defined constructors have a DontDelete prototype (which may be - * reset), while native or "system" constructors have DontEnum | ReadOnly | - * DontDelete. - */ - if (!OBJ_DEFINE_PROPERTY(cx, ctor, - ATOM_TO_JSID(cx->runtime->atomState - .classPrototypeAtom), - OBJECT_TO_JSVAL(proto), - JS_PropertyStub, JS_PropertyStub, - attrs, NULL)) { - return JS_FALSE; - } - - /* - * ECMA says that Object.prototype.constructor, or f.prototype.constructor - * for a user-defined function f, is DontEnum. - */ - return OBJ_DEFINE_PROPERTY(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState - .constructorAtom), - OBJECT_TO_JSVAL(ctor), - CheckCtorGetAccess, CheckCtorSetAccess, - 0, NULL); -} - -JSBool -js_ValueToObject(JSContext *cx, jsval v, JSObject **objp) -{ - JSObject *obj; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - obj = NULL; - } else if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) - return JS_FALSE; - if (JSVAL_IS_OBJECT(v)) - obj = JSVAL_TO_OBJECT(v); - } else { - if (JSVAL_IS_STRING(v)) { - obj = js_StringToObject(cx, JSVAL_TO_STRING(v)); - } else if (JSVAL_IS_INT(v)) { - obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v)); - } else if (JSVAL_IS_DOUBLE(v)) { - obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(v)); - obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v)); - } - if (!obj) - return JS_FALSE; - } - *objp = obj; - return JS_TRUE; -} - -JSObject * -js_ValueToNonNullObject(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - - if (!js_ValueToObject(cx, v, &obj)) - return NULL; - if (!obj) { - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_PROPERTIES, JS_GetStringBytes(str)); - } - } - return obj; -} - -JSBool -js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval) -{ - jsval argv[1]; - - argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]); - return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv, - rval); -} - -JSBool -js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN argc, jsval *argv, jsval *rval) -{ - JSErrorReporter older; - jsid id; - jsval fval; - JSBool ok; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - /* - * Report failure only if an appropriate method was found, and calling it - * returned failure. We propagate failure in this case to make exceptions - * behave properly. - */ - older = JS_SetErrorReporter(cx, NULL); - id = ATOM_TO_JSID(atom); - fval = JSVAL_VOID; -#if JS_HAS_XML_SUPPORT - if (OBJECT_IS_XML(cx, obj)) { - JSXMLObjectOps *ops; - - ops = (JSXMLObjectOps *) obj->map->ops; - obj = ops->getMethod(cx, obj, id, &fval); - ok = (obj != NULL); - } else -#endif - { - ok = OBJ_GET_PROPERTY(cx, obj, id, &fval); - } - if (!ok) - JS_ClearPendingException(cx); - JS_SetErrorReporter(cx, older); - - return JSVAL_IS_PRIMITIVE(fval) || - js_InternalCall(cx, obj, fval, argc, argv, rval); -} - -#if JS_HAS_XDR - -JSBool -js_XDRObject(JSXDRState *xdr, JSObject **objp) -{ - JSContext *cx; - JSAtom *atom; - JSClass *clasp; - uint32 classId, classDef; - JSProtoKey protoKey; - jsid classKey; - JSObject *proto; - - cx = xdr->cx; - atom = NULL; - if (xdr->mode == JSXDR_ENCODE) { - clasp = OBJ_GET_CLASS(cx, *objp); - classId = JS_XDRFindClassIdByName(xdr, clasp->name); - classDef = !classId; - if (classDef) { - if (!JS_XDRRegisterClass(xdr, clasp, &classId)) - return JS_FALSE; - protoKey = JSCLASS_CACHED_PROTO_KEY(clasp); - if (protoKey != JSProto_Null) { - classDef |= (protoKey << 1); - } else { - atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); - if (!atom) - return JS_FALSE; - } - } - } else { - clasp = NULL; /* quell GCC overwarning */ - classDef = 0; - } - - /* - * XDR a flag word, which could be 0 for a class use, in which case no - * name follows, only the id in xdr's class registry; 1 for a class def, - * in which case the flag word is followed by the class name transferred - * from or to atom; or a value greater than 1, an odd number that when - * divided by two yields the JSProtoKey for class. In the last case, as - * in the 0 classDef case, no name is transferred via atom. - */ - if (!JS_XDRUint32(xdr, &classDef)) - return JS_FALSE; - if (classDef == 1 && !js_XDRCStringAtom(xdr, &atom)) - return JS_FALSE; - - if (!JS_XDRUint32(xdr, &classId)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - if (classDef) { - /* NB: we know that JSProto_Null is 0 here, for backward compat. */ - protoKey = classDef >> 1; - classKey = (protoKey != JSProto_Null) - ? INT_TO_JSID(protoKey) - : ATOM_TO_JSID(atom); - if (!js_GetClassPrototype(cx, NULL, classKey, &proto)) - return JS_FALSE; - clasp = OBJ_GET_CLASS(cx, proto); - if (!JS_XDRRegisterClass(xdr, clasp, &classId)) - return JS_FALSE; - } else { - clasp = JS_XDRFindClassById(xdr, classId); - if (!clasp) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_FIND_CLASS, numBuf); - return JS_FALSE; - } - } - } - - if (!clasp->xdrObject) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_XDR_CLASS, clasp->name); - return JS_FALSE; - } - return clasp->xdrObject(xdr, objp); -} - -#endif /* JS_HAS_XDR */ - -#ifdef DEBUG_brendan - -#include -#include - -uint32 js_entry_count_max; -uint32 js_entry_count_sum; -double js_entry_count_sqsum; -uint32 js_entry_count_hist[11]; - -static void -MeterEntryCount(uintN count) -{ - if (count) { - js_entry_count_sum += count; - js_entry_count_sqsum += (double)count * count; - if (count > js_entry_count_max) - js_entry_count_max = count; - } - js_entry_count_hist[JS_MIN(count, 10)]++; -} - -#define DEBUG_scopemeters -#endif /* DEBUG_brendan */ - -#ifdef DEBUG_scopemeters -void -js_DumpScopeMeters(JSRuntime *rt) -{ - static FILE *logfp; - if (!logfp) - logfp = fopen("/tmp/scope.stats", "a"); - - { - double mean = 0., var = 0., sigma = 0.; - double nscopes = rt->liveScopes; - double nentrys = js_entry_count_sum; - if (nscopes > 0 && nentrys >= 0) { - mean = nentrys / nscopes; - var = nscopes * js_entry_count_sqsum - nentrys * nentrys; - if (var < 0.0 || nscopes <= 1) - var = 0.0; - else - var /= nscopes * (nscopes - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.) ? sqrt(var) : 0.; - } - - fprintf(logfp, - "scopes %g entries %g mean %g sigma %g max %u", - nscopes, nentrys, mean, sigma, js_entry_count_max); - } - - fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n", - js_entry_count_hist[0], js_entry_count_hist[1], - js_entry_count_hist[2], js_entry_count_hist[3], - js_entry_count_hist[4], js_entry_count_hist[5], - js_entry_count_hist[6], js_entry_count_hist[7], - js_entry_count_hist[8], js_entry_count_hist[9], - js_entry_count_hist[10]); - js_entry_count_sum = js_entry_count_max = 0; - js_entry_count_sqsum = 0; - memset(js_entry_count_hist, 0, sizeof js_entry_count_hist); - fflush(logfp); -} -#endif - -uint32 -js_Mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSScope *scope; - JSScopeProperty *sprop; - JSClass *clasp; - - JS_ASSERT(OBJ_IS_NATIVE(obj)); - scope = OBJ_SCOPE(obj); -#ifdef DEBUG_brendan - if (scope->object == obj) - MeterEntryCount(scope->entryCount); -#endif - - JS_ASSERT(!SCOPE_LAST_PROP(scope) || - SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope))); - - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) - continue; - MARK_SCOPE_PROPERTY(cx, sprop); - } - - /* No one runs while the GC is running, so we can use LOCKED_... here. */ - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (clasp->mark) - (void) clasp->mark(cx, obj, NULL); - - if (scope->object != obj) { - /* - * An unmutated object that shares a prototype's scope. We can't tell - * how many slots are allocated and in use at obj->slots by looking at - * scope, so we get obj->slots' length from its -1'st element. - */ - return (uint32) obj->slots[-1]; - } - return JS_MIN(scope->map.freeslot, scope->map.nslots); -} - -void -js_Clear(JSContext *cx, JSObject *obj) -{ - JSScope *scope; - JSRuntime *rt; - JSScopeProperty *sprop; - uint32 i, n; - - /* - * Clear our scope and the property cache of all obj's properties only if - * obj owns the scope (i.e., not if obj is unmutated and therefore sharing - * its prototype's scope). NB: we do not clear any reserved slots lying - * below JSSLOT_FREE(clasp). - */ - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - if (scope->object == obj) { - /* Clear the property cache before we clear the scope. */ - rt = cx->runtime; - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (!SCOPE_HAD_MIDDLE_DELETE(scope) || - SCOPE_HAS_PROPERTY(scope, sprop)) { - PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL); - } - } - - /* Now that we're done using scope->lastProp/table, clear scope. */ - js_ClearScope(cx, scope); - - /* Clear slot values and reset freeslot so we're consistent. */ - i = scope->map.nslots; - n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); - while (--i >= n) - obj->slots[i] = JSVAL_VOID; - scope->map.freeslot = n; - } - JS_UNLOCK_OBJ(cx, obj); -} - -jsval -js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) -{ - jsval v; - - JS_LOCK_OBJ(cx, obj); - v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID; - JS_UNLOCK_OBJ(cx, obj); - return v; -} - -JSBool -js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) -{ - JSScope *scope; - uint32 nslots; - JSClass *clasp; - jsval *newslots; - - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - nslots = (uint32) obj->slots[-1]; - if (slot >= nslots) { - /* - * At this point, obj may or may not own scope. If some path calls - * js_GetMutableScope but does not add a slot-owning property, then - * scope->object == obj but nslots will be nominal. If obj shares a - * prototype's scope, then we cannot update scope->map here, but we - * must update obj->slots[-1] when we grow obj->slots. - * - * See js_Mark, before the last return, where we make a special case - * for unmutated (scope->object != obj) objects. - */ - JS_ASSERT(nslots == JS_INITIAL_NSLOTS); - clasp = LOCKED_OBJ_GET_CLASS(obj); - nslots = JSSLOT_FREE(clasp); - if (clasp->reserveSlots) - nslots += clasp->reserveSlots(cx, obj); - JS_ASSERT(slot < nslots); - - newslots = AllocSlots(cx, obj->slots, nslots); - if (!newslots) { - JS_UNLOCK_SCOPE(cx, scope); - return JS_FALSE; - } - if (scope->object == obj) - scope->map.nslots = nslots; - obj->slots = newslots; - } - - /* Whether or not we grew nslots, we may need to advance freeslot. */ - if (scope->object == obj && slot >= scope->map.freeslot) - scope->map.freeslot = slot + 1; - - obj->slots[slot] = v; - JS_UNLOCK_SCOPE(cx, scope); - return JS_TRUE; -} - -#ifdef DEBUG - -/* Routines to print out values during debugging. */ - -void printChar(jschar *cp) { - fprintf(stderr, "jschar* (0x%p) \"", (void *)cp); - while (*cp) - fputc(*cp++, stderr); - fputc('"', stderr); - fputc('\n', stderr); -} - -void printString(JSString *str) { - size_t i, n; - jschar *s; - fprintf(stderr, "string (0x%p) \"", (void *)str); - s = JSSTRING_CHARS(str); - for (i=0, n=JSSTRING_LENGTH(str); i < n; i++) - fputc(s[i], stderr); - fputc('"', stderr); - fputc('\n', stderr); -} - -void printVal(JSContext *cx, jsval val); - -void printObj(JSContext *cx, JSObject *jsobj) { - jsuint i; - jsval val; - JSClass *clasp; - - fprintf(stderr, "object 0x%p\n", (void *)jsobj); - clasp = OBJ_GET_CLASS(cx, jsobj); - fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name); - for (i=0; i < jsobj->map->nslots; i++) { - fprintf(stderr, "slot %3d ", i); - val = jsobj->slots[i]; - if (JSVAL_IS_OBJECT(val)) - fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val)); - else - printVal(cx, val); - } -} - -void printVal(JSContext *cx, jsval val) { - fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val); - if (JSVAL_IS_NULL(val)) { - fprintf(stderr, "null\n"); - } else if (JSVAL_IS_VOID(val)) { - fprintf(stderr, "undefined\n"); - } else if (JSVAL_IS_OBJECT(val)) { - printObj(cx, JSVAL_TO_OBJECT(val)); - } else if (JSVAL_IS_INT(val)) { - fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val)); - } else if (JSVAL_IS_STRING(val)) { - printString(JSVAL_TO_STRING(val)); - } else if (JSVAL_IS_DOUBLE(val)) { - fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val)); - } else { - JS_ASSERT(JSVAL_IS_BOOLEAN(val)); - fprintf(stderr, "(boolean) %s\n", - JSVAL_TO_BOOLEAN(val) ? "true" : "false"); - } - fflush(stderr); -} - -void printId(JSContext *cx, jsid id) { - fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id); - printVal(cx, ID_TO_VALUE(id)); -} - -void printAtom(JSAtom *atom) { - printString(ATOM_TO_STRING(atom)); -} - -#endif diff --git a/spidermonkey/libjs/jsobj.h b/spidermonkey/libjs/jsobj.h deleted file mode 100644 index eb3aedb..0000000 --- a/spidermonkey/libjs/jsobj.h +++ /dev/null @@ -1,596 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsobj_h___ -#define jsobj_h___ -/* - * JS object definitions. - * - * A JS object consists of a possibly-shared object descriptor containing - * ordered property names, called the map; and a dense vector of property - * values, called slots. The map/slot pointer pair is GC'ed, while the map - * is reference counted and the slot vector is malloc'ed. - */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsprvtd.h" -#include "jspubtd.h" - -JS_BEGIN_EXTERN_C - -struct JSObjectMap { - jsrefcount nrefs; /* count of all referencing objects */ - JSObjectOps *ops; /* high level object operation vtable */ - uint32 nslots; /* length of obj->slots vector */ - uint32 freeslot; /* index of next free obj->slots element */ -}; - -/* Shorthand macros for frequently-made calls. */ -#define OBJ_LOOKUP_PROPERTY(cx,obj,id,objp,propp) \ - (obj)->map->ops->lookupProperty(cx,obj,id,objp,propp) -#define OBJ_DEFINE_PROPERTY(cx,obj,id,value,getter,setter,attrs,propp) \ - (obj)->map->ops->defineProperty(cx,obj,id,value,getter,setter,attrs,propp) -#define OBJ_GET_PROPERTY(cx,obj,id,vp) \ - (obj)->map->ops->getProperty(cx,obj,id,vp) -#define OBJ_SET_PROPERTY(cx,obj,id,vp) \ - (obj)->map->ops->setProperty(cx,obj,id,vp) -#define OBJ_GET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ - (obj)->map->ops->getAttributes(cx,obj,id,prop,attrsp) -#define OBJ_SET_ATTRIBUTES(cx,obj,id,prop,attrsp) \ - (obj)->map->ops->setAttributes(cx,obj,id,prop,attrsp) -#define OBJ_DELETE_PROPERTY(cx,obj,id,rval) \ - (obj)->map->ops->deleteProperty(cx,obj,id,rval) -#define OBJ_DEFAULT_VALUE(cx,obj,hint,vp) \ - (obj)->map->ops->defaultValue(cx,obj,hint,vp) -#define OBJ_ENUMERATE(cx,obj,enum_op,statep,idp) \ - (obj)->map->ops->enumerate(cx,obj,enum_op,statep,idp) -#define OBJ_CHECK_ACCESS(cx,obj,id,mode,vp,attrsp) \ - (obj)->map->ops->checkAccess(cx,obj,id,mode,vp,attrsp) - -/* These four are time-optimized to avoid stub calls. */ -#define OBJ_THIS_OBJECT(cx,obj) \ - ((obj)->map->ops->thisObject \ - ? (obj)->map->ops->thisObject(cx,obj) \ - : (obj)) -#define OBJ_DROP_PROPERTY(cx,obj,prop) \ - ((obj)->map->ops->dropProperty \ - ? (obj)->map->ops->dropProperty(cx,obj,prop) \ - : (void)0) -#define OBJ_GET_REQUIRED_SLOT(cx,obj,slot) \ - ((obj)->map->ops->getRequiredSlot \ - ? (obj)->map->ops->getRequiredSlot(cx, obj, slot) \ - : JSVAL_VOID) -#define OBJ_SET_REQUIRED_SLOT(cx,obj,slot,v) \ - ((obj)->map->ops->setRequiredSlot \ - ? (obj)->map->ops->setRequiredSlot(cx, obj, slot, v) \ - : JS_TRUE) - -#define OBJ_TO_INNER_OBJECT(cx,obj) \ - JS_BEGIN_MACRO \ - JSClass *clasp_ = OBJ_GET_CLASS(cx, obj); \ - if (clasp_->flags & JSCLASS_IS_EXTENDED) { \ - JSExtendedClass *xclasp_ = (JSExtendedClass*)clasp_; \ - if (xclasp_->innerObject) \ - obj = xclasp_->innerObject(cx, obj); \ - } \ - JS_END_MACRO - -/* - * In the original JS engine design, obj->slots pointed to a vector of length - * JS_INITIAL_NSLOTS words if obj->map was shared with a prototype object, - * else of length obj->map->nslots. With the advent of JS_GetReservedSlot, - * JS_SetReservedSlot, and JSCLASS_HAS_RESERVED_SLOTS (see jsapi.h), the size - * of the minimum length slots vector in the case where map is shared cannot - * be constant. This length starts at JS_INITIAL_NSLOTS, but may advance to - * include all the reserved slots. - * - * Therefore slots must be self-describing. Rather than tag its low order bit - * (a bit is all we need) to distinguish initial length from reserved length, - * we do "the BSTR thing": over-allocate slots by one jsval, and store the - * *net* length (counting usable slots, which have non-negative obj->slots[] - * indices) in obj->slots[-1]. All code that sets obj->slots must be aware of - * this hack -- you have been warned, and jsobj.c has been updated! - */ -struct JSObject { - JSObjectMap *map; - jsval *slots; -}; - -#define JSSLOT_PROTO 0 -#define JSSLOT_PARENT 1 -#define JSSLOT_CLASS 2 -#define JSSLOT_PRIVATE 3 -#define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE) \ - ? JSSLOT_PRIVATE + 1 \ - : JSSLOT_CLASS + 1) - -#define JSSLOT_FREE(clasp) (JSSLOT_START(clasp) \ - + JSCLASS_RESERVED_SLOTS(clasp)) - -#define JS_INITIAL_NSLOTS 5 - -#ifdef DEBUG -#define MAP_CHECK_SLOT(map,slot) \ - JS_ASSERT((uint32)slot < JS_MIN((map)->freeslot, (map)->nslots)) -#define OBJ_CHECK_SLOT(obj,slot) \ - MAP_CHECK_SLOT((obj)->map, slot) -#else -#define OBJ_CHECK_SLOT(obj,slot) ((void)0) -#endif - -/* Fast macros for accessing obj->slots while obj is locked (if thread-safe). */ -#define LOCKED_OBJ_GET_SLOT(obj,slot) \ - (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot]) -#define LOCKED_OBJ_SET_SLOT(obj,slot,value) \ - (OBJ_CHECK_SLOT(obj, slot), (obj)->slots[slot] = (value)) -#define LOCKED_OBJ_GET_PROTO(obj) \ - JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)) -#define LOCKED_OBJ_GET_CLASS(obj) \ - ((JSClass *)JSVAL_TO_PRIVATE(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_CLASS))) - -#ifdef JS_THREADSAFE - -/* Thread-safe functions and wrapper macros for accessing obj->slots. */ -#define OBJ_GET_SLOT(cx,obj,slot) \ - (OBJ_CHECK_SLOT(obj, slot), \ - (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ - ? LOCKED_OBJ_GET_SLOT(obj, slot) \ - : js_GetSlotThreadSafe(cx, obj, slot)) - -#define OBJ_SET_SLOT(cx,obj,slot,value) \ - (OBJ_CHECK_SLOT(obj, slot), \ - (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->ownercx == cx) \ - ? (void) LOCKED_OBJ_SET_SLOT(obj, slot, value) \ - : js_SetSlotThreadSafe(cx, obj, slot, value)) - -/* - * If thread-safe, define an OBJ_GET_SLOT wrapper that bypasses, for a native - * object, the lock-free "fast path" test of (OBJ_SCOPE(obj)->ownercx == cx), - * to avoid needlessly switching from lock-free to lock-full scope when doing - * GC on a different context from the last one to own the scope. The caller - * in this case is probably a JSClass.mark function, e.g., fun_mark, or maybe - * a finalizer. - * - * The GC runs only when all threads except the one on which the GC is active - * are suspended at GC-safe points, so there is no hazard in directly accessing - * obj->slots[slot] from the GC's thread, once rt->gcRunning has been set. See - * jsgc.c for details. - */ -#define THREAD_IS_RUNNING_GC(rt, thread) \ - ((rt)->gcRunning && (rt)->gcThread == (thread)) - -#define CX_THREAD_IS_RUNNING_GC(cx) \ - THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread) - -#define GC_AWARE_GET_SLOT(cx, obj, slot) \ - ((OBJ_IS_NATIVE(obj) && CX_THREAD_IS_RUNNING_GC(cx)) \ - ? (obj)->slots[slot] \ - : OBJ_GET_SLOT(cx, obj, slot)) - -#else /* !JS_THREADSAFE */ - -#define OBJ_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) -#define OBJ_SET_SLOT(cx,obj,slot,value) LOCKED_OBJ_SET_SLOT(obj,slot,value) -#define GC_AWARE_GET_SLOT(cx,obj,slot) LOCKED_OBJ_GET_SLOT(obj,slot) - -#endif /* !JS_THREADSAFE */ - -/* Thread-safe proto, parent, and class access macros. */ -#define OBJ_GET_PROTO(cx,obj) \ - JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PROTO)) -#define OBJ_SET_PROTO(cx,obj,proto) \ - OBJ_SET_SLOT(cx, obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(proto)) - -#define OBJ_GET_PARENT(cx,obj) \ - JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj, JSSLOT_PARENT)) -#define OBJ_SET_PARENT(cx,obj,parent) \ - OBJ_SET_SLOT(cx, obj, JSSLOT_PARENT, OBJECT_TO_JSVAL(parent)) - -#define OBJ_GET_CLASS(cx,obj) \ - ((JSClass *)JSVAL_TO_PRIVATE(OBJ_GET_SLOT(cx, obj, JSSLOT_CLASS))) - -/* Test whether a map or object is native. */ -#define MAP_IS_NATIVE(map) \ - ((map)->ops == &js_ObjectOps || \ - ((map)->ops && (map)->ops->newObjectMap == js_ObjectOps.newObjectMap)) - -#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) - -extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; -extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; -extern JSClass js_ObjectClass; -extern JSClass js_WithClass; -extern JSClass js_BlockClass; - -/* - * Block scope object macros. The slots reserved by js_BlockClass are: - * - * JSSLOT_PRIVATE JSStackFrame * active frame pointer or null - * JSSLOT_BLOCK_DEPTH int depth of block slots in frame - * - * After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals. - * OBJ_BLOCK_COUNT depends on this arrangement. - * - * A With object is like a Block object, in that both have one reserved slot - * telling the stack depth of the relevant slots (the slot whose value is the - * object named in the with statement, the slots containing the block's local - * variables); and both have a private slot referring to the JSStackFrame in - * whose activation they were created (or null if the with or block object - * outlives the frame). - */ -#define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1) - -#define OBJ_BLOCK_COUNT(cx,obj) \ - ((obj)->map->freeslot - (JSSLOT_BLOCK_DEPTH + 1)) -#define OBJ_BLOCK_DEPTH(cx,obj) \ - JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH)) -#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \ - OBJ_SET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth)) - -/* - * To make sure this slot is well-defined, always call js_NewWithObject to - * create a With object, don't call js_NewObject directly. When creating a - * With object that does not correspond to a stack slot, pass -1 for depth. - * - * When popping the stack across this object's "with" statement, client code - * must call JS_SetPrivate(cx, withobj, NULL). - */ -extern JSObject * -js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth); - -/* - * Create a new block scope object not linked to any proto or parent object. - * Blocks are created by the compiler to reify let blocks and comprehensions. - * Only when dynamic scope is captured do they need to be cloned and spliced - * into an active scope chain. - */ -extern JSObject * -js_NewBlockObject(JSContext *cx); - -extern JSObject * -js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent, - JSStackFrame *fp); - -extern JSBool -js_PutBlockObject(JSContext *cx, JSObject *obj); - -struct JSSharpObjectMap { - jsrefcount depth; - jsatomid sharpgen; - JSHashTable *table; -}; - -#define SHARP_BIT ((jsatomid) 1) -#define BUSY_BIT ((jsatomid) 2) -#define SHARP_ID_SHIFT 2 -#define IS_SHARP(he) (JS_PTR_TO_UINT32((he)->value) & SHARP_BIT) -#define MAKE_SHARP(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|SHARP_BIT)) -#define IS_BUSY(he) (JS_PTR_TO_UINT32((he)->value) & BUSY_BIT) -#define MAKE_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)|BUSY_BIT)) -#define CLEAR_BUSY(he) ((he)->value = JS_UINT32_TO_PTR(JS_PTR_TO_UINT32((he)->value)&~BUSY_BIT)) - -extern JSHashEntry * -js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, - jschar **sp); - -extern void -js_LeaveSharpObject(JSContext *cx, JSIdArray **idap); - -/* - * Mark objects stored in map if GC happens between js_EnterSharpObject - * and js_LeaveSharpObject. GC calls this when map->depth > 0. - */ -extern void -js_GCMarkSharpMap(JSContext *cx, JSSharpObjectMap *map); - -extern JSBool -js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSBool -js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSBool -js_HasOwnPropertyHelper(JSContext *cx, JSObject *obj, JSLookupPropOp lookup, - uintN argc, jsval *argv, jsval *rval); - -extern JSObject* -js_InitBlockClass(JSContext *cx, JSObject* obj); - -extern JSObject * -js_InitObjectClass(JSContext *cx, JSObject *obj); - -/* Select Object.prototype method names shared between jsapi.c and jsobj.c. */ -extern const char js_watch_str[]; -extern const char js_unwatch_str[]; -extern const char js_hasOwnProperty_str[]; -extern const char js_isPrototypeOf_str[]; -extern const char js_propertyIsEnumerable_str[]; -extern const char js_defineGetter_str[]; -extern const char js_defineSetter_str[]; -extern const char js_lookupGetter_str[]; -extern const char js_lookupSetter_str[]; - -extern void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp); - -extern JSObjectMap * -js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -extern void -js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); - -extern JSObjectMap * -js_HoldObjectMap(JSContext *cx, JSObjectMap *map); - -extern JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); - -extern JSBool -js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); - -extern JSObject * -js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent); - -/* - * Fast access to immutable standard objects (constructors and prototypes). - */ -extern JSBool -js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject **objp); - -extern JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj); - -extern JSBool -js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp); - -extern JSObject * -js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, - JSObject *parent, uintN argc, jsval *argv); - -extern void -js_FinalizeObject(JSContext *cx, JSObject *obj); - -extern JSBool -js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); - -extern void -js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); - -/* - * Native property add and lookup variants that hide id in the hidden atom - * subspace, so as to avoid collisions between internal properties such as - * formal arguments and local variables in function objects, and externally - * set properties with the same ids. - */ -extern JSScopeProperty * -js_AddHiddenProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid); - -extern JSBool -js_LookupHiddenProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp); - -/* - * Find or create a property named by id in obj's scope, with the given getter - * and setter, slot, attributes, and other members. - */ -extern JSScopeProperty * -js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid); - -/* - * Change sprop to have the given attrs, getter, and setter in scope, morphing - * it into a potentially new JSScopeProperty. Return a pointer to the changed - * or identical property. - */ -extern JSScopeProperty * -js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter); - -/* - * On error, return false. On success, if propp is non-null, return true with - * obj locked and with a held property in *propp; if propp is null, return true - * but release obj's lock first. Therefore all callers who pass non-null propp - * result parameters must later call OBJ_DROP_PROPERTY(cx, obj, *propp) both to - * drop the held property, and to release the lock on obj. - */ -extern JSBool -js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp); - -extern JSBool -js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - uintN flags, intN shortid, JSProperty **propp); - -/* - * Unlike js_DefineProperty, propp must be non-null. On success, and if id was - * found, return true with *objp non-null and locked, and with a held property - * stored in *propp. If successful but id was not found, return true with both - * *objp and *propp null. Therefore all callers who receive a non-null *propp - * must later call OBJ_DROP_PROPERTY(cx, *objp, *propp). - */ -extern JS_FRIEND_API(JSBool) -js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp); - -/* - * Specialized subroutine that allows caller to preset JSRESOLVE_* flags. - * JSRESOLVE_HIDDEN flags hidden function param/local name lookups, just for - * internal use by fun_resolve and similar built-ins. - */ -extern JSBool -js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, - JSObject **objp, JSProperty **propp); - -#define JSRESOLVE_HIDDEN 0x8000 - -extern JS_FRIEND_API(JSBool) -js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, - JSProperty **propp); - -extern JSObject * -js_FindIdentifierBase(JSContext *cx, jsid id); - -extern JSObject * -js_FindVariableScope(JSContext *cx, JSFunction **funp); - -/* - * NB: js_NativeGet and js_NativeSet are called with the scope containing sprop - * (pobj's scope for Get, obj's for Set) locked, and on successful return, that - * scope is again locked. But on failure, both functions return false with the - * scope containing sprop unlocked. - */ -extern JSBool -js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, - JSScopeProperty *sprop, jsval *vp); - -extern JSBool -js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp); - -extern JSBool -js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -extern JSBool -js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -extern JSBool -js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp); - -extern JSBool -js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp); - -extern JSBool -js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval); - -extern JSBool -js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp); - -extern JSIdArray * -js_NewIdArray(JSContext *cx, jsint length); - -/* - * Unlike realloc(3), this function frees ida on failure. - */ -extern JSIdArray * -js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length); - -extern JSBool -js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp); - -extern void -js_MarkNativeIteratorStates(JSContext *cx); - -extern JSBool -js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, - jsval *vp, uintN *attrsp); - -extern JSBool -js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -extern JSBool -js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -extern JSBool -js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj); - -extern JSBool -js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -extern JSBool -js_GetClassPrototype(JSContext *cx, JSObject *scope, jsid id, - JSObject **protop); - -extern JSBool -js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, - uintN attrs); - -extern JSBool -js_ValueToObject(JSContext *cx, jsval v, JSObject **objp); - -extern JSObject * -js_ValueToNonNullObject(JSContext *cx, jsval v); - -extern JSBool -js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval); - -extern JSBool -js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom, - uintN argc, jsval *argv, jsval *rval); - -extern JSBool -js_XDRObject(JSXDRState *xdr, JSObject **objp); - -extern uint32 -js_Mark(JSContext *cx, JSObject *obj, void *arg); - -extern void -js_Clear(JSContext *cx, JSObject *obj); - -extern jsval -js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); - -extern JSBool -js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v); - -extern JSObject * -js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller); - -extern JSBool -js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj, - JSPrincipals *principals, JSAtom *caller); -JS_END_EXTERN_C - -#endif /* jsobj_h___ */ diff --git a/spidermonkey/libjs/jsopcode.c b/spidermonkey/libjs/jsopcode.c deleted file mode 100644 index 3dec776..0000000 --- a/spidermonkey/libjs/jsopcode.c +++ /dev/null @@ -1,4794 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS bytecode descriptors, disassemblers, and decompilers. - */ -#include "jsstddef.h" -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsdtoa.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jslock.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_DESTRUCTURING -# include "jsnum.h" -#endif - -static const char js_incop_strs[][3] = {"++", "--"}; - -/* Pollute the namespace locally for MSVC Win16, but not for WatCom. */ -#ifdef __WINDOWS_386__ - #ifdef FAR - #undef FAR - #endif -#else /* !__WINDOWS_386__ */ -#ifndef FAR -#define FAR -#endif -#endif /* !__WINDOWS_386__ */ - -const JSCodeSpec FAR js_CodeSpec[] = { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - {name,token,length,nuses,ndefs,prec,format}, -#include "jsopcode.tbl" -#undef OPDEF -}; - -uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0]; - -/************************************************************************/ - -static ptrdiff_t -GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) -{ - uint32 type; - - type = (js_CodeSpec[*pc].format & JOF_TYPEMASK); - if (JOF_TYPE_IS_EXTENDED_JUMP(type)) - return GET_JUMPX_OFFSET(pc2); - return GET_JUMP_OFFSET(pc2); -} - -#ifdef DEBUG - -JS_FRIEND_API(JSBool) -js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp) -{ - jsbytecode *pc, *end; - uintN len; - - pc = script->code; - end = pc + script->length; - while (pc < end) { - if (pc == script->main) - fputs("main:\n", fp); - len = js_Disassemble1(cx, script, pc, - PTRDIFF(pc, script->code, jsbytecode), - lines, fp); - if (!len) - return JS_FALSE; - pc += len; - } - return JS_TRUE; -} - -const char * -ToDisassemblySource(JSContext *cx, jsval v) -{ - JSObject *obj; - JSScopeProperty *sprop; - char *source; - const char *bytes; - JSString *str; - - if (!JSVAL_IS_PRIMITIVE(v)) { - obj = JSVAL_TO_OBJECT(v); - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - source = JS_sprintf_append(NULL, "depth %d {", - OBJ_BLOCK_DEPTH(cx, obj)); - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; - sprop = sprop->parent) { - bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id)); - if (!bytes) - return NULL; - source = JS_sprintf_append(source, "%s: %d%s", - bytes, sprop->shortid, - sprop->parent ? ", " : ""); - } - source = JS_sprintf_append(source, "}"); - if (!source) - return NULL; - str = JS_NewString(cx, source, strlen(source)); - if (!str) - return NULL; - return JS_GetStringBytes(str); - } - } - return js_ValueToPrintableSource(cx, v); -} - -JS_FRIEND_API(uintN) -js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, - JSBool lines, FILE *fp) -{ - JSOp op; - const JSCodeSpec *cs; - ptrdiff_t len, off, jmplen; - uint32 type; - JSAtom *atom; - const char *bytes; - - op = (JSOp)*pc; - if (op >= JSOP_LIMIT) { - char numBuf1[12], numBuf2[12]; - JS_snprintf(numBuf1, sizeof numBuf1, "%d", op); - JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2); - return 0; - } - cs = &js_CodeSpec[op]; - len = (ptrdiff_t) cs->length; - fprintf(fp, "%05u:", loc); - if (lines) - fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc)); - fprintf(fp, " %s", cs->name); - type = cs->format & JOF_TYPEMASK; - switch (type) { - case JOF_BYTE: - if (op == JSOP_TRAP) { - op = JS_GetTrapOpcode(cx, script, pc); - if (op == JSOP_LIMIT) - return 0; - len = (ptrdiff_t) js_CodeSpec[op].length; - } - break; - - case JOF_JUMP: - case JOF_JUMPX: - off = GetJumpOffset(pc, pc); - fprintf(fp, " %u (%d)", loc + off, off); - break; - - case JOF_CONST: - atom = GET_ATOM(cx, script, pc); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, " %s", bytes); - break; - - case JOF_UINT16: - case JOF_LOCAL: - fprintf(fp, " %u", GET_UINT16(pc)); - break; - - case JOF_TABLESWITCH: - case JOF_TABLESWITCHX: - { - jsbytecode *pc2; - jsint i, low, high; - - jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - fprintf(fp, " defaultOffset %d low %d high %d", off, low, high); - for (i = low; i <= high; i++) { - off = GetJumpOffset(pc, pc2); - fprintf(fp, "\n\t%d: %d", i, off); - pc2 += jmplen; - } - len = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - case JOF_LOOKUPSWITCHX: - { - jsbytecode *pc2; - jsatomid npairs; - - jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - fprintf(fp, " offset %d npairs %u", off, (uintN) npairs); - while (npairs) { - atom = GET_ATOM(cx, script, pc2); - pc2 += ATOM_INDEX_LEN; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, "\n\t%s: %d", bytes, off); - npairs--; - } - len = 1 + pc2 - pc; - break; - } - - case JOF_QARG: - fprintf(fp, " %u", GET_ARGNO(pc)); - break; - - case JOF_QVAR: - fprintf(fp, " %u", GET_VARNO(pc)); - break; - - case JOF_INDEXCONST: - fprintf(fp, " %u", GET_VARNO(pc)); - pc += VARNO_LEN; - atom = GET_ATOM(cx, script, pc); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, " %s", bytes); - break; - - case JOF_UINT24: - if (op == JSOP_FINDNAME) { - /* Special case to avoid a JOF_FINDNAME just for this op. */ - atom = js_GetAtom(cx, &script->atomMap, GET_UINT24(pc)); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - fprintf(fp, " %s", bytes); - break; - } - - JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL); - fprintf(fp, " %u", GET_UINT24(pc)); - break; - - case JOF_LITOPX: - atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc)); - bytes = ToDisassemblySource(cx, ATOM_KEY(atom)); - if (!bytes) - return 0; - - /* - * Bytecode: JSOP_LITOPX op [ if JSOP_DEFLOCALFUN]. - * Advance pc to point at op. - */ - pc += 1 + LITERAL_INDEX_LEN; - op = *pc; - cs = &js_CodeSpec[op]; - fprintf(fp, " %s op %s", bytes, cs->name); - if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST) - fprintf(fp, " %u", GET_VARNO(pc)); - - /* - * Set len to advance pc to skip op and any other immediates (namely, - * if JSOP_DEFLOCALFUN). - */ - JS_ASSERT(cs->length > ATOM_INDEX_LEN); - len = cs->length - ATOM_INDEX_LEN; - break; - - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_UNKNOWN_FORMAT, numBuf); - return 0; - } - } - fputs("\n", fp); - return len; -} - -#endif /* DEBUG */ - -/************************************************************************/ - -/* - * Sprintf, but with unlimited and automatically allocated buffering. - */ -typedef struct Sprinter { - JSContext *context; /* context executing the decompiler */ - JSArenaPool *pool; /* string allocation pool */ - char *base; /* base address of buffer in pool */ - size_t size; /* size of buffer allocated at base */ - ptrdiff_t offset; /* offset of next free char in buffer */ -} Sprinter; - -#define INIT_SPRINTER(cx, sp, ap, off) \ - ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \ - (sp)->offset = off) - -#define OFF2STR(sp,off) ((sp)->base + (off)) -#define STR2OFF(sp,str) ((str) - (sp)->base) -#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str)) - -static JSBool -SprintAlloc(Sprinter *sp, size_t nb) -{ - char *base; - - base = sp->base; - if (!base) { - JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb); - } else { - JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb); - } - if (!base) { - JS_ReportOutOfMemory(sp->context); - return JS_FALSE; - } - sp->base = base; - sp->size += nb; - return JS_TRUE; -} - -static ptrdiff_t -SprintPut(Sprinter *sp, const char *s, size_t len) -{ - ptrdiff_t nb, offset; - char *bp; - - /* Allocate space for s, including the '\0' at the end. */ - nb = (sp->offset + len + 1) - sp->size; - if (nb > 0 && !SprintAlloc(sp, nb)) - return -1; - - /* Advance offset and copy s into sp's buffer. */ - offset = sp->offset; - sp->offset += len; - bp = sp->base + offset; - memmove(bp, s, len); - bp[len] = 0; - return offset; -} - -static ptrdiff_t -SprintCString(Sprinter *sp, const char *s) -{ - return SprintPut(sp, s, strlen(s)); -} - -static ptrdiff_t -Sprint(Sprinter *sp, const char *format, ...) -{ - va_list ap; - char *bp; - ptrdiff_t offset; - - va_start(ap, format); - bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ - va_end(ap); - if (!bp) { - JS_ReportOutOfMemory(sp->context); - return -1; - } - offset = SprintCString(sp, bp); - free(bp); - return offset; -} - -const jschar js_EscapeMap[] = { - '\b', 'b', - '\f', 'f', - '\n', 'n', - '\r', 'r', - '\t', 't', - '\v', 'v', - '"', '"', - '\'', '\'', - '\\', '\\', - 0 -}; - -#define DONT_ESCAPE 0x10000 - -static char * -QuoteString(Sprinter *sp, JSString *str, uint32 quote) -{ - JSBool dontEscape, ok; - jschar qc, c; - ptrdiff_t off, len, nb; - const jschar *s, *t, *u, *z; - char *bp; - - /* Sample off first for later return value pointer computation. */ - dontEscape = (quote & DONT_ESCAPE) != 0; - qc = (jschar) quote; - off = sp->offset; - if (qc && Sprint(sp, "%c", (char)qc) < 0) - return NULL; - - /* Loop control variables: z points at end of string sentinel. */ - s = JSSTRING_CHARS(str); - z = s + JSSTRING_LENGTH(str); - for (t = s; t < z; s = ++t) { - /* Move t forward from s past un-quote-worthy characters. */ - c = *t; - while (JS_ISPRINT(c) && c != qc && c != '\\' && !(c >> 8)) { - c = *++t; - if (t == z) - break; - } - len = PTRDIFF(t, s, jschar); - - /* Allocate space for s, including the '\0' at the end. */ - nb = (sp->offset + len + 1) - sp->size; - if (nb > 0 && !SprintAlloc(sp, nb)) - return NULL; - - /* Advance sp->offset and copy s into sp's buffer. */ - bp = sp->base + sp->offset; - sp->offset += len; - while (--len >= 0) - *bp++ = (char) *s++; - *bp = '\0'; - - if (t == z) - break; - - /* Use js_EscapeMap, \u, or \x only if necessary. */ - if ((u = js_strchr(js_EscapeMap, c)) != NULL) { - ok = dontEscape - ? Sprint(sp, "%c", (char)c) >= 0 - : Sprint(sp, "\\%c", (char)u[1]) >= 0; - } else { -#ifdef JS_C_STRINGS_ARE_UTF8 - /* If this is a surrogate pair, make sure to print the pair. */ - if (c >= 0xD800 && c <= 0xDBFF) { - jschar buffer[3]; - buffer[0] = c; - buffer[1] = *++t; - buffer[2] = 0; - if (t == z) { - char numbuf[10]; - JS_snprintf(numbuf, sizeof numbuf, "0x%x", c); - JS_ReportErrorFlagsAndNumber(sp->context, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_BAD_SURROGATE_CHAR, - numbuf); - ok = JS_FALSE; - break; - } - ok = Sprint(sp, "%hs", buffer) >= 0; - } else { - /* Print as UTF-8 string. */ - ok = Sprint(sp, "%hc", c) >= 0; - } -#else - /* Use \uXXXX or \xXX if the string can't be displayed as UTF-8. */ - ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0; -#endif - } - if (!ok) - return NULL; - } - - /* Sprint the closing quote and return the quoted string. */ - if (qc && Sprint(sp, "%c", (char)qc) < 0) - return NULL; - - /* - * If we haven't Sprint'd anything yet, Sprint an empty string so that - * the OFF2STR below gives a valid result. - */ - if (off == sp->offset && Sprint(sp, "") < 0) - return NULL; - return OFF2STR(sp, off); -} - -JSString * -js_QuoteString(JSContext *cx, JSString *str, jschar quote) -{ - void *mark; - Sprinter sprinter; - char *bytes; - JSString *escstr; - - mark = JS_ARENA_MARK(&cx->tempPool); - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - bytes = QuoteString(&sprinter, str, quote); - escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; - JS_ARENA_RELEASE(&cx->tempPool, mark); - return escstr; -} - -/************************************************************************/ - -#if JS_HAS_BLOCK_SCOPE -typedef enum JSBraceState { - ALWAYS_BRACE, - MAYBE_BRACE, - DONT_BRACE -} JSBraceState; -#endif - -struct JSPrinter { - Sprinter sprinter; /* base class state */ - JSArenaPool pool; /* string allocation pool */ - uintN indent; /* indentation in spaces */ - JSPackedBool pretty; /* pretty-print: indent, use newlines */ - JSPackedBool grouped; /* in parenthesized expression context */ - JSScript *script; /* script being printed */ - jsbytecode *dvgfence; /* js_DecompileValueGenerator fencepost */ - JSScope *scope; /* script function scope */ -#if JS_HAS_BLOCK_SCOPE - JSBraceState braceState; /* remove braces around let declaration */ - ptrdiff_t spaceOffset; /* -1 or offset of space before maybe-{ */ -#endif -}; - -/* - * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters - * to functions such as js_DecompileFunction and js_NewPrinter. This time, as - * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a - * uintN is at least 32 bits. - */ -#define JS_IN_GROUP_CONTEXT 0x10000 - -JSPrinter * -js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty) -{ - JSPrinter *jp; - - jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter)); - if (!jp) - return NULL; - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - JS_InitArenaPool(&jp->pool, name, 256, 1); - jp->indent = indent & ~JS_IN_GROUP_CONTEXT; - jp->pretty = pretty; - jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0; - jp->script = NULL; - jp->dvgfence = NULL; - jp->scope = NULL; -#if JS_HAS_BLOCK_SCOPE - jp->braceState = ALWAYS_BRACE; - jp->spaceOffset = -1; -#endif - return jp; -} - -void -js_DestroyPrinter(JSPrinter *jp) -{ - JS_FinishArenaPool(&jp->pool); - JS_free(jp->sprinter.context, jp); -} - -JSString * -js_GetPrinterOutput(JSPrinter *jp) -{ - JSContext *cx; - JSString *str; - - cx = jp->sprinter.context; - if (!jp->sprinter.base) - return cx->runtime->emptyString; - str = JS_NewStringCopyZ(cx, jp->sprinter.base); - if (!str) - return NULL; - JS_FreeArenaPool(&jp->pool); - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); - return str; -} - -#if !JS_HAS_BLOCK_SCOPE -# define SET_MAYBE_BRACE(jp) jp -# define CLEAR_MAYBE_BRACE(jp) jp -#else -# define SET_MAYBE_BRACE(jp) ((jp)->braceState = MAYBE_BRACE, (jp)) -# define CLEAR_MAYBE_BRACE(jp) ((jp)->braceState = ALWAYS_BRACE, (jp)) - -static void -SetDontBrace(JSPrinter *jp) -{ - ptrdiff_t offset; - const char *bp; - - /* When not pretty-printing, newline after brace is chopped. */ - JS_ASSERT(jp->spaceOffset < 0); - offset = jp->sprinter.offset - (jp->pretty ? 3 : 2); - - /* The shortest case is "if (x) {". */ - JS_ASSERT(offset >= 6); - bp = jp->sprinter.base; - if (bp[offset+0] == ' ' && bp[offset+1] == '{') { - JS_ASSERT(!jp->pretty || bp[offset+2] == '\n'); - jp->spaceOffset = offset; - jp->braceState = DONT_BRACE; - } -} -#endif - -int -js_printf(JSPrinter *jp, const char *format, ...) -{ - va_list ap; - char *bp, *fp; - int cc; - - if (*format == '\0') - return 0; - - va_start(ap, format); - - /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */ - if (*format == '\t') { - format++; - -#if JS_HAS_BLOCK_SCOPE - if (*format == '}' && jp->braceState != ALWAYS_BRACE) { - JSBraceState braceState; - - braceState = jp->braceState; - jp->braceState = ALWAYS_BRACE; - if (braceState == DONT_BRACE) { - ptrdiff_t offset, delta, from; - - JS_ASSERT(format[1] == '\n' || format[1] == ' '); - offset = jp->spaceOffset; - JS_ASSERT(offset >= 6); - - /* Replace " {\n" at the end of jp->sprinter with "\n". */ - bp = jp->sprinter.base; - if (bp[offset+0] == ' ' && bp[offset+1] == '{') { - delta = 2; - if (jp->pretty) { - /* If pretty, we don't have to worry about 'else'. */ - JS_ASSERT(bp[offset+2] == '\n'); - } else if (bp[offset-1] != ')') { - /* Must keep ' ' to avoid 'dolet' or 'elselet'. */ - ++offset; - delta = 1; - } - - from = offset + delta; - memmove(bp + offset, bp + from, jp->sprinter.offset - from); - jp->sprinter.offset -= delta; - jp->spaceOffset = -1; - - format += 2; - if (*format == '\0') - return 0; - } - } - } -#endif - - if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) - return -1; - } - - /* Suppress newlines (must be once per format, at the end) if not pretty. */ - fp = NULL; - if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') { - fp = JS_strdup(jp->sprinter.context, format); - if (!fp) - return -1; - fp[cc] = '\0'; - format = fp; - } - - /* Allocate temp space, convert format, and put. */ - bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */ - if (fp) { - JS_free(jp->sprinter.context, fp); - format = NULL; - } - if (!bp) { - JS_ReportOutOfMemory(jp->sprinter.context); - return -1; - } - - cc = strlen(bp); - if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0) - cc = -1; - free(bp); - - va_end(ap); - return cc; -} - -JSBool -js_puts(JSPrinter *jp, const char *s) -{ - return SprintCString(&jp->sprinter, s) >= 0; -} - -/************************************************************************/ - -typedef struct SprintStack { - Sprinter sprinter; /* sprinter for postfix to infix buffering */ - ptrdiff_t *offsets; /* stack of postfix string offsets */ - jsbytecode *opcodes; /* parallel stack of JS opcodes */ - uintN top; /* top of stack index */ - uintN inArrayInit; /* array initialiser/comprehension level */ - JSPrinter *printer; /* permanent output goes here */ -} SprintStack; - -/* - * Get a stacked offset from ss->sprinter.base, or if the stacked value |off| - * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try - * to decompile the code that generated the missing value. This is used when - * reporting errors, where the model stack will lack |pcdepth| non-negative - * offsets (see js_DecompileValueGenerator and js_DecompileCode). - * - * If the stacked offset is -1, return 0 to index the NUL padding at the start - * of ss->sprinter.base. If this happens, it means there is a decompiler bug - * to fix, but it won't violate memory safety. - */ -static ptrdiff_t -GetOff(SprintStack *ss, uintN i) -{ - ptrdiff_t off; - JSString *str; - - off = ss->offsets[i]; - if (off < 0) { -#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder - JS_ASSERT(off < -1); -#endif - if (++off == 0) { - if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) - memset(ss->sprinter.base, 0, ss->sprinter.offset); - return 0; - } - - str = js_DecompileValueGenerator(ss->sprinter.context, off, - JSVAL_NULL, NULL); - if (!str) - return 0; - off = SprintCString(&ss->sprinter, JS_GetStringBytes(str)); - if (off < 0) - off = 0; - ss->offsets[i] = off; - } - return off; -} - -static const char * -GetStr(SprintStack *ss, uintN i) -{ - ptrdiff_t off; - - /* - * Must call GetOff before using ss->sprinter.base, since it may be null - * until bootstrapped by GetOff. - */ - off = GetOff(ss, i); - return OFF2STR(&ss->sprinter, off); -} - -/* Gap between stacked strings to allow for insertion of parens and commas. */ -#define PAREN_SLOP (2 + 1) - -/* - * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME, - * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in - * bytecode, so they don't preempt valid opcodes. - */ -#define JSOP_GETPROP2 256 -#define JSOP_GETELEM2 257 - -static JSBool -PushOff(SprintStack *ss, ptrdiff_t off, JSOp op) -{ - uintN top; - - if (!SprintAlloc(&ss->sprinter, PAREN_SLOP)) - return JS_FALSE; - - /* ss->top points to the next free slot; be paranoid about overflow. */ - top = ss->top; - JS_ASSERT(top < ss->printer->script->depth); - if (top >= ss->printer->script->depth) { - JS_ReportOutOfMemory(ss->sprinter.context); - return JS_FALSE; - } - - /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */ - ss->offsets[top] = off; - ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP - : (op == JSOP_GETELEM2) ? JSOP_GETELEM - : (jsbytecode) op; - ss->top = ++top; - memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); - ss->sprinter.offset += PAREN_SLOP; - return JS_TRUE; -} - -static ptrdiff_t -PopOff(SprintStack *ss, JSOp op) -{ - uintN top; - const JSCodeSpec *cs, *topcs; - ptrdiff_t off; - - /* ss->top points to the next free slot; be paranoid about underflow. */ - top = ss->top; - JS_ASSERT(top != 0); - if (top == 0) - return 0; - - ss->top = --top; - off = GetOff(ss, top); - topcs = &js_CodeSpec[ss->opcodes[top]]; - cs = &js_CodeSpec[op]; - if (topcs->prec != 0 && topcs->prec < cs->prec) { - ss->sprinter.offset = ss->offsets[top] = off - 2; - off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); - } else { - ss->sprinter.offset = off; - } - return off; -} - -static const char * -PopStr(SprintStack *ss, JSOp op) -{ - ptrdiff_t off; - - off = PopOff(ss, op); - return OFF2STR(&ss->sprinter, off); -} - -typedef struct TableEntry { - jsval key; - ptrdiff_t offset; - JSAtom *label; - jsint order; /* source order for stable tableswitch sort */ -} TableEntry; - -static JSBool -CompareOffsets(void *arg, const void *v1, const void *v2, int *result) -{ - ptrdiff_t offset_diff; - const TableEntry *te1 = (const TableEntry *) v1, - *te2 = (const TableEntry *) v2; - - offset_diff = te1->offset - te2->offset; - *result = (offset_diff == 0 ? te1->order - te2->order - : offset_diff < 0 ? -1 - : 1); - return JS_TRUE; -} - -static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb); - -static JSBool -DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, - jsbytecode *pc, ptrdiff_t switchLength, - ptrdiff_t defaultOffset, JSBool isCondSwitch) -{ - JSContext *cx; - JSPrinter *jp; - ptrdiff_t off, off2, diff, caseExprOff; - char *lval, *rval; - uintN i; - jsval key; - JSString *str; - - cx = ss->sprinter.context; - jp = ss->printer; - - /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */ - off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP); - lval = OFF2STR(&ss->sprinter, off); - - js_printf(CLEAR_MAYBE_BRACE(jp), "\tswitch (%s) {\n", lval); - - if (tableLength) { - diff = table[0].offset - defaultOffset; - if (diff > 0) { - jp->indent += 2; - js_printf(jp, "\t%s:\n", js_default_str); - jp->indent += 2; - if (!Decompile(ss, pc + defaultOffset, diff)) - return JS_FALSE; - jp->indent -= 4; - } - - caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0; - - for (i = 0; i < tableLength; i++) { - off = table[i].offset; - off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength; - - key = table[i].key; - if (isCondSwitch) { - ptrdiff_t nextCaseExprOff; - - /* - * key encodes the JSOP_CASE bytecode's offset from switchtop. - * The next case expression follows immediately, unless we are - * at the last case. - */ - nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); - nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; - jp->indent += 2; - if (!Decompile(ss, pc + caseExprOff, - nextCaseExprOff - caseExprOff)) { - return JS_FALSE; - } - caseExprOff = nextCaseExprOff; - - /* Balance the stack as if this JSOP_CASE matched. */ - --ss->top; - } else { - /* - * key comes from an atom, not the decompiler, so we need to - * quote it if it's a string literal. But if table[i].label - * is non-null, key was constant-propagated and label is the - * name of the const we should show as the case label. We set - * key to undefined so this identifier is escaped, if required - * by non-ASCII characters, but not quoted, by QuoteString. - */ - if (table[i].label) { - str = ATOM_TO_STRING(table[i].label); - key = JSVAL_VOID; - } else { - str = js_ValueToString(cx, key); - if (!str) - return JS_FALSE; - } - rval = QuoteString(&ss->sprinter, str, - (jschar)(JSVAL_IS_STRING(key) ? '"' : 0)); - if (!rval) - return JS_FALSE; - RETRACT(&ss->sprinter, rval); - jp->indent += 2; - js_printf(jp, "\tcase %s:\n", rval); - } - - jp->indent += 2; - if (off <= defaultOffset && defaultOffset < off2) { - diff = defaultOffset - off; - if (diff != 0) { - if (!Decompile(ss, pc + off, diff)) - return JS_FALSE; - off = defaultOffset; - } - jp->indent -= 2; - js_printf(jp, "\t%s:\n", js_default_str); - jp->indent += 2; - } - if (!Decompile(ss, pc + off, off2 - off)) - return JS_FALSE; - jp->indent -= 4; - - /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */ - if (isCondSwitch) - ++ss->top; - } - } - - if (defaultOffset == switchLength) { - jp->indent += 2; - js_printf(jp, "\t%s:;\n", js_default_str); - jp->indent -= 2; - } - js_printf(jp, "\t}\n"); - - /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */ - if (isCondSwitch) - --ss->top; - return JS_TRUE; -} - -static JSAtom * -GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot) -{ - JSScope *scope; - JSScopeProperty *sprop; - JSObject *obj, *proto; - - scope = jp->scope; - while (scope) { - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (sprop->getter != getter) - continue; - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - if ((uintN) sprop->shortid == slot) - return JSID_TO_ATOM(sprop->id); - } - obj = scope->object; - if (!obj) - break; - proto = OBJ_GET_PROTO(jp->sprinter.context, obj); - if (!proto) - break; - scope = OBJ_SCOPE(proto); - } - return NULL; -} - -/* - * NB: Indexed by SRC_DECL_* defines from jsemit.h. - */ -static const char * const var_prefix[] = {"var ", "const ", "let "}; - -static const char * -VarPrefix(jssrcnote *sn) -{ - if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) { - ptrdiff_t type = js_GetSrcNoteOffset(sn, 0); - if ((uintN)type <= SRC_DECL_LET) - return var_prefix[type]; - } - return ""; -} -#define LOCAL_ASSERT_RV(expr, rv) \ - JS_BEGIN_MACRO \ - JS_ASSERT(expr); \ - if (!(expr)) return (rv); \ - JS_END_MACRO - -const char * -GetLocal(SprintStack *ss, jsint i) -{ - ptrdiff_t off; - JSContext *cx; - JSScript *script; - jsatomid j, n; - JSAtom *atom; - JSObject *obj; - jsint depth, count; - JSScopeProperty *sprop; - const char *rval; - -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "") - - off = ss->offsets[i]; - if (off >= 0) - return OFF2STR(&ss->sprinter, off); - - /* - * We must be called from js_DecompileValueGenerator (via Decompile) when - * dereferencing a local that's undefined or null. Search script->atomMap - * for the block containing this local by its stack index, i. - */ - cx = ss->sprinter.context; - script = ss->printer->script; - for (j = 0, n = script->atomMap.length; j < n; j++) { - atom = script->atomMap.vector[j]; - if (ATOM_IS_OBJECT(atom)) { - obj = ATOM_TO_OBJECT(atom); - if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) { - depth = OBJ_BLOCK_DEPTH(cx, obj); - count = OBJ_BLOCK_COUNT(cx, obj); - if ((jsuint)(i - depth) < (jsuint)count) - break; - } - } - } - - LOCAL_ASSERT(j < n); - i -= depth; - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) { - if (sprop->shortid == i) - break; - } - - LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id)); - atom = JSID_TO_ATOM(sprop->id); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - return rval; - -#undef LOCAL_ASSERT -} - -#if JS_HAS_DESTRUCTURING - -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) -#define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op = *pc])->length) - -static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc); - -static jsbytecode * -DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - JSBool *hole) -{ - JSContext *cx; - JSPrinter *jp; - JSOp op; - const JSCodeSpec *cs; - uintN oplen, i; - const char *lval, *xval; - ptrdiff_t todo; - JSAtom *atom; - - *hole = JS_FALSE; - cx = ss->sprinter.context; - jp = ss->printer; - LOAD_OP_DATA(pc); - - switch (op) { - case JSOP_POP: - *hole = JS_TRUE; - todo = SprintPut(&ss->sprinter, ", ", 2); - break; - - case JSOP_DUP: - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - lval = PopStr(ss, JSOP_NOP); - todo = SprintCString(&ss->sprinter, lval); - if (op == JSOP_SETSP) - return pc; - LOCAL_ASSERT(*pc == JSOP_POP); - break; - - case JSOP_SETARG: - case JSOP_SETVAR: - case JSOP_SETGVAR: - case JSOP_SETLOCAL: - LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_SETSP); - /* FALL THROUGH */ - - case JSOP_SETLOCALPOP: - i = GET_UINT16(pc); - atom = NULL; - lval = NULL; - if (op == JSOP_SETARG) - atom = GetSlotAtom(jp, js_GetArgument, i); - else if (op == JSOP_SETVAR) - atom = GetSlotAtom(jp, js_GetLocalVariable, i); - else if (op == JSOP_SETGVAR) - atom = GET_ATOM(cx, jp->script, pc); - else - lval = GetLocal(ss, i); - if (atom) - lval = js_AtomToPrintableString(cx, atom); - LOCAL_ASSERT(lval); - todo = SprintCString(&ss->sprinter, lval); - if (op != JSOP_SETLOCALPOP) { - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op == JSOP_SETSP) - return pc; - LOCAL_ASSERT(op == JSOP_POP); - } - break; - - default: - /* - * We may need to auto-parenthesize the left-most value decompiled - * here, so add back PAREN_SLOP temporarily. Then decompile until the - * opcode that would reduce the stack depth to (ss->top-1), which we - * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for - * the nb parameter. - */ - todo = ss->sprinter.offset; - ss->sprinter.offset = todo + PAREN_SLOP; - pc = Decompile(ss, pc, -ss->top); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); - xval = PopStr(ss, JSOP_NOP); - lval = PopStr(ss, JSOP_GETPROP); - ss->sprinter.offset = todo; - if (*lval == '\0') { - /* lval is from JSOP_BINDNAME, so just print xval. */ - todo = SprintCString(&ss->sprinter, xval); - } else if (*xval == '\0') { - /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */ - todo = SprintCString(&ss->sprinter, lval); - } else { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[ss->opcodes[ss->top+1]].format - & JOF_XMLNAME) - ? "%s.%s" - : "%s[%s]", - lval, xval); - } - break; - } - - if (todo < 0) - return NULL; - - LOCAL_ASSERT(pc < endpc); - pc += oplen; - return pc; -} - -/* - * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring - * left-hand side object or array initialiser, including nested destructuring - * initialisers. On successful return, the decompilation will be pushed on ss - * and the return value will point to the POP or GROUP bytecode following the - * destructuring expression. - * - * At any point, if pc is equal to endpc and would otherwise advance, we stop - * immediately and return endpc. - */ -static jsbytecode * -DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) -{ - ptrdiff_t head, todo; - JSContext *cx; - JSPrinter *jp; - JSOp op, saveop; - const JSCodeSpec *cs; - uintN oplen; - jsint i, lasti; - jsdouble d; - const char *lval; - jsbytecode *pc2; - jsatomid atomIndex; - JSAtom *atom; - jssrcnote *sn; - JSString *str; - JSBool hole; - - LOCAL_ASSERT(*pc == JSOP_DUP); - pc += JSOP_DUP_LENGTH; - - /* - * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP - * chars so the destructuring decompilation accumulates contiguously in - * ss->sprinter starting with "[". - */ - head = SprintPut(&ss->sprinter, "[", 1); - if (head < 0 || !PushOff(ss, head, JSOP_NOP)) - return NULL; - ss->sprinter.offset -= PAREN_SLOP; - LOCAL_ASSERT(head == ss->sprinter.offset - 1); - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); - - cx = ss->sprinter.context; - jp = ss->printer; - lasti = -1; - - while (pc < endpc) { - LOAD_OP_DATA(pc); - saveop = op; - - switch (op) { - case JSOP_POP: - pc += oplen; - goto out; - - /* Handle the optimized number-pushing opcodes. */ - case JSOP_ZERO: d = i = 0; goto do_getelem; - case JSOP_ONE: d = i = 1; goto do_getelem; - case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem; - case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem; - - /* Handle the extended literal form of JSOP_NUMBER. */ - case JSOP_LITOPX: - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - LOCAL_ASSERT(op == JSOP_NUMBER); - goto do_getatom; - - case JSOP_NUMBER: - atomIndex = GET_ATOM_INDEX(pc); - - do_getatom: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - d = *ATOM_TO_DOUBLE(atom); - LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d)); - i = (jsint)d; - - do_getelem: - sn = js_GetSrcNote(jp->script, pc); - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_GETELEM); - - /* Distinguish object from array by opcode or source note. */ - if (saveop == JSOP_LITERAL || - (sn && SN_TYPE(sn) == SRC_INITPROP)) { - *OFF2STR(&ss->sprinter, head) = '{'; - if (Sprint(&ss->sprinter, "%g: ", d) < 0) - return NULL; - } else { - /* Sanity check for the gnarly control flow above. */ - LOCAL_ASSERT(i == d); - - /* Fill in any holes (holes at the end don't matter). */ - while (++lasti < i) { - if (SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - } - } - break; - - case JSOP_LITERAL: - atomIndex = GET_LITERAL_INDEX(pc); - goto do_getatom; - - case JSOP_GETPROP: - *OFF2STR(&ss->sprinter, head) = '{'; - atom = GET_ATOM(cx, jp->script, pc); - str = ATOM_TO_STRING(atom); - if (!QuoteString(&ss->sprinter, str, - js_IsIdentifier(str) ? 0 : (jschar)'\'')) { - return NULL; - } - if (SprintPut(&ss->sprinter, ": ", 2) < 0) - return NULL; - break; - - default: - LOCAL_ASSERT(0); - } - - pc += oplen; - if (pc == endpc) - return pc; - - /* - * Decompile the left-hand side expression whose bytecode starts at pc - * and continues for a bounded number of bytecodes or stack operations - * (and which in any event stops before endpc). - */ - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); - if (!pc) - return NULL; - if (pc == endpc || *pc != JSOP_DUP) - break; - - /* - * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another - * destructuring initialiser abuts this one, and we should stop. This - * happens with source of the form '[a] = [b] = c'. - */ - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_DESTRUCT) - break; - - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - - pc += JSOP_DUP_LENGTH; - } - -out: - lval = OFF2STR(&ss->sprinter, head); - todo = SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1); - if (todo < 0) - return NULL; - return pc; -} - -static jsbytecode * -DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, - jssrcnote *sn, ptrdiff_t *todop) -{ - JSOp op; - const JSCodeSpec *cs; - uintN oplen, start, end, i; - ptrdiff_t todo; - JSBool hole; - const char *rval; - - LOAD_OP_DATA(pc); - LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL); - - todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - ss->sprinter.offset -= PAREN_SLOP; - - for (;;) { - pc += oplen; - if (pc == endpc) - return pc; - pc = DecompileDestructuringLHS(ss, pc, endpc, &hole); - if (!pc) - return NULL; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op != JSOP_PUSH && op != JSOP_GETLOCAL) - break; - if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0) - return NULL; - } - - LOCAL_ASSERT(op == JSOP_SETSP); - if (SprintPut(&ss->sprinter, "] = [", 5) < 0) - return NULL; - - start = GET_UINT16(pc); - end = ss->top - 1; - for (i = start; i < end; i++) { - rval = GetStr(ss, i); - if (Sprint(&ss->sprinter, "%s%s", - (i == start) ? "" : ", ", - (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) { - return NULL; - } - } - - if (SprintPut(&ss->sprinter, "]", 1) < 0) - return NULL; - ss->sprinter.offset = ss->offsets[i]; - ss->top = start; - *todop = todo; - return pc; -} - -#undef LOCAL_ASSERT -#undef LOAD_OP_DATA - -#endif /* JS_HAS_DESTRUCTURING */ - -/* - * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise - * the decompiler starts at pc and continues until it reaches an opcode for - * which decompiling would result in the stack depth equaling -(nb + 1). - */ -static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb) -{ - JSContext *cx; - JSPrinter *jp, *jp2; - jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done; - ptrdiff_t tail, todo, len, oplen, cond, next; - JSOp op, lastop, saveop; - const JSCodeSpec *cs; - jssrcnote *sn, *sn2; - const char *lval, *rval, *xval, *fmt; - jsint i, argc; - char **argv; - jsatomid atomIndex; - JSAtom *atom; - JSObject *obj; - JSFunction *fun; - JSString *str; - JSBool ok; -#if JS_HAS_XML_SUPPORT - JSBool foreach, inXML, quoteAttr; -#else -#define inXML JS_FALSE -#endif - jsval val; - int stackDummy; - - static const char exception_cookie[] = "/*EXCEPTION*/"; - static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; - static const char forelem_cookie[] = "/*FORELEM*/"; - static const char with_cookie[] = "/*WITH*/"; - static const char dot_format[] = "%s.%s"; - static const char index_format[] = "%s[%s]"; - static const char predot_format[] = "%s%s.%s"; - static const char postdot_format[] = "%s.%s%s"; - static const char preindex_format[] = "%s%s[%s]"; - static const char postindex_format[] = "%s[%s]%s"; - static const char ss_format[] = "%s%s"; - -/* - * Local macros - */ -#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return NULL -#define POP_STR() PopStr(ss, op) -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) - -/* - * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to - * common ATOM_TO_STRING(atom) here and near the call sites. - */ -#define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom)) -#define ATOM_IS_KEYWORD(atom) \ - (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \ - JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF) - -/* - * Given an atom already fetched from jp->script's atom map, quote/escape its - * string appropriately into rval, and select fmt from the quoted and unquoted - * alternatives. - */ -#define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \ - JS_BEGIN_MACRO \ - jschar quote_; \ - if (!ATOM_IS_IDENTIFIER(atom)) { \ - quote_ = '\''; \ - fmt = qfmt; \ - } else { \ - quote_ = 0; \ - fmt = ufmt; \ - } \ - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \ - if (!rval) \ - return NULL; \ - JS_END_MACRO - -/* - * Get atom from jp->script's atom map, quote/escape its string appropriately - * into rval, and select fmt from the quoted and unquoted alternatives. - */ -#define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \ - JS_BEGIN_MACRO \ - atom = GET_ATOM(cx, jp->script, pc); \ - GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \ - JS_END_MACRO - - cx = ss->sprinter.context; - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return NULL; - } - - jp = ss->printer; - startpc = pc; - endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb; - forelem_tail = forelem_done = NULL; - tail = -1; - todo = -2; /* NB: different from Sprint() error return. */ - saveop = JSOP_NOP; - sn = NULL; - rval = NULL; -#if JS_HAS_XML_SUPPORT - foreach = inXML = quoteAttr = JS_FALSE; -#endif - - while (nb < 0 || pc < endpc) { - /* - * Move saveop to lastop so prefixed bytecodes can take special action - * while sharing maximal code. Set op and saveop to the new bytecode, - * use op in POP_STR to trigger automatic parenthesization, but push - * saveop at the bottom of the loop if this op pushes. Thus op may be - * set to nop or otherwise mutated to suppress auto-parens. - */ - lastop = saveop; - op = saveop = (JSOp) *pc; - cs = &js_CodeSpec[saveop]; - len = oplen = cs->length; - - if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs) - return pc; - - if (pc + oplen == jp->dvgfence) { - JSStackFrame *fp; - uint32 format, mode, type; - - /* - * Rewrite non-get ops to their "get" format if the error is in - * the bytecode at pc, so we don't decompile more than the error - * expression. - */ - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - format = cs->format; - if (((fp && pc == fp->pc) || - (pc == startpc && cs->nuses != 0)) && - format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR)) { - mode = (format & JOF_MODEMASK); - if (mode == JOF_NAME) { - /* - * JOF_NAME does not imply JOF_CONST, so we must check for - * the QARG and QVAR format types, and translate those to - * JSOP_GETARG or JSOP_GETVAR appropriately, instead of to - * JSOP_NAME. - */ - type = format & JOF_TYPEMASK; - op = (type == JOF_QARG) - ? JSOP_GETARG - : (type == JOF_QVAR) - ? JSOP_GETVAR - : (type == JOF_LOCAL) - ? JSOP_GETLOCAL - : JSOP_NAME; - - i = cs->nuses - js_CodeSpec[op].nuses; - while (--i >= 0) - PopOff(ss, JSOP_NOP); - } else { - /* - * We must replace the faulting pc's bytecode with a - * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM}, - * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to - * throw away the assignment op's right-hand operand and - * decompile it as if it were a GET of its left-hand - * operand. - */ - if (mode == JOF_PROP) { - op = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP; - } else if (mode == JOF_ELEM) { - op = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM; - } else { - /* - * Zero mode means precisely that op is uncategorized - * for our purposes, so we must write per-op special - * case code here. - */ - switch (op) { - case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: - op = JSOP_GETELEM; - break; -#if JS_HAS_LVALUE_RETURN - case JSOP_SETCALL: - op = JSOP_CALL; - break; -#endif - default: - LOCAL_ASSERT(0); - } - } - } - } - - saveop = op; - if (op >= JSOP_LIMIT) { - switch (op) { - case JSOP_GETPROP2: - saveop = JSOP_GETPROP; - break; - case JSOP_GETELEM2: - saveop = JSOP_GETELEM; - break; - default:; - } - } - LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen); - - jp->dvgfence = NULL; - } - - if (cs->token) { - switch (cs->nuses) { - case 2: - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { - /* - * Avoid over-parenthesizing y in x op= y based on its - * expansion: x = x op y (replace y by z = w to see the - * problem). - */ - op = pc[oplen]; - LOCAL_ASSERT(op != saveop); - } - rval = POP_STR(); - lval = POP_STR(); - if (op != saveop) { - /* Print only the right operand of the assignment-op. */ - todo = SprintCString(&ss->sprinter, rval); - op = saveop; - } else if (!inXML) { - todo = Sprint(&ss->sprinter, "%s %s %s", - lval, cs->token, rval); - } else { - /* In XML, just concatenate the two operands. */ - LOCAL_ASSERT(op == JSOP_ADD); - todo = Sprint(&ss->sprinter, ss_format, lval, rval); - } - break; - - case 1: - rval = POP_STR(); - todo = Sprint(&ss->sprinter, ss_format, cs->token, rval); - break; - - case 0: - todo = SprintCString(&ss->sprinter, cs->token); - break; - - default: - todo = -2; - break; - } - } else { - switch (op) { -#define BEGIN_LITOPX_CASE(OP) \ - case OP: \ - atomIndex = GET_ATOM_INDEX(pc); \ - do_##OP: \ - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - -#define END_LITOPX_CASE \ - break; - - case JSOP_NOP: - /* - * Check for a do-while loop, a for-loop with an empty - * initializer part, a labeled statement, a function - * definition, or try/finally. - */ - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_WHILE: - js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n"); - jp->indent += 4; - break; - - case SRC_FOR: - rval = ""; - - do_forloop: - /* Skip the JSOP_NOP or JSOP_POP bytecode. */ - pc++; - - /* Get the cond, next, and loop-closing tail offsets. */ - cond = js_GetSrcNoteOffset(sn, 0); - next = js_GetSrcNoteOffset(sn, 1); - tail = js_GetSrcNoteOffset(sn, 2); - LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0); - - /* Print the keyword and the possibly empty init-part. */ - js_printf(jp, "\tfor (%s;", rval); - - if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) { - /* Decompile the loop condition. */ - DECOMPILE_CODE(pc, cond); - js_printf(jp, " %s", POP_STR()); - } - - /* Need a semicolon whether or not there was a cond. */ - js_puts(jp, ";"); - - if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) { - /* Decompile the loop updater. */ - DECOMPILE_CODE(pc + next, tail - next - 1); - js_printf(jp, " %s", POP_STR()); - } - - /* Do the loop body. */ - js_printf(SET_MAYBE_BRACE(jp), ") {\n"); - jp->indent += 4; - oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0; - DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - - /* Set len so pc skips over the entire loop. */ - len = tail + js_CodeSpec[pc[tail]].length; - break; - - case SRC_LABEL: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - jp->indent -= 4; - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s:\n", rval); - jp->indent += 4; - break; - - case SRC_LABELBRACE: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s: {\n", rval); - jp->indent += 4; - break; - - case SRC_ENDBRACE: - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - - case SRC_FUNCDEF: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); - do_function: - obj = ATOM_TO_OBJECT(atom); - fun = (JSFunction *) JS_GetPrivate(cx, obj); - jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun), - jp->indent, jp->pretty); - if (!jp2) - return NULL; - jp2->scope = jp->scope; - js_puts(jp2, "\n"); - ok = js_DecompileFunction(jp2, fun); - if (ok) { - js_puts(jp2, "\n"); - str = js_GetPrinterOutput(jp2); - if (str) - js_printf(jp, "%s\n", JS_GetStringBytes(str)); - else - ok = JS_FALSE; - } - js_DestroyPrinter(jp2); - if (!ok) - return NULL; - - break; - - case SRC_BRACE: - js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); - jp->indent += 4; - len = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, len - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - - default:; - } - break; - - case JSOP_GROUP: - cs = &js_CodeSpec[lastop]; - if ((cs->prec != 0 && - cs->prec == js_CodeSpec[pc[JSOP_GROUP_LENGTH]].prec) || - pc[JSOP_GROUP_LENGTH] == JSOP_PUSHOBJ || - pc[JSOP_GROUP_LENGTH] == JSOP_DUP) { - /* - * Force parens if this JSOP_GROUP forced re-association - * against precedence, or if this is a call or constructor - * expression, or if it is destructured (JSOP_DUP). - * - * This is necessary to handle the operator new grammar, - * by which new x(y).z means (new x(y))).z. For example - * new (x(y).z) must decompile with the constructor - * parenthesized, but normal precedence has JSOP_GETPROP - * (for the final .z) higher than JSOP_NEW. In general, - * if the call or constructor expression is parenthesized, - * we preserve parens. - */ - op = JSOP_NAME; - rval = POP_STR(); - todo = SprintCString(&ss->sprinter, rval); - } else { - /* - * Don't explicitly parenthesize -- just fix the top - * opcode so that the auto-parens magic in PopOff can do - * its thing. - */ - LOCAL_ASSERT(ss->top != 0); - ss->opcodes[ss->top-1] = saveop = lastop; - todo = -2; - } - break; - - case JSOP_STARTITER: - todo = -2; - break; - - case JSOP_PUSH: -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) - return NULL; - LOCAL_ASSERT(*pc == JSOP_SETSP); - len = oplen = JSOP_SETSP_LENGTH; - goto end_groupassignment; - } -#endif - /* FALL THROUGH */ - - case JSOP_PUSHOBJ: - case JSOP_BINDNAME: - do_JSOP_BINDNAME: - todo = Sprint(&ss->sprinter, ""); - break; - - case JSOP_TRY: - js_printf(CLEAR_MAYBE_BRACE(jp), "\ttry {\n"); - jp->indent += 4; - todo = -2; - break; - - case JSOP_FINALLY: - jp->indent -= 4; - js_printf(CLEAR_MAYBE_BRACE(jp), "\t} finally {\n"); - jp->indent += 4; - - /* - * We must push an empty string placeholder for gosub's return - * address, popped by JSOP_RETSUB and counted by script->depth - * but not by ss->top (see JSOP_SETSP, below). - */ - todo = Sprint(&ss->sprinter, exception_cookie); - if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; - todo = Sprint(&ss->sprinter, retsub_pc_cookie); - break; - - case JSOP_RETSUB: - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0); - lval = POP_STR(); - LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0); - todo = -2; - break; - - case JSOP_SWAP: - /* - * We don't generate this opcode currently, and previously we - * did not need to decompile it. If old, serialized bytecode - * uses it still, we should fall through and set todo = -2. - */ - /* FALL THROUGH */ - - case JSOP_GOSUB: - case JSOP_GOSUBX: - /* - * JSOP_GOSUB and GOSUBX have no effect on the decompiler's - * string stack because the next op in bytecode order finds - * the stack balanced by a JSOP_RETSUB executed elsewhere. - */ - todo = -2; - break; - - case JSOP_SETSP: - { - uintN newtop, oldtop, i; - - /* - * The compiler models operand stack depth and fixes the stack - * pointer on entry to a catch clause based on its depth model. - * The decompiler must match the code generator's model, which - * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops. - */ - newtop = (uintN) GET_UINT16(pc); - oldtop = ss->top; - LOCAL_ASSERT(newtop <= oldtop); - todo = -2; - -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - todo = Sprint(&ss->sprinter, "%s[] = [", - VarPrefix(sn)); - if (todo < 0) - return NULL; - for (i = newtop; i < oldtop; i++) { - rval = OFF2STR(&ss->sprinter, ss->offsets[i]); - if (Sprint(&ss->sprinter, ss_format, - (i == newtop) ? "" : ", ", - (i == oldtop - 1 && *rval == '\0') - ? ", " : rval) < 0) { - return NULL; - } - } - if (SprintPut(&ss->sprinter, "]", 1) < 0) - return NULL; - - /* - * Kill newtop before the end_groupassignment: label by - * retracting/popping early. Control will either jump to - * do_forloop: or do_letheadbody: or else break from our - * case JSOP_SETSP: after the switch (*pc2) below. - */ - if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); - ss->top = newtop; - } - - end_groupassignment: - /* - * Thread directly to the next opcode if we can, to handle - * the special cases of a group assignment in the first or - * last part of a for(;;) loop head, or in a let block or - * expression head. - * - * NB: todo at this point indexes space in ss->sprinter - * that is liable to be overwritten. The code below knows - * exactly how long rval lives, or else copies it down via - * SprintCString. - */ - rval = OFF2STR(&ss->sprinter, todo); - todo = -2; - pc2 = pc + oplen; - switch (*pc2) { - case JSOP_NOP: - /* First part of for(;;) or let block/expr head. */ - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - if (SN_TYPE(sn) == SRC_FOR) { - pc = pc2; - goto do_forloop; - } - if (SN_TYPE(sn) == SRC_DECL) { - if (ss->top == jp->script->depth) { - /* - * This must be an empty destructuring - * in the head of a let whose body block - * is also empty. - */ - pc = pc2 + 1; - len = js_GetSrcNoteOffset(sn, 0); - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK); - js_printf(jp, "\tlet (%s) {\n", rval); - js_printf(jp, "\t}\n"); - goto end_setsp; - } - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) - return NULL; - op = JSOP_POP; - pc = pc2 + 1; - goto do_letheadbody; - } - } - break; - - case JSOP_GOTO: - case JSOP_GOTOX: - /* Third part of for(;;) loop head. */ - cond = GetJumpOffset(pc2, pc2); - sn = js_GetSrcNote(jp->script, pc2 + cond - 1); - if (sn && SN_TYPE(sn) == SRC_FOR) { - todo = SprintCString(&ss->sprinter, rval); - saveop = JSOP_NOP; - } - break; - } - - /* - * If control flow reaches this point with todo still -2, - * just print rval as an expression statement. - */ - if (todo == -2) - js_printf(jp, "\t%s;\n", rval); - end_setsp: - break; - } -#endif - if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); - ss->top = newtop; - } - break; - } - - case JSOP_EXCEPTION: - /* The catch decompiler handles this op itself. */ - LOCAL_ASSERT(JS_FALSE); - break; - - case JSOP_POP: - /* - * By default, do not automatically parenthesize when popping - * a stacked expression decompilation. We auto-parenthesize - * only when JSOP_POP is annotated with SRC_PCDELTA, meaning - * comma operator. - */ - op = JSOP_POPV; - /* FALL THROUGH */ - - case JSOP_POPV: - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_FOR: - /* Force parens around 'in' expression at 'for' front. */ - if (ss->opcodes[ss->top-1] == JSOP_IN) - op = JSOP_LSH; - rval = POP_STR(); - todo = -2; - goto do_forloop; - - case SRC_PCDELTA: - /* Comma operator: use JSOP_POP for correct precedence. */ - op = JSOP_POP; - - /* Pop and save to avoid blowing stack depth budget. */ - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - - /* - * The offset tells distance to the end of the right-hand - * operand of the comma operator. - */ - done = pc + len; - pc += js_GetSrcNoteOffset(sn, 0); - len = 0; - - if (!Decompile(ss, done, pc - done)) { - JS_free(cx, (char *)lval); - return NULL; - } - - /* Pop Decompile result and print comma expression. */ - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s, %s", lval, rval); - JS_free(cx, (char *)lval); - break; - - case SRC_HIDDEN: - /* Hide this pop, it's from a goto in a with or for/in. */ - todo = -2; - break; - - case SRC_DECL: - /* This pop is at the end of the let block/expr head. */ - pc += JSOP_POP_LENGTH; -#if JS_HAS_DESTRUCTURING - do_letheadbody: -#endif - len = js_GetSrcNoteOffset(sn, 0); - if (pc[len] == JSOP_LEAVEBLOCK) { - js_printf(CLEAR_MAYBE_BRACE(jp), "\tlet (%s) {\n", - POP_STR()); - jp->indent += 4; - DECOMPILE_CODE(pc, len); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - } else { - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); - - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - - if (!Decompile(ss, pc, len)) { - JS_free(cx, (char *)lval); - return NULL; - } - rval = POP_STR(); - todo = Sprint(&ss->sprinter, - (*rval == '{') - ? "let (%s) (%s)" - : "let (%s) %s", - lval, rval); - JS_free(cx, (char *)lval); - } - break; - - default: - /* Turn off parens around a yield statement. */ - if (ss->opcodes[ss->top-1] == JSOP_YIELD) - op = JSOP_NOP; - - rval = POP_STR(); - if (*rval != '\0') { -#if JS_HAS_BLOCK_SCOPE - /* - * If a let declaration is the only child of a control - * structure that does not require braces, it must not - * be braced. If it were braced explicitly, it would - * be bracketed by JSOP_ENTERBLOCK/JSOP_LEAVEBLOCK. - */ - if (jp->braceState == MAYBE_BRACE && - pc + JSOP_POP_LENGTH == endpc && - !strncmp(rval, var_prefix[SRC_DECL_LET], 4) && - rval[4] != '(') { - SetDontBrace(jp); - } -#endif - js_printf(jp, - (*rval == '{' || - (strncmp(rval, js_function_str, 8) == 0 && - rval[8] == ' ')) - ? "\t(%s);\n" - : "\t%s;\n", - rval); - } - todo = -2; - break; - } - break; - - case JSOP_POP2: - case JSOP_ENDITER: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - (void) PopOff(ss, op); - if (op == JSOP_POP2) - (void) PopOff(ss, op); - break; - - case JSOP_ENTERWITH: - LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc)); - rval = POP_STR(); - js_printf(SET_MAYBE_BRACE(jp), "\twith (%s) {\n", rval); - jp->indent += 4; - todo = Sprint(&ss->sprinter, with_cookie); - break; - - case JSOP_LEAVEWITH: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, with_cookie) == 0); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; - - BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK) - { - JSAtom **atomv, *smallv[5]; - JSScopeProperty *sprop; - - obj = ATOM_TO_OBJECT(atom); - argc = OBJ_BLOCK_COUNT(cx, obj); - if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) { - atomv = smallv; - } else { - atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *)); - if (!atomv) - return NULL; - } - - /* From here on, control must flow through enterblock_out. */ - for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; - sprop = sprop->parent) { - if (!(sprop->flags & SPROP_HAS_SHORTID)) - continue; - LOCAL_ASSERT(sprop->shortid < argc); - atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id); - } - ok = JS_TRUE; - for (i = 0; i < argc; i++) { - atom = atomv[i]; - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval || - !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { - ok = JS_FALSE; - goto enterblock_out; - } - } - - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { -#if JS_HAS_BLOCK_SCOPE - case SRC_BRACE: - js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n"); - jp->indent += 4; - len = js_GetSrcNoteOffset(sn, 0); - ok = Decompile(ss, pc + oplen, len - oplen) != NULL; - if (!ok) - goto enterblock_out; - jp->indent -= 4; - js_printf(jp, "\t}\n"); - break; -#endif - - case SRC_CATCH: - jp->indent -= 4; - js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch ("); - - pc2 = pc; - pc += oplen; - LOCAL_ASSERT(*pc == JSOP_EXCEPTION); - pc += JSOP_EXCEPTION_LENGTH; - if (*pc == JSOP_DUP) { - sn2 = js_GetSrcNote(jp->script, pc); - if (sn2 && SN_TYPE(sn2) == SRC_HIDDEN) { - /* - * This is a hidden dup to save the exception for - * later. It must exist only when the catch has - * an exception guard. - */ - LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0); - pc += JSOP_DUP_LENGTH; - } - } -#if JS_HAS_DESTRUCTURING - if (*pc == JSOP_DUP) { - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - goto enterblock_out; - } - LOCAL_ASSERT(*pc == JSOP_POP); - pc += JSOP_POP_LENGTH; - lval = PopStr(ss, JSOP_NOP); - js_puts(jp, lval); - } else { -#endif - LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP); - i = GET_UINT16(pc); - pc += JSOP_SETLOCALPOP_LENGTH; - atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; - str = ATOM_TO_STRING(atom); - if (!QuoteString(&jp->sprinter, str, 0)) { - ok = JS_FALSE; - goto enterblock_out; - } -#if JS_HAS_DESTRUCTURING - } -#endif - - len = js_GetSrcNoteOffset(sn, 0); - if (len) { - len -= PTRDIFF(pc, pc2, jsbytecode); - LOCAL_ASSERT(len > 0); - js_printf(jp, " if "); - ok = Decompile(ss, pc, len) != NULL; - if (!ok) - goto enterblock_out; - js_printf(jp, "%s", POP_STR()); - pc += len; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - pc += js_CodeSpec[*pc].length; - } - - js_printf(jp, ") {\n"); - jp->indent += 4; - len = 0; - break; - } - - todo = -2; - - enterblock_out: - if (atomv != smallv) - JS_free(cx, atomv); - if (!ok) - return NULL; - } - END_LITOPX_CASE - - case JSOP_LEAVEBLOCK: - case JSOP_LEAVEBLOCKEXPR: - { - uintN top, depth; - - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (op == JSOP_LEAVEBLOCKEXPR) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE); - rval = POP_STR(); - } else if (sn) { - LOCAL_ASSERT(op == JSOP_LEAVEBLOCK); - if (SN_TYPE(sn) == SRC_HIDDEN) - break; - LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH); - LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top); - } - top = ss->top; - depth = GET_UINT16(pc); - LOCAL_ASSERT(top >= depth); - top -= depth; - ss->top = top; - ss->sprinter.offset = GetOff(ss, top); - if (op == JSOP_LEAVEBLOCKEXPR) - todo = SprintCString(&ss->sprinter, rval); - break; - } - - case JSOP_GETLOCAL: - i = GET_UINT16(pc); - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT((uintN)i < ss->top); - rval = GetLocal(ss, i); - -#if JS_HAS_DESTRUCTURING - if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) { - pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo); - if (!pc) - return NULL; - LOCAL_ASSERT(*pc == JSOP_SETSP); - len = oplen = JSOP_SETSP_LENGTH; - goto end_groupassignment; - } -#endif - - todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval); - break; - - case JSOP_SETLOCAL: - case JSOP_SETLOCALPOP: - i = GET_UINT16(pc); - lval = GetStr(ss, i); - rval = POP_STR(); - goto do_setlval; - - case JSOP_INCLOCAL: - case JSOP_DECLOCAL: - i = GET_UINT16(pc); - lval = GetLocal(ss, i); - goto do_inclval; - - case JSOP_LOCALINC: - case JSOP_LOCALDEC: - i = GET_UINT16(pc); - lval = GetLocal(ss, i); - goto do_lvalinc; - - case JSOP_FORLOCAL: - i = GET_UINT16(pc); - lval = GetStr(ss, i); - atom = NULL; - goto do_forlvalinloop; - - case JSOP_RETRVAL: - todo = -2; - break; - - case JSOP_SETRVAL: - case JSOP_RETURN: - rval = POP_STR(); - if (*rval != '\0') - js_printf(jp, "\t%s %s;\n", js_return_str, rval); - else - js_printf(jp, "\t%s;\n", js_return_str); - todo = -2; - break; - -#if JS_HAS_GENERATORS - case JSOP_YIELD: - op = JSOP_SETNAME; /* turn off most parens */ - rval = POP_STR(); - todo = (*rval != '\0') - ? Sprint(&ss->sprinter, - (strncmp(rval, js_yield_str, 5) == 0 && - (rval[5] == ' ' || rval[5] == '\0')) - ? "%s (%s)" - : "%s %s", - js_yield_str, rval) - : SprintCString(&ss->sprinter, js_yield_str); - break; - - case JSOP_ARRAYPUSH: - { - uintN pos, blockpos, startpos; - ptrdiff_t start; - - rval = POP_STR(); - pos = ss->top; - while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK && - op != JSOP_NEWINIT) { - LOCAL_ASSERT(pos != 0); - } - blockpos = pos; - while (ss->opcodes[pos] == JSOP_ENTERBLOCK) { - if (pos == 0) - break; - --pos; - } - LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); - startpos = pos; - start = ss->offsets[pos]; - LOCAL_ASSERT(ss->sprinter.base[start] == '[' || - ss->sprinter.base[start] == '#'); - pos = blockpos; - while (ss->opcodes[++pos] == JSOP_STARTITER) - LOCAL_ASSERT(pos < ss->top); - LOCAL_ASSERT(pos < ss->top); - xval = OFF2STR(&ss->sprinter, ss->offsets[pos]); - lval = OFF2STR(&ss->sprinter, start); - RETRACT(&ss->sprinter, lval); - todo = Sprint(&ss->sprinter, "%s%s%.*s", - lval, rval, rval - xval, xval); - if (todo < 0) - return NULL; - ss->offsets[startpos] = todo; - todo = -2; - break; - } -#endif - - case JSOP_THROWING: - todo = -2; - break; - - case JSOP_THROW: - sn = js_GetSrcNote(jp->script, pc); - todo = -2; - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - break; - rval = POP_STR(); - js_printf(jp, "\t%s %s;\n", cs->name, rval); - break; - - case JSOP_GOTO: - case JSOP_GOTOX: - sn = js_GetSrcNote(jp->script, pc); - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_CONT2LABEL: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\tcontinue %s;\n", rval); - break; - case SRC_CONTINUE: - js_printf(jp, "\tcontinue;\n"); - break; - case SRC_BREAK2LABEL: - atom = js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) js_GetSrcNoteOffset(sn, 0)); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\tbreak %s;\n", rval); - break; - case SRC_HIDDEN: - break; - default: - js_printf(jp, "\tbreak;\n"); - break; - } - todo = -2; - break; - - case JSOP_IFEQ: - case JSOP_IFEQX: - { - JSBool elseif = JS_FALSE; - - if_again: - len = GetJumpOffset(pc, pc); - sn = js_GetSrcNote(jp->script, pc); - - switch (sn ? SN_TYPE(sn) : SRC_NULL) { - case SRC_IF: - case SRC_IF_ELSE: - op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); - if (ss->inArrayInit) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); - if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) - return NULL; - } else { - js_printf(SET_MAYBE_BRACE(jp), - elseif ? " if (%s) {\n" : "\tif (%s) {\n", - rval); - jp->indent += 4; - } - - if (SN_TYPE(sn) == SRC_IF) { - DECOMPILE_CODE(pc + oplen, len - oplen); - } else { - LOCAL_ASSERT(!ss->inArrayInit); - tail = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - pc += tail; - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); - oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); - js_printf(jp, "\t} else"); - - /* - * If the second offset for sn is non-zero, it tells - * the distance from the goto around the else, to the - * ifeq for the if inside the else that forms an "if - * else if" chain. Thus cond spans the condition of - * the second if, so we simply decompile it and start - * over at label if_again. - */ - cond = js_GetSrcNoteOffset(sn, 1); - if (cond != 0) { - DECOMPILE_CODE(pc + oplen, cond - oplen); - pc += cond; - elseif = JS_TRUE; - goto if_again; - } - - js_printf(SET_MAYBE_BRACE(jp), " {\n"); - jp->indent += 4; - DECOMPILE_CODE(pc + oplen, len - oplen); - } - - if (!ss->inArrayInit) { - jp->indent -= 4; - js_printf(jp, "\t}\n"); - } - todo = -2; - break; - - case SRC_WHILE: - rval = POP_STR(); - js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval); - jp->indent += 4; - tail = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - break; - - case SRC_COND: - xval = JS_strdup(cx, POP_STR()); - if (!xval) - return NULL; - len = js_GetSrcNoteOffset(sn, 0); - DECOMPILE_CODE(pc + oplen, len - oplen); - lval = JS_strdup(cx, POP_STR()); - if (!lval) { - JS_free(cx, (void *)xval); - return NULL; - } - pc += len; - LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX); - oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); - DECOMPILE_CODE(pc + oplen, len - oplen); - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s ? %s : %s", - xval, lval, rval); - JS_free(cx, (void *)xval); - JS_free(cx, (void *)lval); - break; - - default: - break; - } - break; - } - - case JSOP_IFNE: - case JSOP_IFNEX: - /* Currently, this must be a do-while loop's upward branch. */ - jp->indent -= 4; - js_printf(jp, "\t} while (%s);\n", POP_STR()); - todo = -2; - break; - - case JSOP_OR: - case JSOP_ORX: - xval = "||"; - - do_logical_connective: - /* Top of stack is the first clause in a disjunction (||). */ - lval = JS_strdup(cx, POP_STR()); - if (!lval) - return NULL; - done = pc + GetJumpOffset(pc, pc); - pc += len; - len = PTRDIFF(done, pc, jsbytecode); - DECOMPILE_CODE(pc, len); - rval = POP_STR(); - if (jp->pretty && - jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) { - rval = JS_strdup(cx, rval); - if (!rval) { - tail = -1; - } else { - todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval); - tail = Sprint(&ss->sprinter, "%*s%s", - jp->indent + 4, "", rval); - JS_free(cx, (char *)rval); - } - if (tail < 0) - todo = -1; - } else { - todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval); - } - JS_free(cx, (char *)lval); - break; - - case JSOP_AND: - case JSOP_ANDX: - xval = "&&"; - goto do_logical_connective; - - case JSOP_FORARG: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_fornameinloop; - - case JSOP_FORVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_fornameinloop; - - case JSOP_FORNAME: - atom = GET_ATOM(cx, jp->script, pc); - - do_fornameinloop: - lval = ""; - do_forlvalinloop: - sn = js_GetSrcNote(jp->script, pc); - xval = NULL; - goto do_forinloop; - - case JSOP_FORPROP: - xval = NULL; - atom = GET_ATOM(cx, jp->script, pc); - if (!ATOM_IS_IDENTIFIER(atom)) { - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - (jschar)'\''); - if (!xval) - return NULL; - atom = NULL; - } - lval = POP_STR(); - sn = NULL; - - do_forinloop: - pc += oplen; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - oplen = js_CodeSpec[*pc].length; - len = GetJumpOffset(pc, pc); - sn2 = js_GetSrcNote(jp->script, pc); - tail = js_GetSrcNoteOffset(sn2, 0); - - do_forinhead: - if (!atom && xval) { - /* - * If xval is not a dummy empty string, we have to strdup - * it to save it from being clobbered by the first Sprint - * below. Standard dumb decompiler operating procedure! - */ - if (*xval == '\0') { - xval = NULL; - } else { - xval = JS_strdup(cx, xval); - if (!xval) - return NULL; - } - } - -#if JS_HAS_XML_SUPPORT - if (foreach) { - foreach = JS_FALSE; - todo = Sprint(&ss->sprinter, "for %s (%s%s", - js_each_str, VarPrefix(sn), lval); - } else -#endif - { - todo = Sprint(&ss->sprinter, "for (%s%s", - VarPrefix(sn), lval); - } - if (atom) { - if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0) - return NULL; - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!xval) - return NULL; - } else if (xval) { - LOCAL_ASSERT(*xval != '\0'); - ok = (Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? ".%s" - : "[%s]", - xval) - >= 0); - JS_free(cx, (char *)xval); - if (!ok) - return NULL; - } - if (todo < 0) - return NULL; - - lval = OFF2STR(&ss->sprinter, todo); - rval = GetStr(ss, ss->top-1); - RETRACT(&ss->sprinter, rval); - if (ss->inArrayInit) { - todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval); - if (todo < 0) - return NULL; - ss->offsets[ss->top-1] = todo; - ss->sprinter.offset += PAREN_SLOP; - DECOMPILE_CODE(pc + oplen, tail - oplen); - } else { - js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n", - lval, rval); - jp->indent += 4; - DECOMPILE_CODE(pc + oplen, tail - oplen); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - } - todo = -2; - break; - - case JSOP_FORELEM: - pc++; - LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); - len = js_CodeSpec[*pc].length; - - /* - * Arrange for the JSOP_ENUMELEM case to set tail for use by - * do_forinhead: code that uses on it to find the loop-closing - * jump (whatever its format, normal or extended), in order to - * bound the recursively decompiled loop body. - */ - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(!forelem_tail); - forelem_tail = pc + js_GetSrcNoteOffset(sn, 0); - - /* - * This gets a little wacky. Only the length of the for loop - * body PLUS the element-indexing expression is known here, so - * we pass the after-loop pc to the JSOP_ENUMELEM case, which - * is immediately below, to decompile that helper bytecode via - * the 'forelem_done' local. - * - * Since a for..in loop can't nest in the head of another for - * loop, we can use forelem_{tail,done} singletons to remember - * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto) - * to label do_forinhead. - */ - LOCAL_ASSERT(!forelem_done); - forelem_done = pc + GetJumpOffset(pc, pc); - - /* Our net stack balance after forelem;ifeq is +1. */ - todo = SprintCString(&ss->sprinter, forelem_cookie); - break; - - case JSOP_ENUMELEM: - case JSOP_ENUMCONSTELEM: - /* - * The stack has the object under the (top) index expression. - * The "rval" property id is underneath those two on the stack. - * The for loop body net and gross lengths can now be adjusted - * to account for the length of the indexing expression that - * came after JSOP_FORELEM and before JSOP_ENUMELEM. - */ - atom = NULL; - xval = POP_STR(); - op = JSOP_GETELEM; /* lval must have high precedence */ - lval = POP_STR(); - op = saveop; - rval = POP_STR(); - LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0); - LOCAL_ASSERT(forelem_tail > pc); - tail = forelem_tail - pc; - forelem_tail = NULL; - LOCAL_ASSERT(forelem_done > pc); - len = forelem_done - pc; - forelem_done = NULL; - goto do_forinhead; - -#if JS_HAS_GETTER_SETTER - case JSOP_GETTER: - case JSOP_SETTER: - todo = -2; - break; -#endif - - case JSOP_DUP2: - rval = GetStr(ss, ss->top-2); - todo = SprintCString(&ss->sprinter, rval); - if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2])) - return NULL; - /* FALL THROUGH */ - - case JSOP_DUP: -#if JS_HAS_DESTRUCTURING - sn = js_GetSrcNote(jp->script, pc); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT); - pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) - return NULL; - len = 0; - lval = POP_STR(); - op = saveop = JSOP_ENUMELEM; - rval = POP_STR(); - - if (strcmp(rval, forelem_cookie) == 0) { - LOCAL_ASSERT(forelem_tail > pc); - tail = forelem_tail - pc; - forelem_tail = NULL; - LOCAL_ASSERT(forelem_done > pc); - len = forelem_done - pc; - forelem_done = NULL; - xval = NULL; - atom = NULL; - - /* - * Null sn if this is a 'for (var [k, v] = i in o)' - * loop, because 'var [k, v = i;' has already been - * hoisted. - */ - if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR) - sn = NULL; - goto do_forinhead; - } - - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); - break; - } -#endif - - rval = GetStr(ss, ss->top-1); - saveop = ss->opcodes[ss->top-1]; - todo = SprintCString(&ss->sprinter, rval); - break; - - case JSOP_SETARG: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_setname; - - case JSOP_SETVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_setname; - - case JSOP_SETCONST: - case JSOP_SETNAME: - case JSOP_SETGVAR: - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_SETCONST: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - - do_setname: - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - rval = POP_STR(); - if (op == JSOP_SETNAME) - (void) PopOff(ss, op); - - do_setlval: - sn = js_GetSrcNote(jp->script, pc - 1); - if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { - todo = Sprint(&ss->sprinter, "%s %s= %s", - lval, - (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token, - rval); - } else { - sn = js_GetSrcNote(jp->script, pc); - todo = Sprint(&ss->sprinter, "%s%s = %s", - VarPrefix(sn), lval, rval); - } - if (op == JSOP_SETLOCALPOP) { - if (!PushOff(ss, todo, saveop)) - return NULL; - rval = POP_STR(); - LOCAL_ASSERT(*rval != '\0'); - js_printf(jp, "\t%s;\n", rval); - todo = -2; - } - break; - - case JSOP_NEW: - case JSOP_CALL: - case JSOP_EVAL: -#if JS_HAS_LVALUE_RETURN - case JSOP_SETCALL: -#endif - op = JSOP_SETNAME; /* turn off most parens */ - argc = GET_ARGC(pc); - argv = (char **) - JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv); - if (!argv) - return NULL; - - ok = JS_TRUE; - for (i = argc; i > 0; i--) { - argv[i] = JS_strdup(cx, POP_STR()); - if (!argv[i]) { - ok = JS_FALSE; - break; - } - } - - /* Skip the JSOP_PUSHOBJ-created empty string. */ - LOCAL_ASSERT(ss->top >= 2); - (void) PopOff(ss, op); - - op = saveop; - argv[0] = JS_strdup(cx, POP_STR()); - if (!argv[i]) - ok = JS_FALSE; - - lval = "(", rval = ")"; - if (op == JSOP_NEW) { - if (argc == 0) - lval = rval = ""; - todo = Sprint(&ss->sprinter, "%s %s%s", - js_new_str, argv[0], lval); - } else { - todo = Sprint(&ss->sprinter, ss_format, - argv[0], lval); - } - if (todo < 0) - ok = JS_FALSE; - - for (i = 1; i <= argc; i++) { - if (!argv[i] || - Sprint(&ss->sprinter, ss_format, - argv[i], (i < argc) ? ", " : "") < 0) { - ok = JS_FALSE; - break; - } - } - if (Sprint(&ss->sprinter, rval) < 0) - ok = JS_FALSE; - - for (i = 0; i <= argc; i++) { - if (argv[i]) - JS_free(cx, argv[i]); - } - JS_free(cx, argv); - if (!ok) - return NULL; -#if JS_HAS_LVALUE_RETURN - if (op == JSOP_SETCALL) { - if (!PushOff(ss, todo, op)) - return NULL; - todo = Sprint(&ss->sprinter, ""); - } -#endif - break; - - case JSOP_DELNAME: - atom = GET_ATOM(cx, jp->script, pc); - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_delete_lval: - todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); - break; - - case JSOP_DELPROP: - GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval); - break; - - case JSOP_DELELEM: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = saveop; - lval = POP_STR(); - if (*xval == '\0') - goto do_delete_lval; - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? "%s %s.%s" - : "%s %s[%s]", - js_delete_str, lval, xval); - break; - -#if JS_HAS_XML_SUPPORT - case JSOP_DELDESC: - xval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s..%s", - js_delete_str, lval, xval); - break; -#endif - - case JSOP_TYPEOFEXPR: - case JSOP_TYPEOF: - case JSOP_VOID: - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval); - break; - - case JSOP_INCARG: - case JSOP_DECARG: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_incatom; - - case JSOP_INCVAR: - case JSOP_DECVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_incatom; - - case JSOP_INCNAME: - case JSOP_DECNAME: - case JSOP_INCGVAR: - case JSOP_DECGVAR: - atom = GET_ATOM(cx, jp->script, pc); - do_incatom: - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_inclval: - todo = Sprint(&ss->sprinter, ss_format, - js_incop_strs[!(cs->format & JOF_INC)], lval); - break; - - case JSOP_INCPROP: - case JSOP_DECPROP: - GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, - js_incop_strs[!(cs->format & JOF_INC)], - lval, rval); - break; - - case JSOP_INCELEM: - case JSOP_DECELEM: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = JSOP_GETELEM; - lval = POP_STR(); - if (*xval != '\0') { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? predot_format - : preindex_format, - js_incop_strs[!(cs->format & JOF_INC)], - lval, xval); - } else { - todo = Sprint(&ss->sprinter, ss_format, - js_incop_strs[!(cs->format & JOF_INC)], lval); - } - break; - - case JSOP_ARGINC: - case JSOP_ARGDEC: - atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc)); - LOCAL_ASSERT(atom); - goto do_atominc; - - case JSOP_VARINC: - case JSOP_VARDEC: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_atominc; - - case JSOP_NAMEINC: - case JSOP_NAMEDEC: - case JSOP_GVARINC: - case JSOP_GVARDEC: - atom = GET_ATOM(cx, jp->script, pc); - do_atominc: - lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!lval) - return NULL; - RETRACT(&ss->sprinter, lval); - do_lvalinc: - todo = Sprint(&ss->sprinter, ss_format, - lval, js_incop_strs[!(cs->format & JOF_INC)]); - break; - - case JSOP_PROPINC: - case JSOP_PROPDEC: - GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, lval, rval, - js_incop_strs[!(cs->format & JOF_INC)]); - break; - - case JSOP_ELEMINC: - case JSOP_ELEMDEC: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = JSOP_GETELEM; - lval = POP_STR(); - if (*xval != '\0') { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? postdot_format - : postindex_format, - lval, xval, - js_incop_strs[!(cs->format & JOF_INC)]); - } else { - todo = Sprint(&ss->sprinter, ss_format, - lval, js_incop_strs[!(cs->format & JOF_INC)]); - } - break; - - case JSOP_GETPROP2: - op = JSOP_GETPROP; - (void) PopOff(ss, lastop); - /* FALL THROUGH */ - - case JSOP_GETPROP: - case JSOP_GETXPROP: - atom = GET_ATOM(cx, jp->script, pc); - - do_getprop: - GET_QUOTE_AND_FMT(index_format, dot_format, rval); - - do_getprop_lval: - lval = POP_STR(); - todo = Sprint(&ss->sprinter, fmt, lval, rval); - break; - -#if JS_HAS_XML_SUPPORT - BEGIN_LITOPX_CASE(JSOP_GETMETHOD) - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_PCBASE) - goto do_getprop; - GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval); - goto do_getprop_lval; - - BEGIN_LITOPX_CASE(JSOP_SETMETHOD) - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_PCBASE) - goto do_setprop; - GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s", - "%s.function::%s %s= %s", - xval); - goto do_setprop_rval; -#endif - - case JSOP_SETPROP: - atom = GET_ATOM(cx, jp->script, pc); - - do_setprop: - GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval); - - do_setprop_rval: - rval = POP_STR(); - - /* - * Force precedence below the numeric literal opcodes, so that - * 42..foo or 10000..toString(16), e.g., decompile with parens - * around the left-hand side of dot. - */ - op = JSOP_GETPROP; - lval = POP_STR(); - sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, fmt, lval, xval, - (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token - : "", - rval); - break; - - case JSOP_GETELEM2: - op = JSOP_GETELEM; - (void) PopOff(ss, lastop); - /* FALL THROUGH */ - - case JSOP_GETELEM: - case JSOP_GETXELEM: - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - op = saveop; - lval = POP_STR(); - if (*xval == '\0') { - todo = Sprint(&ss->sprinter, "%s", lval); - } else { - todo = Sprint(&ss->sprinter, - (js_CodeSpec[lastop].format & JOF_XMLNAME) - ? dot_format - : index_format, - lval, xval); - } - break; - - case JSOP_SETELEM: - rval = POP_STR(); - op = JSOP_NOP; /* turn off parens */ - xval = POP_STR(); - cs = &js_CodeSpec[ss->opcodes[ss->top]]; - op = JSOP_GETELEM; /* lval must have high precedence */ - lval = POP_STR(); - op = saveop; - if (*xval == '\0') - goto do_setlval; - sn = js_GetSrcNote(jp->script, pc - 1); - todo = Sprint(&ss->sprinter, - (cs->format & JOF_XMLNAME) - ? "%s.%s %s= %s" - : "%s[%s] %s= %s", - lval, xval, - (sn && SN_TYPE(sn) == SRC_ASSIGNOP) - ? (lastop == JSOP_GETTER) - ? js_getter_str - : (lastop == JSOP_SETTER) - ? js_setter_str - : js_CodeSpec[lastop].token - : "", - rval); - break; - - case JSOP_ARGSUB: - i = (jsint) GET_ATOM_INDEX(pc); - todo = Sprint(&ss->sprinter, "%s[%d]", - js_arguments_str, (int) i); - break; - - case JSOP_ARGCNT: - todo = Sprint(&ss->sprinter, dot_format, - js_arguments_str, js_length_str); - break; - - case JSOP_GETARG: - i = GET_ARGNO(pc); - atom = GetSlotAtom(jp, js_GetArgument, i); -#if JS_HAS_DESTRUCTURING - if (!atom) { - todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i); - break; - } -#else - LOCAL_ASSERT(atom); -#endif - goto do_name; - - case JSOP_GETVAR: - atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc)); - LOCAL_ASSERT(atom); - goto do_name; - - case JSOP_NAME: - case JSOP_GETGVAR: - atom = GET_ATOM(cx, jp->script, pc); - do_name: - lval = ""; - do_qname: - sn = js_GetSrcNote(jp->script, pc); - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - todo = Sprint(&ss->sprinter, "%s%s%s", - VarPrefix(sn), lval, rval); - break; - - case JSOP_UINT16: - i = (jsint) GET_ATOM_INDEX(pc); - goto do_sprint_int; - - case JSOP_UINT24: - i = (jsint) GET_UINT24(pc); - do_sprint_int: - todo = Sprint(&ss->sprinter, "%u", (unsigned) i); - break; - - case JSOP_LITERAL: - atomIndex = GET_LITERAL_INDEX(pc); - goto do_JSOP_STRING; - - case JSOP_FINDNAME: - atomIndex = GET_LITERAL_INDEX(pc); - todo = Sprint(&ss->sprinter, ""); - if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - goto do_name; - - case JSOP_LITOPX: - atomIndex = GET_LITERAL_INDEX(pc); - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = saveop = *pc2; - pc += len - (1 + ATOM_INDEX_LEN); - cs = &js_CodeSpec[op]; - len = cs->length; - switch (op) { - case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; - case JSOP_BINDNAME: goto do_JSOP_BINDNAME; - case JSOP_CLOSURE: goto do_JSOP_CLOSURE; -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; -#endif -#if JS_HAS_XML_SUPPORT - case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; - case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; -#endif - case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; - case JSOP_NUMBER: goto do_JSOP_NUMBER; - case JSOP_OBJECT: goto do_JSOP_OBJECT; -#if JS_HAS_XML_SUPPORT - case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; - case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; -#endif - case JSOP_REGEXP: goto do_JSOP_REGEXP; - case JSOP_SETCONST: goto do_JSOP_SETCONST; - case JSOP_STRING: goto do_JSOP_STRING; -#if JS_HAS_XML_SUPPORT - case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; - case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; - case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; - case JSOP_XMLPI: goto do_JSOP_XMLPI; -#endif - case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK; - default: LOCAL_ASSERT(0); - } - /* NOTREACHED */ - break; - - BEGIN_LITOPX_CASE(JSOP_NUMBER) - val = ATOM_KEY(atom); - if (JSVAL_IS_INT(val)) { - long ival = (long)JSVAL_TO_INT(val); - todo = Sprint(&ss->sprinter, "%ld", ival); - } else { - char buf[DTOSTR_STANDARD_BUFFER_SIZE]; - char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, - 0, *JSVAL_TO_DOUBLE(val)); - if (!numStr) { - JS_ReportOutOfMemory(cx); - return NULL; - } - todo = Sprint(&ss->sprinter, numStr); - } - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_STRING) - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - inXML ? DONT_ESCAPE : '"'); - if (!rval) - return NULL; - todo = STR2OFF(&ss->sprinter, rval); - END_LITOPX_CASE - - case JSOP_OBJECT: - case JSOP_REGEXP: - case JSOP_ANONFUNOBJ: - case JSOP_NAMEDFUNOBJ: - atomIndex = GET_ATOM_INDEX(pc); - - do_JSOP_OBJECT: - do_JSOP_REGEXP: - do_JSOP_ANONFUNOBJ: - do_JSOP_NAMEDFUNOBJ: - atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex); - if (op == JSOP_OBJECT || op == JSOP_REGEXP) { - if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL, - &val)) { - return NULL; - } - } else { - if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom), - JS_IN_GROUP_CONTEXT | - JS_DONT_PRETTY_PRINT, - 0, NULL, &val)) { - return NULL; - } - } - str = JSVAL_TO_STRING(val); - todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str), - JSSTRING_LENGTH(str)); - break; - - case JSOP_TABLESWITCH: - case JSOP_TABLESWITCHX: - { - ptrdiff_t jmplen, off, off2; - jsint j, n, low, high; - TableEntry *table, pivot; - - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); - len = js_GetSrcNoteOffset(sn, 0); - jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - - n = high - low + 1; - if (n == 0) { - table = NULL; - j = 0; - } else { - table = (TableEntry *) - JS_malloc(cx, (size_t)n * sizeof *table); - if (!table) - return NULL; - for (i = j = 0; i < n; i++) { - table[j].label = NULL; - off2 = GetJumpOffset(pc, pc2); - if (off2) { - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); - table[j].label = - js_GetAtom(cx, &jp->script->atomMap, - (jsatomid) - js_GetSrcNoteOffset(sn, 0)); - } - table[j].key = INT_TO_JSVAL(low + i); - table[j].offset = off2; - table[j].order = j; - j++; - } - pc2 += jmplen; - } - js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry), - CompareOffsets, NULL); - } - - ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, - JS_FALSE); - JS_free(cx, table); - if (!ok) - return NULL; - todo = -2; - break; - } - - case JSOP_LOOKUPSWITCH: - case JSOP_LOOKUPSWITCHX: - { - ptrdiff_t jmplen, off, off2; - jsatomid npairs, k; - TableEntry *table; - - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); - len = js_GetSrcNoteOffset(sn, 0); - jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - off = GetJumpOffset(pc, pc2); - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - - table = (TableEntry *) - JS_malloc(cx, (size_t)npairs * sizeof *table); - if (!table) - return NULL; - for (k = 0; k < npairs; k++) { - sn = js_GetSrcNote(jp->script, pc2); - if (sn) { - LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL); - table[k].label = - js_GetAtom(cx, &jp->script->atomMap, (jsatomid) - js_GetSrcNoteOffset(sn, 0)); - } else { - table[k].label = NULL; - } - atom = GET_ATOM(cx, jp->script, pc2); - pc2 += ATOM_INDEX_LEN; - off2 = GetJumpOffset(pc, pc2); - pc2 += jmplen; - table[k].key = ATOM_KEY(atom); - table[k].offset = off2; - } - - ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, - JS_FALSE); - JS_free(cx, table); - if (!ok) - return NULL; - todo = -2; - break; - } - - case JSOP_CONDSWITCH: - { - ptrdiff_t off, off2, caseOff; - jsint ncases; - TableEntry *table; - - sn = js_GetSrcNote(jp->script, pc); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH); - len = js_GetSrcNoteOffset(sn, 0); - off = js_GetSrcNoteOffset(sn, 1); - - /* - * Count the cases using offsets from switch to first case, - * and case to case, stored in srcnote immediates. - */ - pc2 = pc; - off2 = off; - for (ncases = 0; off2 != 0; ncases++) { - pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); - if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) { - /* End of cases, but count default as a case. */ - off2 = 0; - } else { - sn = js_GetSrcNote(jp->script, pc2); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); - off2 = js_GetSrcNoteOffset(sn, 0); - } - } - - /* - * Allocate table and rescan the cases using their srcnotes, - * stashing each case's delta from switch top in table[i].key, - * and the distance to its statements in table[i].offset. - */ - table = (TableEntry *) - JS_malloc(cx, (size_t)ncases * sizeof *table); - if (!table) - return NULL; - pc2 = pc; - off2 = off; - for (i = 0; i < ncases; i++) { - pc2 += off2; - LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT || - *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX); - caseOff = pc2 - pc; - table[i].key = INT_TO_JSVAL((jsint) caseOff); - table[i].offset = caseOff + GetJumpOffset(pc2, pc2); - if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) { - sn = js_GetSrcNote(jp->script, pc2); - LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA); - off2 = js_GetSrcNoteOffset(sn, 0); - } - } - - /* - * Find offset of default code by fetching the default offset - * from the end of table. JSOP_CONDSWITCH always has a default - * case at the end. - */ - off = JSVAL_TO_INT(table[ncases-1].key); - pc2 = pc + off; - off += GetJumpOffset(pc2, pc2); - - ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off, - JS_TRUE); - JS_free(cx, table); - if (!ok) - return NULL; - todo = -2; - break; - } - - case JSOP_CASE: - case JSOP_CASEX: - { - lval = POP_STR(); - if (!lval) - return NULL; - js_printf(jp, "\tcase %s:\n", lval); - todo = -2; - break; - } - - case JSOP_NEW_EQ: - case JSOP_NEW_NE: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %c== %s", - lval, (op == JSOP_NEW_EQ) ? '=' : '!', rval); - break; - - BEGIN_LITOPX_CASE(JSOP_CLOSURE) - LOCAL_ASSERT(ATOM_IS_OBJECT(atom)); - todo = -2; - goto do_function; - END_LITOPX_CASE - -#if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTALL: - js_printf(jp, "\texport *;\n"); - todo = -2; - break; - - BEGIN_LITOPX_CASE(JSOP_EXPORTNAME) - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - js_printf(jp, "\texport %s;\n", rval); - todo = -2; - END_LITOPX_CASE - - case JSOP_IMPORTALL: - lval = POP_STR(); - js_printf(jp, "\timport %s.*;\n", lval); - todo = -2; - break; - - case JSOP_IMPORTPROP: - do_importprop: - GET_ATOM_QUOTE_AND_FMT("\timport %s[%s];\n", - "\timport %s.%s;\n", - rval); - lval = POP_STR(); - js_printf(jp, fmt, lval, rval); - todo = -2; - break; - - case JSOP_IMPORTELEM: - xval = POP_STR(); - op = JSOP_GETELEM; - if (js_CodeSpec[lastop].format & JOF_XMLNAME) - goto do_importprop; - lval = POP_STR(); - js_printf(jp, "\timport %s[%s];\n", lval, xval); - todo = -2; - break; -#endif /* JS_HAS_EXPORT_IMPORT */ - - case JSOP_TRAP: - op = JS_GetTrapOpcode(cx, jp->script, pc); - if (op == JSOP_LIMIT) - return NULL; - saveop = op; - *pc = op; - cs = &js_CodeSpec[op]; - len = cs->length; - DECOMPILE_CODE(pc, len); - *pc = JSOP_TRAP; - todo = -2; - break; - - case JSOP_NEWINIT: - { - JSBool isArray; - - LOCAL_ASSERT(ss->top >= 2); - (void) PopOff(ss, op); - lval = POP_STR(); - isArray = (*lval == 'A'); - todo = ss->sprinter.offset; -#if JS_HAS_SHARP_VARS - op = (JSOp)pc[len]; - if (op == JSOP_DEFSHARP) { - pc += len; - cs = &js_CodeSpec[op]; - len = cs->length; - i = (jsint) GET_ATOM_INDEX(pc); - if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0) - return NULL; - } -#endif /* JS_HAS_SHARP_VARS */ - if (isArray) { - ++ss->inArrayInit; - if (SprintCString(&ss->sprinter, "[") < 0) - return NULL; - } else { - if (SprintCString(&ss->sprinter, "{") < 0) - return NULL; - } - break; - } - - case JSOP_ENDINIT: - op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); - sn = js_GetSrcNote(jp->script, pc); - - /* Skip any #n= prefix to find the opening bracket. */ - for (xval = rval; *xval != '[' && *xval != '{'; xval++) - continue; - if (*xval == '[') - --ss->inArrayInit; - todo = Sprint(&ss->sprinter, "%s%s%c", - rval, - (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", - (*xval == '[') ? ']' : '}'); - break; - - case JSOP_INITPROP: - atom = GET_ATOM(cx, jp->script, pc); - xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), - (jschar) - (ATOM_IS_IDENTIFIER(atom) ? 0 : '\'')); - if (!xval) - return NULL; - rval = POP_STR(); - lval = POP_STR(); - do_initprop: -#ifdef OLD_GETTER_SETTER - todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", - lval, - (lval[1] != '\0') ? ", " : "", - xval, - (lastop == JSOP_GETTER || lastop == JSOP_SETTER) - ? " " : "", - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval); -#else - if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { - if (!atom || !ATOM_IS_STRING(atom) || - !ATOM_IS_IDENTIFIER(atom) || - ATOM_IS_KEYWORD(atom) || - ((ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ || - strncmp(rval, js_function_str, 8) != 0) && - ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) { - todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval, - (lval[1] != '\0') ? ", " : "", xval, - (lastop == JSOP_GETTER || - lastop == JSOP_SETTER) - ? " " : "", - (lastop == JSOP_GETTER) ? js_getter_str : - (lastop == JSOP_SETTER) ? js_setter_str : - "", - rval); - } else { - rval += 8 + 1; - LOCAL_ASSERT(rval[strlen(rval)-1] == '}'); - todo = Sprint(&ss->sprinter, "%s%s%s %s%s", - lval, - (lval[1] != '\0') ? ", " : "", - (lastop == JSOP_GETTER) - ? js_get_str : js_set_str, - xval, - rval); - } - } else { - todo = Sprint(&ss->sprinter, "%s%s%s:%s", - lval, - (lval[1] != '\0') ? ", " : "", - xval, - rval); - } -#endif - break; - - case JSOP_INITELEM: - rval = POP_STR(); - xval = POP_STR(); - lval = POP_STR(); - sn = js_GetSrcNote(jp->script, pc); - if (sn && SN_TYPE(sn) == SRC_INITPROP) { - atom = NULL; - goto do_initprop; - } - todo = Sprint(&ss->sprinter, "%s%s%s", - lval, - (lval[1] != '\0' || *xval != '0') ? ", " : "", - rval); - break; - -#if JS_HAS_SHARP_VARS - case JSOP_DEFSHARP: - i = (jsint) GET_ATOM_INDEX(pc); - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval); - break; - - case JSOP_USESHARP: - i = (jsint) GET_ATOM_INDEX(pc); - todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i); - break; -#endif /* JS_HAS_SHARP_VARS */ - -#if JS_HAS_DEBUGGER_KEYWORD - case JSOP_DEBUGGER: - js_printf(jp, "\tdebugger;\n"); - todo = -2; - break; -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - case JSOP_STARTXML: - case JSOP_STARTXMLEXPR: - inXML = op == JSOP_STARTXML; - todo = -2; - break; - - case JSOP_DEFXMLNS: - rval = POP_STR(); - js_printf(jp, "\t%s %s %s = %s;\n", - js_default_str, js_xml_str, js_namespace_str, rval); - todo = -2; - break; - - case JSOP_ANYNAME: - if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) { - len += JSOP_TOATTRNAME_LENGTH; - todo = SprintPut(&ss->sprinter, "@*", 2); - } else { - todo = SprintPut(&ss->sprinter, "*", 1); - } - break; - - BEGIN_LITOPX_CASE(JSOP_QNAMEPART) - if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) { - saveop = JSOP_TOATTRNAME; - len += JSOP_TOATTRNAME_LENGTH; - lval = "@"; - goto do_qname; - } - goto do_name; - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_QNAMECONST) - rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0); - if (!rval) - return NULL; - RETRACT(&ss->sprinter, rval); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); - END_LITOPX_CASE - - case JSOP_QNAME: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval); - break; - - case JSOP_TOATTRNAME: - op = JSOP_NOP; /* turn off parens */ - rval = POP_STR(); - todo = Sprint(&ss->sprinter, "@[%s]", rval); - break; - - case JSOP_TOATTRVAL: - todo = -2; - break; - - case JSOP_ADDATTRNAME: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s %s", lval, rval); - /* This gets reset by all XML tag expressions. */ - quoteAttr = JS_TRUE; - break; - - case JSOP_ADDATTRVAL: - rval = POP_STR(); - lval = POP_STR(); - if (quoteAttr) - todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval); - else - todo = Sprint(&ss->sprinter, "%s=%s", lval, rval); - break; - - case JSOP_BINDXMLNAME: - /* Leave the name stacked and push a dummy string. */ - todo = Sprint(&ss->sprinter, ""); - break; - - case JSOP_SETXMLNAME: - /* Pop the r.h.s., the dummy string, and the name. */ - rval = POP_STR(); - (void) PopOff(ss, op); - lval = POP_STR(); - goto do_setlval; - - case JSOP_XMLELTEXPR: - case JSOP_XMLTAGEXPR: - todo = Sprint(&ss->sprinter, "{%s}", POP_STR()); - inXML = JS_TRUE; - /* If we're an attribute value, we shouldn't quote this. */ - quoteAttr = JS_FALSE; - break; - - case JSOP_TOXMLLIST: - op = JSOP_NOP; /* turn off parens */ - todo = Sprint(&ss->sprinter, "<>%s", POP_STR()); - inXML = JS_FALSE; - break; - - case JSOP_FOREACH: - foreach = JS_TRUE; - todo = -2; - break; - - case JSOP_TOXML: - inXML = JS_FALSE; - /* FALL THROUGH */ - - case JSOP_XMLNAME: - case JSOP_FILTER: - /* Conversion and prefix ops do nothing in the decompiler. */ - todo = -2; - break; - - case JSOP_ENDFILTER: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval); - break; - - case JSOP_DESCENDANTS: - rval = POP_STR(); - lval = POP_STR(); - todo = Sprint(&ss->sprinter, "%s..%s", lval, rval); - break; - - BEGIN_LITOPX_CASE(JSOP_XMLOBJECT) - todo = Sprint(&ss->sprinter, "", - ATOM_TO_OBJECT(atom)); - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_XMLCDATA) - todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0)) - return NULL; - SprintPut(&ss->sprinter, "]]>", 3); - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT) - todo = SprintPut(&ss->sprinter, "", 3); - END_LITOPX_CASE - - BEGIN_LITOPX_CASE(JSOP_XMLPI) - rval = JS_strdup(cx, POP_STR()); - if (!rval) - return NULL; - todo = SprintPut(&ss->sprinter, "sprinter, ATOM_TO_STRING(atom), 0) && - (*rval == '\0' || - (SprintPut(&ss->sprinter, " ", 1) >= 0 && - SprintCString(&ss->sprinter, rval))); - JS_free(cx, (char *)rval); - if (!ok) - return NULL; - SprintPut(&ss->sprinter, "?>", 2); - END_LITOPX_CASE - - case JSOP_GETFUNNS: - todo = SprintPut(&ss->sprinter, js_function_str, 8); - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default: - todo = -2; - break; - -#undef BEGIN_LITOPX_CASE -#undef END_LITOPX_CASE - } - } - - if (todo < 0) { - /* -2 means "don't push", -1 means reported error. */ - if (todo == -1) - return NULL; - } else { - if (!PushOff(ss, todo, saveop)) - return NULL; - } - pc += len; - } - -/* - * Undefine local macros. - */ -#undef inXML -#undef DECOMPILE_CODE -#undef POP_STR -#undef LOCAL_ASSERT -#undef ATOM_IS_IDENTIFIER -#undef GET_QUOTE_AND_FMT -#undef GET_ATOM_QUOTE_AND_FMT - - return pc; -} - -static JSBool -InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) -{ - size_t offsetsz, opcodesz; - void *space; - - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP); - - /* Allocate the parallel (to avoid padding) offset and opcode stacks. */ - offsetsz = depth * sizeof(ptrdiff_t); - opcodesz = depth * sizeof(jsbytecode); - JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz); - if (!space) - return JS_FALSE; - ss->offsets = (ptrdiff_t *) space; - ss->opcodes = (jsbytecode *) ((char *)space + offsetsz); - - ss->top = ss->inArrayInit = 0; - ss->printer = jp; - return JS_TRUE; -} - -JSBool -js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, - uintN pcdepth) -{ - uintN depth, i; - SprintStack ss; - JSContext *cx; - void *mark; - JSBool ok; - JSScript *oldscript; - char *last; - - depth = script->depth; - JS_ASSERT(pcdepth <= depth); - - /* Initialize a sprinter for use with the offset stack. */ - cx = jp->sprinter.context; - mark = JS_ARENA_MARK(&cx->tempPool); - ok = InitSprintStack(cx, &ss, jp, depth); - if (!ok) - goto out; - - /* - * If we are called from js_DecompileValueGenerator with a portion of - * script's bytecode that starts with a non-zero model stack depth given - * by pcdepth, attempt to initialize the missing string offsets in ss to - * |spindex| negative indexes from fp->sp for the activation fp in which - * the error arose. - * - * See js_DecompileValueGenerator for how its |spindex| parameter is used, - * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are - * potentially stored below. - */ - ss.top = pcdepth; - if (pcdepth != 0) { - JSStackFrame *fp; - ptrdiff_t top; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - top = fp ? fp->sp - fp->spbase : 0; - for (i = 0; i < pcdepth; i++) { - ss.offsets[i] = -1; - ss.opcodes[i] = JSOP_NOP; - } - if (fp && fp->pc == pc && (uintN)top == pcdepth) { - for (i = 0; i < pcdepth; i++) { - ptrdiff_t off; - jsbytecode *genpc; - - off = (intN)i - (intN)depth; - genpc = (jsbytecode *) fp->spbase[off]; - if (JS_UPTRDIFF(genpc, script->code) < script->length) { - ss.offsets[i] += (ptrdiff_t)i - top; - ss.opcodes[i] = *genpc; - } - } - } - } - - /* Call recursive subroutine to do the hard work. */ - oldscript = jp->script; - jp->script = script; - ok = Decompile(&ss, pc, len) != NULL; - jp->script = oldscript; - - /* If the given code didn't empty the stack, do it now. */ - if (ss.top) { - do { - last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); - } while (ss.top > pcdepth); - js_printf(jp, "%s", last); - } - -out: - /* Free all temporary stuff allocated under this call. */ - JS_ARENA_RELEASE(&cx->tempPool, mark); - return ok; -} - -JSBool -js_DecompileScript(JSPrinter *jp, JSScript *script) -{ - return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); -} - -static const char native_code_str[] = "\t[native code]\n"; - -JSBool -js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun) -{ - JSScript *script; - JSScope *scope, *save; - JSBool ok; - - if (!FUN_INTERPRETED(fun)) { - js_printf(jp, native_code_str); - return JS_TRUE; - } - script = fun->u.i.script; - scope = fun->object ? OBJ_SCOPE(fun->object) : NULL; - save = jp->scope; - jp->scope = scope; - ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0); - jp->scope = save; - return ok; -} - -JSBool -js_DecompileFunction(JSPrinter *jp, JSFunction *fun) -{ - JSContext *cx; - uintN i, nargs, indent; - void *mark; - JSAtom **params; - JSScope *scope, *oldscope; - JSScopeProperty *sprop; - jsbytecode *pc, *endpc; - ptrdiff_t len; - JSBool ok; - - /* - * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a - * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force - * an expression by parenthesizing. - */ - if (jp->pretty) { - js_printf(jp, "\t"); - } else { - if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) - js_puts(jp, "("); - } - if (JSFUN_GETTER_TEST(fun->flags)) - js_printf(jp, "%s ", js_getter_str); - else if (JSFUN_SETTER_TEST(fun->flags)) - js_printf(jp, "%s ", js_setter_str); - - js_printf(jp, "%s ", js_function_str); - if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0)) - return JS_FALSE; - js_puts(jp, "("); - - if (FUN_INTERPRETED(fun) && fun->object) { - size_t paramsize; -#ifdef JS_HAS_DESTRUCTURING - SprintStack ss; - JSScript *oldscript; -#endif - - /* - * Print the parameters. - * - * This code is complicated by the need to handle duplicate parameter - * names, as required by ECMA (bah!). A duplicate parameter is stored - * as another node with the same id (the parameter name) but different - * shortid (the argument index) along the property tree ancestor line - * starting at SCOPE_LAST_PROP(scope). Only the last duplicate param - * is mapped by the scope's hash table. - */ - cx = jp->sprinter.context; - nargs = fun->nargs; - mark = JS_ARENA_MARK(&cx->tempPool); - paramsize = nargs * sizeof(JSAtom *); - JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize); - if (!params) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - memset(params, 0, paramsize); - scope = OBJ_SCOPE(fun->object); - for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { - if (sprop->getter != js_GetArgument) - continue; - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT((uint16) sprop->shortid < nargs); - JS_ASSERT(JSID_IS_ATOM(sprop->id)); - params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id); - } - - pc = fun->u.i.script->main; - endpc = pc + fun->u.i.script->length; - ok = JS_TRUE; - -#ifdef JS_HAS_DESTRUCTURING - /* Skip JSOP_GENERATOR in case of destructuring parameters. */ - if (*pc == JSOP_GENERATOR) - pc += JSOP_GENERATOR_LENGTH; - - ss.printer = NULL; - oldscript = jp->script; - jp->script = fun->u.i.script; - oldscope = jp->scope; - jp->scope = scope; -#endif - - for (i = 0; i < nargs; i++) { - if (i > 0) - js_puts(jp, ", "); - -#if JS_HAS_DESTRUCTURING -#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE) - - if (!params[i]) { - ptrdiff_t todo; - const char *lval; - - LOCAL_ASSERT(*pc == JSOP_GETARG); - pc += JSOP_GETARG_LENGTH; - LOCAL_ASSERT(*pc == JSOP_DUP); - if (!ss.printer) { - ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth); - if (!ok) - break; - } - pc = DecompileDestructuring(&ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - break; - } - LOCAL_ASSERT(*pc == JSOP_POP); - pc += JSOP_POP_LENGTH; - lval = PopStr(&ss, JSOP_NOP); - todo = SprintCString(&jp->sprinter, lval); - if (todo < 0) { - ok = JS_FALSE; - break; - } - continue; - } - -#undef LOCAL_ASSERT -#endif - - if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) { - ok = JS_FALSE; - break; - } - } - -#ifdef JS_HAS_DESTRUCTURING - jp->script = oldscript; - jp->scope = oldscope; -#endif - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!ok) - return JS_FALSE; -#ifdef __GNUC__ - } else { - scope = NULL; - pc = NULL; -#endif - } - - js_printf(jp, ") {\n"); - indent = jp->indent; - jp->indent += 4; - if (FUN_INTERPRETED(fun) && fun->object) { - oldscope = jp->scope; - jp->scope = scope; - len = fun->u.i.script->code + fun->u.i.script->length - pc; - ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0); - jp->scope = oldscope; - if (!ok) { - jp->indent = indent; - return JS_FALSE; - } - } else { - js_printf(jp, native_code_str); - } - jp->indent -= 4; - js_printf(jp, "\t}"); - - if (!jp->pretty) { - if (!jp->grouped && (fun->flags & JSFUN_LAMBDA)) - js_puts(jp, ")"); - } - return JS_TRUE; -} - -#undef LOCAL_ASSERT_RV - -JSString * -js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, - JSString *fallback) -{ - JSStackFrame *fp, *down; - jsbytecode *pc, *begin, *end; - jsval *sp, *spbase, *base, *limit; - intN depth, pcdepth; - JSScript *script; - JSOp op; - const JSCodeSpec *cs; - jssrcnote *sn; - ptrdiff_t len, oplen; - JSPrinter *jp; - JSString *name; - - for (fp = cx->fp; fp && !fp->script; fp = fp->down) - continue; - if (!fp) - goto do_fallback; - - /* Try to find sp's generating pc depth slots under it on the stack. */ - pc = fp->pc; - sp = fp->sp; - spbase = fp->spbase; - if ((uintN)(sp - spbase) > fp->script->depth) { - /* - * Preparing to make an internal invocation, using an argv stack - * segment pushed just above fp's operand stack space. Such an argv - * stack has no generating pc "basement", so we must fall back. - */ - goto do_fallback; - } - - if (spindex == JSDVG_SEARCH_STACK) { - if (!pc) { - /* - * Current frame is native: look under it for a scripted call - * in which a decompilable bytecode string that generated the - * value as an actual argument might exist. - */ - JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun))); - down = fp->down; - if (!down) - goto do_fallback; - script = down->script; - spbase = down->spbase; - base = fp->argv; - limit = base + fp->argc; - } else { - /* - * This should be a script activation, either a top-level - * script or a scripted function. But be paranoid about calls - * to js_DecompileValueGenerator from code that hasn't fully - * initialized a (default-all-zeroes) frame. - */ - script = fp->script; - spbase = base = fp->spbase; - limit = fp->sp; - } - - /* - * Pure paranoia about default-zeroed frames being active while - * js_DecompileValueGenerator is called. It can't hurt much now; - * error reporting performance is not an issue. - */ - if (!script || !base || !limit) - goto do_fallback; - - /* - * Try to find operand-generating pc depth slots below sp. - * - * In the native case, we know the arguments have generating pc's - * under them, on account of fp->down->script being non-null: all - * compiled scripts get depth slots for generating pc's allocated - * upon activation, at the top of js_Interpret. - * - * In the script or scripted function case, the same reasoning - * applies to fp rather than to fp->down. - * - * We search from limit to base to find the most recently calculated - * value matching v under assumption that it is it that caused - * exception, see bug 328664. - */ - for (sp = limit;;) { - if (sp <= base) - goto do_fallback; - --sp; - if (*sp == v) { - depth = (intN)script->depth; - sp -= depth; - pc = (jsbytecode *) *sp; - break; - } - } - } else { - /* - * At this point, pc may or may not be null, i.e., we could be in - * a script activation, or we could be in a native frame that was - * called by another native function. Check pc and script. - */ - if (!pc) - goto do_fallback; - script = fp->script; - if (!script) - goto do_fallback; - - if (spindex != JSDVG_IGNORE_STACK) { - JS_ASSERT(spindex < 0); - depth = (intN)script->depth; -#if !JS_HAS_NO_SUCH_METHOD - JS_ASSERT(-depth <= spindex); -#endif - spindex -= depth; - - base = (jsval *) cx->stackPool.current->base; - limit = (jsval *) cx->stackPool.current->avail; - sp = fp->sp + spindex; - if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base)) - pc = (jsbytecode *) *sp; - } - } - - /* - * Again, be paranoid, this time about possibly loading an invalid pc - * from fp->sp[-(1+depth)]. - */ - if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) { - pc = fp->pc; - if (!pc) - goto do_fallback; - } - op = (JSOp) *pc; - if (op == JSOP_TRAP) - op = JS_GetTrapOpcode(cx, script, pc); - - /* None of these stack-writing ops generates novel values. */ - JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX && - op != JSOP_DUP && op != JSOP_DUP2 && - op != JSOP_SWAP); - - /* - * |this| could convert to a very long object initialiser, so cite it by - * its keyword name instead. - */ - if (op == JSOP_THIS) - return JS_NewStringCopyZ(cx, js_this_str); - - /* - * JSOP_BINDNAME is special: it generates a value, the base object of a - * reference. But if it is the generating op for a diagnostic produced by - * js_DecompileValueGenerator, the name being bound is irrelevant. Just - * fall back to the base object. - */ - if (op == JSOP_BINDNAME) - goto do_fallback; - - /* NAME ops are self-contained, others require left or right context. */ - cs = &js_CodeSpec[op]; - begin = pc; - end = pc + cs->length; - if ((cs->format & JOF_MODEMASK) != JOF_NAME) { - JSSrcNoteType noteType; - - sn = js_GetSrcNote(script, pc); - if (!sn) - goto do_fallback; - noteType = SN_TYPE(sn); - if (noteType == SRC_PCBASE) { - begin -= js_GetSrcNoteOffset(sn, 0); - } else if (noteType == SRC_PCDELTA) { - end = begin + js_GetSrcNoteOffset(sn, 0); - begin += cs->length; - } else { - goto do_fallback; - } - } - len = PTRDIFF(end, begin, jsbytecode); - if (len <= 0) - goto do_fallback; - - /* - * Walk forward from script->main and compute starting stack depth. - * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced. - * FIXME: Optimize to use last empty-stack sequence point. - */ - pcdepth = 0; - for (pc = script->main; pc < begin; pc += oplen) { - jsbytecode *pc2; - uint32 type; - intN nuses, ndefs; - - /* Let pc2 be non-null only for JSOP_LITOPX. */ - pc2 = NULL; - op = (JSOp) *pc; - if (op == JSOP_TRAP) - op = JS_GetTrapOpcode(cx, script, pc); - cs = &js_CodeSpec[op]; - oplen = cs->length; - - if (op == JSOP_SETSP) { - pcdepth = GET_UINT16(pc); - continue; - } - - /* - * A (C ? T : E) expression requires skipping either T (if begin is in - * E) or both T and E (if begin is after the whole expression) before - * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that - * tests condition C. We know that the stack depth can't change from - * what it was with C on top of stack. - */ - sn = js_GetSrcNote(script, pc); - if (sn && SN_TYPE(sn) == SRC_COND) { - ptrdiff_t jmpoff, jmplen; - - jmpoff = js_GetSrcNoteOffset(sn, 0); - if (pc + jmpoff < begin) { - pc += jmpoff; - op = *pc; - JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX); - cs = &js_CodeSpec[op]; - oplen = cs->length; - jmplen = GetJumpOffset(pc, pc); - if (pc + jmplen < begin) { - oplen = (uintN) jmplen; - continue; - } - - /* - * Ok, begin lies in E. Manually pop C off the model stack, - * since we have moved beyond the IFEQ now. - */ - --pcdepth; - } - } - - type = cs->format & JOF_TYPEMASK; - switch (type) { - case JOF_TABLESWITCH: - case JOF_TABLESWITCHX: - { - jsint jmplen, i, low, high; - - jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - pc2 += jmplen; - low = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - high = GET_JUMP_OFFSET(pc2); - pc2 += JUMP_OFFSET_LEN; - for (i = low; i <= high; i++) - pc2 += jmplen; - oplen = 1 + pc2 - pc; - break; - } - - case JOF_LOOKUPSWITCH: - case JOF_LOOKUPSWITCHX: - { - jsint jmplen; - jsbytecode *pc2; - jsatomid npairs; - - jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN - : JUMPX_OFFSET_LEN; - pc2 = pc; - pc2 += jmplen; - npairs = GET_ATOM_INDEX(pc2); - pc2 += ATOM_INDEX_LEN; - while (npairs) { - pc2 += ATOM_INDEX_LEN; - pc2 += jmplen; - npairs--; - } - oplen = 1 + pc2 - pc; - break; - } - - case JOF_LITOPX: - pc2 = pc + 1 + LITERAL_INDEX_LEN; - op = *pc2; - cs = &js_CodeSpec[op]; - JS_ASSERT(cs->length > ATOM_INDEX_LEN); - oplen += cs->length - (1 + ATOM_INDEX_LEN); - break; - - default:; - } - - if (sn && SN_TYPE(sn) == SRC_HIDDEN) - continue; - - nuses = cs->nuses; - if (nuses < 0) { - /* Call opcode pushes [callee, this, argv...]. */ - nuses = 2 + GET_ARGC(pc); - } else if (op == JSOP_RETSUB) { - /* Pop [exception or hole, retsub pc-index]. */ - JS_ASSERT(nuses == 0); - nuses = 2; - } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) { - JS_ASSERT(nuses == 0); - nuses = GET_UINT16(pc); - } - pcdepth -= nuses; - JS_ASSERT(pcdepth >= 0); - - ndefs = cs->ndefs; - if (op == JSOP_FINALLY) { - /* Push [exception or hole, retsub pc-index]. */ - JS_ASSERT(ndefs == 0); - ndefs = 2; - } else if (op == JSOP_ENTERBLOCK) { - jsatomid atomIndex; - JSAtom *atom; - JSObject *obj; - - JS_ASSERT(ndefs == 0); - atomIndex = pc2 ? GET_LITERAL_INDEX(pc) : GET_ATOM_INDEX(pc); - atom = js_GetAtom(cx, &script->atomMap, atomIndex); - obj = ATOM_TO_OBJECT(atom); - JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth); - ndefs = OBJ_BLOCK_COUNT(cx, obj); - } - pcdepth += ndefs; - } - - name = NULL; - jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE); - if (jp) { - if (fp->fun && fp->fun->object) { - JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object)); - jp->scope = OBJ_SCOPE(fp->fun->object); - } - jp->dvgfence = end; - if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) - name = js_GetPrinterOutput(jp); - js_DestroyPrinter(jp); - } - return name; - - do_fallback: - return fallback ? fallback : js_ValueToSource(cx, v); -} diff --git a/spidermonkey/libjs/jsopcode.h b/spidermonkey/libjs/jsopcode.h deleted file mode 100644 index 3f7e1de..0000000 --- a/spidermonkey/libjs/jsopcode.h +++ /dev/null @@ -1,318 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsopcode_h___ -#define jsopcode_h___ -/* - * JS bytecode definitions. - */ -#include -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsutil.h" - -JS_BEGIN_EXTERN_C - -/* - * JS operation bytecodes. - */ -typedef enum JSOp { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - op = val, -#include "jsopcode.tbl" -#undef OPDEF - JSOP_LIMIT -} JSOp; - -typedef enum JSOpLength { -#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ - op##_LENGTH = length, -#include "jsopcode.tbl" -#undef OPDEF - JSOP_LIMIT_LENGTH -} JSOpLength; - -/* - * JS bytecode formats. - */ -#define JOF_BYTE 0 /* single bytecode, no immediates */ -#define JOF_JUMP 1 /* signed 16-bit jump offset immediate */ -#define JOF_CONST 2 /* unsigned 16-bit constant pool index */ -#define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ -#define JOF_TABLESWITCH 4 /* table switch */ -#define JOF_LOOKUPSWITCH 5 /* lookup switch */ -#define JOF_QARG 6 /* quickened get/set function argument ops */ -#define JOF_QVAR 7 /* quickened get/set local variable ops */ -#define JOF_INDEXCONST 8 /* uint16 slot index + constant pool index */ -#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ -#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ -#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ -#define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ -#define JOF_LITOPX 13 /* JOF_UINT24 followed by op being extended, - where op if JOF_CONST has no unsigned 16- - bit immediate operand */ -#define JOF_LOCAL 14 /* block-local operand stack variable */ -#define JOF_TYPEMASK 0x000f /* mask for above immediate types */ -#define JOF_NAME 0x0010 /* name operation */ -#define JOF_PROP 0x0020 /* obj.prop operation */ -#define JOF_ELEM 0x0030 /* obj[index] operation */ -#define JOF_MODEMASK 0x0030 /* mask for above addressing modes */ -#define JOF_SET 0x0040 /* set (i.e., assignment) operation */ -#define JOF_DEL 0x0080 /* delete operation */ -#define JOF_DEC 0x0100 /* decrement (--, not ++) opcode */ -#define JOF_INC 0x0200 /* increment (++, not --) opcode */ -#define JOF_INCDEC 0x0300 /* increment or decrement opcode */ -#define JOF_POST 0x0400 /* postorder increment or decrement */ -#define JOF_IMPORT 0x0800 /* import property op */ -#define JOF_FOR 0x1000 /* for-in property op */ -#define JOF_ASSIGNING JOF_SET /* hint for JSClass.resolve, used for ops - that do simplex assignment */ -#define JOF_DETECTING 0x2000 /* object detection flag for JSNewResolveOp */ -#define JOF_BACKPATCH 0x4000 /* backpatch placeholder during codegen */ -#define JOF_LEFTASSOC 0x8000 /* left-associative operator */ -#define JOF_DECLARING 0x10000 /* var, const, or function declaration op */ -#define JOF_XMLNAME 0x20000 /* XML name: *, a::b, @a, @a::b, etc. */ - -#define JOF_TYPE_IS_EXTENDED_JUMP(t) \ - ((unsigned)((t) - JOF_JUMPX) <= (unsigned)(JOF_LOOKUPSWITCHX - JOF_JUMPX)) - -/* - * Immediate operand getters, setters, and bounds. - */ - -/* Short (2-byte signed offset) relative jump macros. */ -#define JUMP_OFFSET_LEN 2 -#define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) -#define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) -#define GET_JUMP_OFFSET(pc) ((int16)(((pc)[1] << 8) | (pc)[2])) -#define SET_JUMP_OFFSET(pc,off) ((pc)[1] = JUMP_OFFSET_HI(off), \ - (pc)[2] = JUMP_OFFSET_LO(off)) -#define JUMP_OFFSET_MIN ((int16)0x8000) -#define JUMP_OFFSET_MAX ((int16)0x7fff) - -/* - * When a short jump won't hold a relative offset, its 2-byte immediate offset - * operand is an unsigned index of a span-dependency record, maintained until - * code generation finishes -- after which some (but we hope not nearly all) - * span-dependent jumps must be extended (see OptimizeSpanDeps in jsemit.c). - * - * If the span-dependency record index overflows SPANDEP_INDEX_MAX, the jump - * offset will contain SPANDEP_INDEX_HUGE, indicating that the record must be - * found (via binary search) by its "before span-dependency optimization" pc - * offset (from script main entry point). - */ -#define GET_SPANDEP_INDEX(pc) ((uint16)(((pc)[1] << 8) | (pc)[2])) -#define SET_SPANDEP_INDEX(pc,i) ((pc)[1] = JUMP_OFFSET_HI(i), \ - (pc)[2] = JUMP_OFFSET_LO(i)) -#define SPANDEP_INDEX_MAX ((uint16)0xfffe) -#define SPANDEP_INDEX_HUGE ((uint16)0xffff) - -/* Ultimately, if short jumps won't do, emit long (4-byte signed) offsets. */ -#define JUMPX_OFFSET_LEN 4 -#define JUMPX_OFFSET_B3(off) ((jsbytecode)((off) >> 24)) -#define JUMPX_OFFSET_B2(off) ((jsbytecode)((off) >> 16)) -#define JUMPX_OFFSET_B1(off) ((jsbytecode)((off) >> 8)) -#define JUMPX_OFFSET_B0(off) ((jsbytecode)(off)) -#define GET_JUMPX_OFFSET(pc) ((int32)(((pc)[1] << 24) | ((pc)[2] << 16) \ - | ((pc)[3] << 8) | (pc)[4])) -#define SET_JUMPX_OFFSET(pc,off)((pc)[1] = JUMPX_OFFSET_B3(off), \ - (pc)[2] = JUMPX_OFFSET_B2(off), \ - (pc)[3] = JUMPX_OFFSET_B1(off), \ - (pc)[4] = JUMPX_OFFSET_B0(off)) -#define JUMPX_OFFSET_MIN ((int32)0x80000000) -#define JUMPX_OFFSET_MAX ((int32)0x7fffffff) - -/* - * A literal is indexed by a per-script atom map. Most scripts have relatively - * few literals, so the standard JOF_CONST format specifies a fixed 16 bits of - * immediate operand index. A script with more than 64K literals must push all - * high-indexed literals on the stack using JSOP_LITERAL, then use JOF_ELEM ops - * instead of JOF_PROP, etc. - */ -#define ATOM_INDEX_LEN 2 -#define ATOM_INDEX_HI(i) ((jsbytecode)((i) >> 8)) -#define ATOM_INDEX_LO(i) ((jsbytecode)(i)) -#define GET_ATOM_INDEX(pc) ((jsatomid)(((pc)[1] << 8) | (pc)[2])) -#define SET_ATOM_INDEX(pc,i) ((pc)[1] = ATOM_INDEX_HI(i), \ - (pc)[2] = ATOM_INDEX_LO(i)) -#define GET_ATOM(cx,script,pc) js_GetAtom((cx), &(script)->atomMap, \ - GET_ATOM_INDEX(pc)) - -/* A full atom index for JSOP_UINT24 uses 24 bits of immediate operand. */ -#define UINT24_HI(i) ((jsbytecode)((i) >> 16)) -#define UINT24_MID(i) ((jsbytecode)((i) >> 8)) -#define UINT24_LO(i) ((jsbytecode)(i)) -#define GET_UINT24(pc) ((jsatomid)(((pc)[1] << 16) | \ - ((pc)[2] << 8) | \ - (pc)[3])) -#define SET_UINT24(pc,i) ((pc)[1] = UINT24_HI(i), \ - (pc)[2] = UINT24_MID(i), \ - (pc)[3] = UINT24_LO(i)) - -/* Same format for JSOP_LITERAL, etc., but future-proof with different names. */ -#define LITERAL_INDEX_LEN 3 -#define LITERAL_INDEX_HI(i) UINT24_HI(i) -#define LITERAL_INDEX_MID(i) UINT24_MID(i) -#define LITERAL_INDEX_LO(i) UINT24_LO(i) -#define GET_LITERAL_INDEX(pc) GET_UINT24(pc) -#define SET_LITERAL_INDEX(pc,i) SET_UINT24(pc,i) - -/* Atom index limit is determined by SN_3BYTE_OFFSET_FLAG, see jsemit.h. */ -#define ATOM_INDEX_LIMIT_LOG2 23 -#define ATOM_INDEX_LIMIT ((uint32)1 << ATOM_INDEX_LIMIT_LOG2) - -JS_STATIC_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= - ATOM_INDEX_LIMIT_LOG2 + 1); - -/* Common uint16 immediate format helpers. */ -#define UINT16_HI(i) ((jsbytecode)((i) >> 8)) -#define UINT16_LO(i) ((jsbytecode)(i)) -#define GET_UINT16(pc) ((uintN)(((pc)[1] << 8) | (pc)[2])) -#define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) -#define UINT16_LIMIT ((uintN)1 << 16) - -/* Actual argument count operand format helpers. */ -#define ARGC_HI(argc) UINT16_HI(argc) -#define ARGC_LO(argc) UINT16_LO(argc) -#define GET_ARGC(pc) GET_UINT16(pc) -#define ARGC_LIMIT UINT16_LIMIT - -/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */ -#define GET_ARGNO(pc) GET_UINT16(pc) -#define SET_ARGNO(pc,argno) SET_UINT16(pc,argno) -#define ARGNO_LEN 2 -#define ARGNO_LIMIT UINT16_LIMIT - -#define GET_VARNO(pc) GET_UINT16(pc) -#define SET_VARNO(pc,varno) SET_UINT16(pc,varno) -#define VARNO_LEN 2 -#define VARNO_LIMIT UINT16_LIMIT - -struct JSCodeSpec { - const char *name; /* JS bytecode name */ - const char *token; /* JS source literal or null */ - int8 length; /* length including opcode byte */ - int8 nuses; /* arity, -1 if variadic */ - int8 ndefs; /* number of stack results */ - uint8 prec; /* operator precedence */ - uint32 format; /* immediate operand format */ -}; - -extern const JSCodeSpec js_CodeSpec[]; -extern uintN js_NumCodeSpecs; -extern const jschar js_EscapeMap[]; - -/* - * Return a GC'ed string containing the chars in str, with any non-printing - * chars or quotes (' or " as specified by the quote argument) escaped, and - * with the quote character at the beginning and end of the result string. - */ -extern JSString * -js_QuoteString(JSContext *cx, JSString *str, jschar quote); - -/* - * JSPrinter operations, for printf style message formatting. The return - * value from js_GetPrinterOutput() is the printer's cumulative output, in - * a GC'ed string. - */ -extern JSPrinter * -js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty); - -extern void -js_DestroyPrinter(JSPrinter *jp); - -extern JSString * -js_GetPrinterOutput(JSPrinter *jp); - -extern int -js_printf(JSPrinter *jp, const char *format, ...); - -extern JSBool -js_puts(JSPrinter *jp, const char *s); - -#ifdef DEBUG -/* - * Disassemblers, for debugging only. - */ -#include - -extern JS_FRIEND_API(JSBool) -js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp); - -extern JS_FRIEND_API(uintN) -js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, - JSBool lines, FILE *fp); -#endif /* DEBUG */ - -/* - * Decompilers, for script, function, and expression pretty-printing. - */ -extern JSBool -js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, - uintN pcdepth); - -extern JSBool -js_DecompileScript(JSPrinter *jp, JSScript *script); - -extern JSBool -js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun); - -extern JSBool -js_DecompileFunction(JSPrinter *jp, JSFunction *fun); - -/* - * Find the source expression that resulted in v, and return a new string - * containing it. Fall back on v's string conversion (fallback) if we can't - * find the bytecode that generated and pushed v on the operand stack. - * - * Search the current stack frame if spindex is JSDVG_SEARCH_STACK. Don't - * look for v on the stack if spindex is JSDVG_IGNORE_STACK. Otherwise, - * spindex is the negative index of v, measured from cx->fp->sp, or from a - * lower frame's sp if cx->fp is native. - */ -extern JSString * -js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v, - JSString *fallback); - -#define JSDVG_IGNORE_STACK 0 -#define JSDVG_SEARCH_STACK 1 - -JS_END_EXTERN_C - -#endif /* jsopcode_h___ */ diff --git a/spidermonkey/libjs/jsopcode.tbl b/spidermonkey/libjs/jsopcode.tbl deleted file mode 100644 index 4a4ca89..0000000 --- a/spidermonkey/libjs/jsopcode.tbl +++ /dev/null @@ -1,478 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=0 ft=C: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JavaScript operation bytecodes. If you need to allocate a bytecode, look - * for a name of the form JSOP_UNUSED* and claim it. Otherwise, always add at - * the end of the table. - * - * Includers must define an OPDEF macro of the following form: - * - * #define OPDEF(op,val,name,image,length,nuses,ndefs,prec,format) ... - * - * Selected arguments can be expanded in initializers. The op argument is - * expanded followed by comma in the JSOp enum (jsopcode.h), e.g. The value - * field must be dense for now, because jsopcode.c uses an OPDEF() expansion - * inside the js_CodeSpec[] initializer. - * - * Field Description - * op Bytecode name, which is the JSOp enumerator name - * value Bytecode value, which is the JSOp enumerator value - * name C string containing name for disassembler - * image C string containing "image" for pretty-printer, null if ugly - * length Number of bytes including any immediate operands - * nuses Number of stack slots consumed by bytecode, -1 if variadic - * ndefs Number of stack slots produced by bytecode - * prec Operator precedence, zero if not an operator - * format Bytecode plus immediate operand encoding format - * - * Precedence Operators Opcodes - * 1 let (x = y) z, w JSOP_LEAVEBLOCKEXPR - * 2 , JSOP_POP with SRC_PCDELTA note - * 3 =, +=, etc. JSOP_SETNAME, etc. (all JOF_ASSIGNING) - * 4 ?: JSOP_IFEQ, JSOP_IFEQX - * 5 || JSOP_OR, JSOP_ORX - * 6 && JSOP_AND, JSOP_ANDX - * 7 | JSOP_BITOR - * 8 ^ JSOP_BITXOR - * 9 & JSOP_BITAND - * 10 ==, !=, etc. JSOP_EQ, JSOP_NE, etc. - * 11 <, in, etc. JSOP_LT, JSOP_IN, etc. - * 12 <<, >>, >>> JSOP_LSH, JSOP_RSH, JSOP_URSH - * 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc. - * 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD - * 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc. - * 16 0, function(){} etc. JSOP_ZERO, JSOP_ANONFUNOBJ, etc. - * 17 delete, new JSOP_DEL*, JSOP_NEW - * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. - * 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc. - * - * The push-numeric-constant operators, JSOP_ZERO, JSOP_NUMBER, etc., have - * lower precedence than the member operators emitted for the . operator, to - * cause the decompiler to parenthesize the . left operand, e.g. (0).foo. - * Otherwise the . could be taken as a decimal point. We use the same level - * 16 for function expressions too, to force parenthesization. - * - * This file is best viewed with 128 columns: -12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 - */ - -/* legend: op val name image len use def prec format */ - -/* Longstanding JavaScript bytecodes. */ -OPDEF(JSOP_NOP, 0, "nop", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_PUSH, 1, "push", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_POPV, 2, "popv", NULL, 1, 1, 0, 2, JOF_BYTE) -OPDEF(JSOP_ENTERWITH, 3, "enterwith", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_LEAVEWITH, 4, "leavewith", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_RETURN, 5, "return", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_GOTO, 6, "goto", NULL, 3, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_IFEQ, 7, "ifeq", NULL, 3, 1, 0, 4, JOF_JUMP|JOF_DETECTING) -OPDEF(JSOP_IFNE, 8, "ifne", NULL, 3, 1, 0, 0, JOF_JUMP) - -/* Get the arguments object for the current, lightweight function activation. */ -OPDEF(JSOP_ARGUMENTS, 9, js_arguments_str, js_arguments_str, 1, 0, 1, 18, JOF_BYTE) - -/* ECMA-compliant for-in loop with argument or local variable loop control. */ -OPDEF(JSOP_FORARG, 10, "forarg", NULL, 3, 0, 1, 19, JOF_QARG|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORVAR, 11, "forvar", NULL, 3, 0, 1, 19, JOF_QVAR|JOF_NAME|JOF_FOR) - -/* More longstanding bytecodes. */ -OPDEF(JSOP_DUP, 12, "dup", NULL, 1, 1, 2, 0, JOF_BYTE) -OPDEF(JSOP_DUP2, 13, "dup2", NULL, 1, 2, 4, 0, JOF_BYTE) -OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 7, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 8, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 9, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 10, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) -OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_GE, 23, "ge", ">=", 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_LSH, 24, "lsh", "<<", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_RSH, 25, "rsh", ">>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_URSH, 26, "ursh", ">>>", 1, 2, 1, 12, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_ADD, 27, "add", "+", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_SUB, 28, "sub", "-", 1, 2, 1, 13, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MUL, 29, "mul", "*", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_DIV, 30, "div", "/", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_MOD, 31, "mod", "%", 1, 2, 1, 14, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16) -OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_CONST|JOF_NAME|JOF_DEL) -OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_CONST|JOF_PROP|JOF_DEL) -OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) -OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE) -OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) -OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC) -OPDEF(JSOP_INCELEM, 43, "incelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC) -OPDEF(JSOP_DECNAME, 44, "decname", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) -OPDEF(JSOP_DECPROP, 45, "decprop", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC) -OPDEF(JSOP_DECELEM, 46, "decelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC) -OPDEF(JSOP_NAMEINC, 47, "nameinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_PROPINC, 48, "propinc", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_INC|JOF_POST) -OPDEF(JSOP_ELEMINC, 49, "eleminc", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_INC|JOF_POST) -OPDEF(JSOP_NAMEDEC, 50, "namedec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_PROPDEC, 51, "propdec", NULL, 3, 1, 1, 15, JOF_CONST|JOF_PROP|JOF_DEC|JOF_POST) -OPDEF(JSOP_ELEMDEC, 52, "elemdec", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEC|JOF_POST) -OPDEF(JSOP_GETPROP, 53, "getprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_SETPROP, 54, "setprop", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_GETELEM, 55, "getelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) -OPDEF(JSOP_SETELEM, 56, "setelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_PUSHOBJ, 57, "pushobj", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_CALL, 58, "call", NULL, 3, -1, 1, 18, JOF_UINT16) -OPDEF(JSOP_NAME, 59, "name", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) -OPDEF(JSOP_NUMBER, 60, "number", NULL, 3, 0, 1, 16, JOF_CONST) -OPDEF(JSOP_STRING, 61, "string", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_ZERO, 62, "zero", "0", 1, 0, 1, 16, JOF_BYTE) -OPDEF(JSOP_ONE, 63, "one", "1", 1, 0, 1, 16, JOF_BYTE) -OPDEF(JSOP_NULL, 64, js_null_str, js_null_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_THIS, 65, js_this_str, js_this_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FALSE, 66, js_false_str, js_false_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_TRUE, 67, js_true_str, js_true_str, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_OR, 68, "or", NULL, 3, 1, 0, 5, JOF_JUMP|JOF_DETECTING) -OPDEF(JSOP_AND, 69, "and", NULL, 3, 1, 0, 6, JOF_JUMP|JOF_DETECTING) - -/* The switch bytecodes have variable length. */ -OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLESWITCH|JOF_DETECTING) -OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING) - -/* New, infallible/transitive identity ops. */ -OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) -OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING) - -/* Lexical closure constructor. */ -OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_CONST) - -/* Export and import ops. */ -OPDEF(JSOP_EXPORTALL, 75, "exportall", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_EXPORTNAME,76, "exportname", NULL, 3, 0, 0, 0, JOF_CONST|JOF_NAME) -OPDEF(JSOP_IMPORTALL, 77, "importall", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_IMPORTPROP,78, "importprop", NULL, 3, 1, 0, 0, JOF_CONST|JOF_PROP|JOF_IMPORT) -OPDEF(JSOP_IMPORTELEM,79, "importelem", NULL, 1, 2, 0, 0, JOF_BYTE |JOF_ELEM|JOF_IMPORT) - -/* Push object literal. */ -OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_CONST) - -/* Pop value and discard it. */ -OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) - -/* Convert value to number, for unary +. */ -OPDEF(JSOP_POS, 82, "pos", "+ ", 1, 1, 1, 15, JOF_BYTE) - -/* Trap into debugger for breakpoint, etc. */ -OPDEF(JSOP_TRAP, 83, "trap", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* Fast get/set ops for function arguments and local variables. */ -OPDEF(JSOP_GETARG, 84, "getarg", NULL, 3, 0, 1, 19, JOF_QARG |JOF_NAME) -OPDEF(JSOP_SETARG, 85, "setarg", NULL, 3, 1, 1, 3, JOF_QARG |JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_GETVAR, 86, "getvar", NULL, 3, 0, 1, 19, JOF_QVAR |JOF_NAME) -OPDEF(JSOP_SETVAR, 87, "setvar", NULL, 3, 1, 1, 3, JOF_QVAR |JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* Push unsigned 16-bit int constant. */ -OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16) - -/* Object and array literal support. */ -OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 1, 2, 1, 0, JOF_BYTE) -OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) -OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 1, 0, 3, JOF_CONST|JOF_PROP|JOF_DETECTING) -OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 2, 0, 3, JOF_BYTE |JOF_ELEM|JOF_DETECTING) -OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 3, 0, 0, 0, JOF_UINT16) -OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 3, 0, 1, 0, JOF_UINT16) - -/* Fast inc/dec ops for args and local vars. */ -OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC) -OPDEF(JSOP_INCVAR, 96, "incvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC) -OPDEF(JSOP_DECARG, 97, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC) -OPDEF(JSOP_DECVAR, 98, "decvar", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC) -OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_ARGDEC, 101,"argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_VARDEC, 102,"vardec", NULL, 3, 0, 1, 15, JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST) - -/* - * Initialize for-in iterator. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL. - */ -OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE) - -/* ECMA-compliant for/in ops. */ -OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME|JOF_FOR) -OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_FOR) -OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) -OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) - -/* ECMA-compliant assignment ops. */ -OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* Exception handling ops. */ -OPDEF(JSOP_THROW, 110,"throw", NULL, 1, 1, 0, 0, JOF_BYTE) - -/* 'in' and 'instanceof' ops. */ -OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC) - -/* debugger op */ -OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* gosub/retsub for finally handling */ -OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP) -OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* More exception handling ops. */ -OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16) - -/* - * ECMA-compliant switch statement ops. - * CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push - * lval if false; and DEFAULT is POP lval and GOTO. - */ -OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 0, JOF_JUMP) -OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) - -/* - * ECMA-compliant call to eval op - */ -OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16) - -/* - * ECMA-compliant helper for 'for (x[i] in o)' loops. - */ -OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_ASSIGNING) - -/* - * Getter and setter prefix bytecodes. These modify the next bytecode, either - * an assignment or a property initializer code, which then defines a property - * getter or setter. - */ -OPDEF(JSOP_GETTER, 123,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_SETTER, 124,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Prolog bytecodes for defining function, var, and const names. - */ -OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) -OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) -OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_CONST|JOF_DECLARING) - -/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */ -OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 16, JOF_CONST) - -/* ECMA ed. 3 named function expression. */ -OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 16, JOF_CONST) - -/* - * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately - * after to throw away the exception value. - */ -OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) - -/* ECMA-mandated parenthesization opcode, which nulls the reference base register, obj; see jsinterp.c. */ -OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* Host object extension: given 'o.item(i) = j', the left-hand side compiles JSOP_SETCALL, rather than JSOP_CALL. */ -OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET|JOF_ASSIGNING) - -/* - * Exception handling no-ops, for more economical byte-coding than SRC_TRYFIN - * srcnote-annotated JSOP_NOPs. - */ -OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Swap the top two stack elements. - */ -OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE) - -/* - * Bytecodes that avoid making an arguments object in most cases: - * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1]. - * JSOP_ARGCNT returns fp->argc. - */ -OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME) -OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE) - -/* - * Define a local function object as a local variable. - * The local variable's slot number is the first immediate two-byte operand. - * The function object's atom index is the second immediate operand. - */ -OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_INDEXCONST|JOF_DECLARING) - -/* Extended jumps. */ -OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 3, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING) -OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX) -OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX) -OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING) -OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING) - -/* Placeholders for a real jump opcode set during backpatch chain fixup. */ -OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH) -OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH) - -/* Set pending exception from the stack, to trigger rethrow. */ -OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE) - -/* Set and get return value pseudo-register in stack frame. */ -OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* Optimized global variable ops (we don't bother doing a JSOP_FORGVAR op). */ -OPDEF(JSOP_GETGVAR, 154,"getgvar", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME) -OPDEF(JSOP_SETGVAR, 155,"setgvar", NULL, 3, 1, 1, 3, JOF_CONST|JOF_NAME|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_INCGVAR, 156,"incgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC) -OPDEF(JSOP_DECGVAR, 157,"decgvar", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC) -OPDEF(JSOP_GVARINC, 158,"gvarinc", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_GVARDEC, 159,"gvardec", NULL, 3, 0, 1, 15, JOF_CONST|JOF_NAME|JOF_DEC|JOF_POST) - -/* Regular expression literal requiring special "fork on exec" handling. */ -OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_CONST) - -/* XML (ECMA-357, a.k.a. "E4X") support. */ -OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_CONST|JOF_XMLNAME) -OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_CONST|JOF_XMLNAME) -OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME) -OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE) -OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) -OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) -OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE) -OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP) -OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 1, 1, 0, 18, JOF_BYTE) -OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE) -OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_XMLOBJECT, 180,"xmlobject", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_CONST) -OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_CONST) -OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 1, 1, 0, JOF_BYTE) -OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) - -/* - * Opcodes for extended literal addressing, using unsigned 24-bit immediate - * operands to hold integer operands (JSOP_UINT24), extended atom indexes in - * script->atomMap (JSOP_LITERAL, JSOP_FINDNAME), and ops prefixed by such - * atom index immediates (JSOP_LITOPX). See jsemit.c, EmitAtomIndexOp. - */ -OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24) -OPDEF(JSOP_LITERAL, 189,"literal", NULL, 4, 0, 1, 19, JOF_UINT24) -OPDEF(JSOP_FINDNAME, 190,"findname", NULL, 4, 0, 2, 0, JOF_UINT24) -OPDEF(JSOP_LITOPX, 191,"litopx", NULL, 5, 0, 0, 0, JOF_LITOPX) - -/* - * Opcodes to help the decompiler deal with XML. - */ -OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_SETMETHOD, 194,"setmethod", NULL, 3, 2, 1, 3, JOF_CONST|JOF_PROP|JOF_SET|JOF_ASSIGNING|JOF_DETECTING) - -/* - * Stop interpretation, emitted at end of script to save the threaded bytecode - * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). - */ -OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) - -/* - * Get an extant property or element value, throwing ReferenceError if the - * identified property does not exist. - */ -OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) -OPDEF(JSOP_GETXELEM, 197,"getxelem", NULL, 1, 2, 1, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC) - -/* - * Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef). - */ -OPDEF(JSOP_TYPEOFEXPR, 198,js_typeof_str, NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING) - -/* - * Block-local scope support. - */ -OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST) -OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16) -OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME) -OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|JOF_NAME|JOF_SET) -OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC) -OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC) -OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST) -OPDEF(JSOP_LOCALDEC, 206,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST) -OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL|JOF_NAME|JOF_FOR) - -/* - * Iterator, generator, and array comprehension support. - */ -OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE) -OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) -OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) -OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) - -OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL, 1, 1, 1, 0, JOF_BYTE) - -/* - * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). - */ -OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET|JOF_ASSIGNING) - -/* - * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, - * which must be moved down when the block pops. - */ -OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, 0, 0, 1, JOF_UINT16) diff --git a/spidermonkey/libjs/jsosdep.h b/spidermonkey/libjs/jsosdep.h deleted file mode 100644 index a266144..0000000 --- a/spidermonkey/libjs/jsosdep.h +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsosdep_h___ -#define jsosdep_h___ -/* - * OS (and machine, and compiler XXX) dependent information. - */ - -#if defined(XP_WIN) || defined(XP_OS2) - -#if defined(_WIN32) || defined (XP_OS2) -#define JS_HAVE_LONG_LONG -#else -#undef JS_HAVE_LONG_LONG -#endif -#endif /* XP_WIN || XP_OS2 */ - -#ifdef XP_BEOS -#define JS_HAVE_LONG_LONG -#endif - - -#ifdef XP_UNIX - -/* - * Get OS specific header information. - */ -#if defined(XP_MACOSX) || defined(DARWIN) -#define JS_HAVE_LONG_LONG - -#elif defined(AIXV3) || defined(AIX) -#define JS_HAVE_LONG_LONG - -#elif defined(BSDI) -#define JS_HAVE_LONG_LONG - -#elif defined(HPUX) -#define JS_HAVE_LONG_LONG - -#elif defined(IRIX) -#define JS_HAVE_LONG_LONG - -#elif defined(linux) -#define JS_HAVE_LONG_LONG - -#elif defined(OSF1) -#define JS_HAVE_LONG_LONG - -#elif defined(_SCO_DS) -#undef JS_HAVE_LONG_LONG - -#elif defined(SOLARIS) -#define JS_HAVE_LONG_LONG - -#elif defined(FREEBSD) -#define JS_HAVE_LONG_LONG - -#elif defined(SUNOS4) -#undef JS_HAVE_LONG_LONG - -/* -** Missing function prototypes -*/ - -extern void *sbrk(int); - -#elif defined(UNIXWARE) -#undef JS_HAVE_LONG_LONG - -#elif defined(VMS) && defined(__ALPHA) -#define JS_HAVE_LONG_LONG - -#endif - -#endif /* XP_UNIX */ - -#endif /* jsosdep_h___ */ - diff --git a/spidermonkey/libjs/jsotypes.h b/spidermonkey/libjs/jsotypes.h deleted file mode 100644 index 38d7286..0000000 --- a/spidermonkey/libjs/jsotypes.h +++ /dev/null @@ -1,202 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This section typedefs the old 'native' types to the new PRs. - * These definitions are scheduled to be eliminated at the earliest - * possible time. The NSPR API is implemented and documented using - * the new definitions. - */ - -/* - * Note that we test for PROTYPES_H, not JSOTYPES_H. This is to avoid - * double-definitions of scalar types such as uint32, if NSPR's - * protypes.h is also included. - */ -#ifndef PROTYPES_H -#define PROTYPES_H - -#ifdef XP_BEOS -/* BeOS defines most int types in SupportDefs.h (int8, uint8, int16, - * uint16, int32, uint32, int64, uint64), so in the interest of - * not conflicting with other definitions elsewhere we have to skip the - * #ifdef jungle below, duplicate some definitions, and do our stuff. - */ -#include - -typedef JSUintn uintn; -#ifndef _XP_Core_ -typedef JSIntn intn; -#endif - -#else - -/* SVR4 typedef of uint is commonly found on UNIX machines. */ -#if defined(XP_UNIX) && !defined(__QNXNTO__) -#include -#else -typedef JSUintn uint; -#endif - -typedef JSUintn uintn; -typedef JSUint64 uint64; -#if !defined(_WIN32) && !defined(XP_OS2) -typedef JSUint32 uint32; -#else -typedef unsigned long uint32; -#endif -typedef JSUint16 uint16; -typedef JSUint8 uint8; - -#ifndef _XP_Core_ -typedef JSIntn intn; -#endif - -/* - * On AIX 4.3, sys/inttypes.h (which is included by sys/types.h, a very - * common header file) defines the types int8, int16, int32, and int64. - * So we don't define these four types here to avoid conflicts in case - * the code also includes sys/types.h. - */ -#if defined(AIX) && defined(HAVE_SYS_INTTYPES_H) -#include -#else -typedef JSInt64 int64; - -/* /usr/include/model.h on HP-UX defines int8, int16, and int32 */ -#ifdef HPUX -#include -#else -#if !defined(_WIN32) && !defined(XP_OS2) -typedef JSInt32 int32; -#else -typedef long int32; -#endif -typedef JSInt16 int16; -typedef JSInt8 int8; -#endif /* HPUX */ -#endif /* AIX && HAVE_SYS_INTTYPES_H */ - -#endif /* XP_BEOS */ - -typedef JSFloat64 float64; - -/* Re: jsbit.h */ -#define TEST_BIT JS_TEST_BIT -#define SET_BIT JS_SET_BIT -#define CLEAR_BIT JS_CLEAR_BIT - -/* Re: prarena.h->plarena.h */ -#define PRArena PLArena -#define PRArenaPool PLArenaPool -#define PRArenaStats PLArenaStats -#define PR_ARENA_ALIGN PL_ARENA_ALIGN -#define PR_INIT_ARENA_POOL PL_INIT_ARENA_POOL -#define PR_ARENA_ALLOCATE PL_ARENA_ALLOCATE -#define PR_ARENA_GROW PL_ARENA_GROW -#define PR_ARENA_MARK PL_ARENA_MARK -#define PR_CLEAR_UNUSED PL_CLEAR_UNUSED -#define PR_CLEAR_ARENA PL_CLEAR_ARENA -#define PR_ARENA_RELEASE PL_ARENA_RELEASE -#define PR_COUNT_ARENA PL_COUNT_ARENA -#define PR_ARENA_DESTROY PL_ARENA_DESTROY -#define PR_InitArenaPool PL_InitArenaPool -#define PR_FreeArenaPool PL_FreeArenaPool -#define PR_FinishArenaPool PL_FinishArenaPool -#define PR_CompactArenaPool PL_CompactArenaPool -#define PR_ArenaFinish PL_ArenaFinish -#define PR_ArenaAllocate PL_ArenaAllocate -#define PR_ArenaGrow PL_ArenaGrow -#define PR_ArenaRelease PL_ArenaRelease -#define PR_ArenaCountAllocation PL_ArenaCountAllocation -#define PR_ArenaCountInplaceGrowth PL_ArenaCountInplaceGrowth -#define PR_ArenaCountGrowth PL_ArenaCountGrowth -#define PR_ArenaCountRelease PL_ArenaCountRelease -#define PR_ArenaCountRetract PL_ArenaCountRetract - -/* Re: prevent.h->plevent.h */ -#define PREvent PLEvent -#define PREventQueue PLEventQueue -#define PR_CreateEventQueue PL_CreateEventQueue -#define PR_DestroyEventQueue PL_DestroyEventQueue -#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor -#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR -#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR -#define PR_PostEvent PL_PostEvent -#define PR_PostSynchronousEvent PL_PostSynchronousEvent -#define PR_GetEvent PL_GetEvent -#define PR_EventAvailable PL_EventAvailable -#define PREventFunProc PLEventFunProc -#define PR_MapEvents PL_MapEvents -#define PR_RevokeEvents PL_RevokeEvents -#define PR_ProcessPendingEvents PL_ProcessPendingEvents -#define PR_WaitForEvent PL_WaitForEvent -#define PR_EventLoop PL_EventLoop -#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD -#define PRHandleEventProc PLHandleEventProc -#define PRDestroyEventProc PLDestroyEventProc -#define PR_InitEvent PL_InitEvent -#define PR_GetEventOwner PL_GetEventOwner -#define PR_HandleEvent PL_HandleEvent -#define PR_DestroyEvent PL_DestroyEvent -#define PR_DequeueEvent PL_DequeueEvent -#define PR_GetMainEventQueue PL_GetMainEventQueue - -/* Re: prhash.h->plhash.h */ -#define PRHashEntry PLHashEntry -#define PRHashTable PLHashTable -#define PRHashNumber PLHashNumber -#define PRHashFunction PLHashFunction -#define PRHashComparator PLHashComparator -#define PRHashEnumerator PLHashEnumerator -#define PRHashAllocOps PLHashAllocOps -#define PR_NewHashTable PL_NewHashTable -#define PR_HashTableDestroy PL_HashTableDestroy -#define PR_HashTableRawLookup PL_HashTableRawLookup -#define PR_HashTableRawAdd PL_HashTableRawAdd -#define PR_HashTableRawRemove PL_HashTableRawRemove -#define PR_HashTableAdd PL_HashTableAdd -#define PR_HashTableRemove PL_HashTableRemove -#define PR_HashTableEnumerateEntries PL_HashTableEnumerateEntries -#define PR_HashTableLookup PL_HashTableLookup -#define PR_HashTableDump PL_HashTableDump -#define PR_HashString PL_HashString -#define PR_CompareStrings PL_CompareStrings -#define PR_CompareValues PL_CompareValues - -#endif /* !defined(PROTYPES_H) */ diff --git a/spidermonkey/libjs/jsparse.c b/spidermonkey/libjs/jsparse.c deleted file mode 100644 index 132e2ad..0000000 --- a/spidermonkey/libjs/jsparse.c +++ /dev/null @@ -1,6547 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS parser. - * - * This is a recursive-descent parser for the JavaScript language specified by - * "The JavaScript 1.5 Language Specification". It uses lexical and semantic - * feedback to disambiguate non-LL(1) structures. It generates trees of nodes - * induced by the recursive parsing (not precise syntax trees, see jsparse.h). - * After tree construction, it rewrites trees to fold constants and evaluate - * compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to - * generate bytecode. - * - * This parser attempts no error recovery. - */ -#include "jsstddef.h" -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" - -#if JS_HAS_XML_SUPPORT -#include "jsxml.h" -#endif - -#if JS_HAS_DESTRUCTURING -#include "jsdhash.h" -#endif - -/* - * JS parsers, from lowest to highest precedence. - * - * Each parser takes a context, a token stream, and a tree context struct. - * Each returns a parse node tree or null on error. - */ - -typedef JSParseNode * -JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc); - -typedef JSParseNode * -JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowCallSyntax); - -typedef JSParseNode * -JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tt, JSBool afterDot); - -static JSParser FunctionStmt; -static JSParser FunctionExpr; -static JSParser Statements; -static JSParser Statement; -static JSParser Variables; -static JSParser Expr; -static JSParser AssignExpr; -static JSParser CondExpr; -static JSParser OrExpr; -static JSParser AndExpr; -static JSParser BitOrExpr; -static JSParser BitXorExpr; -static JSParser BitAndExpr; -static JSParser EqExpr; -static JSParser RelExpr; -static JSParser ShiftExpr; -static JSParser AddExpr; -static JSParser MulExpr; -static JSParser UnaryExpr; -static JSMemberParser MemberExpr; -static JSPrimaryParser PrimaryExpr; - -/* - * Insist that the next token be of type tt, or report errno and return null. - * NB: this macro uses cx and ts from its lexical environment. - */ -#define MUST_MATCH_TOKEN(tt, errno) \ - JS_BEGIN_MACRO \ - if (js_GetToken(cx, ts) != tt) { \ - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ - errno); \ - return NULL; \ - } \ - JS_END_MACRO - -#define CHECK_RECURSION() \ - JS_BEGIN_MACRO \ - int stackDummy; \ - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \ - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, \ - JSMSG_OVER_RECURSED); \ - return NULL; \ - } \ - JS_END_MACRO - -#ifdef METER_PARSENODES -static uint32 parsenodes = 0; -static uint32 maxparsenodes = 0; -static uint32 recyclednodes = 0; -#endif - -static JSParseNode * -RecycleTree(JSParseNode *pn, JSTreeContext *tc) -{ - JSParseNode *next; - - if (!pn) - return NULL; - JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ - next = pn->pn_next; - pn->pn_next = tc->nodeList; - tc->nodeList = pn; -#ifdef METER_PARSENODES - recyclednodes++; -#endif - return next; -} - -static JSParseNode * -NewOrRecycledNode(JSContext *cx, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = tc->nodeList; - if (!pn) { - JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool); - if (!pn) - JS_ReportOutOfMemory(cx); - } else { - tc->nodeList = pn->pn_next; - - /* Recycle immediate descendents only, to save work and working set. */ - switch (pn->pn_arity) { - case PN_FUNC: - RecycleTree(pn->pn_body, tc); - break; - case PN_LIST: - if (pn->pn_head) { - /* XXX check for dup recycles in the list */ - *pn->pn_tail = tc->nodeList; - tc->nodeList = pn->pn_head; -#ifdef METER_PARSENODES - recyclednodes += pn->pn_count; -#endif - } - break; - case PN_TERNARY: - RecycleTree(pn->pn_kid1, tc); - RecycleTree(pn->pn_kid2, tc); - RecycleTree(pn->pn_kid3, tc); - break; - case PN_BINARY: - RecycleTree(pn->pn_left, tc); - RecycleTree(pn->pn_right, tc); - break; - case PN_UNARY: - RecycleTree(pn->pn_kid, tc); - break; - case PN_NAME: - RecycleTree(pn->pn_expr, tc); - break; - case PN_NULLARY: - break; - } - } -#ifdef METER_PARSENODES - if (pn) { - parsenodes++; - if (parsenodes - recyclednodes > maxparsenodes) - maxparsenodes = parsenodes - recyclednodes; - } -#endif - return pn; -} - -/* - * Allocate a JSParseNode from cx's temporary arena. - */ -static JSParseNode * -NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, - JSTreeContext *tc) -{ - JSParseNode *pn; - JSToken *tp; - - pn = NewOrRecycledNode(cx, tc); - if (!pn) - return NULL; - tp = &CURRENT_TOKEN(ts); - pn->pn_type = tp->type; - pn->pn_pos = tp->pos; - pn->pn_op = JSOP_NOP; - pn->pn_arity = arity; - pn->pn_next = NULL; - pn->pn_ts = ts; - pn->pn_source = NULL; - return pn; -} - -static JSParseNode * -NewBinary(JSContext *cx, JSTokenType tt, - JSOp op, JSParseNode *left, JSParseNode *right, - JSTreeContext *tc) -{ - JSParseNode *pn, *pn1, *pn2; - - if (!left || !right) - return NULL; - - /* - * Flatten a left-associative (left-heavy) tree of a given operator into - * a list, to reduce js_FoldConstants and js_EmitTree recursion. - */ - if (left->pn_type == tt && - left->pn_op == op && - (js_CodeSpec[op].format & JOF_LEFTASSOC)) { - if (left->pn_arity != PN_LIST) { - pn1 = left->pn_left, pn2 = left->pn_right; - left->pn_arity = PN_LIST; - PN_INIT_LIST_1(left, pn1); - PN_APPEND(left, pn2); - if (tt == TOK_PLUS) { - if (pn1->pn_type == TOK_STRING) - left->pn_extra |= PNX_STRCAT; - else if (pn1->pn_type != TOK_NUMBER) - left->pn_extra |= PNX_CANTFOLD; - if (pn2->pn_type == TOK_STRING) - left->pn_extra |= PNX_STRCAT; - else if (pn2->pn_type != TOK_NUMBER) - left->pn_extra |= PNX_CANTFOLD; - } - } - PN_APPEND(left, right); - left->pn_pos.end = right->pn_pos.end; - if (tt == TOK_PLUS) { - if (right->pn_type == TOK_STRING) - left->pn_extra |= PNX_STRCAT; - else if (right->pn_type != TOK_NUMBER) - left->pn_extra |= PNX_CANTFOLD; - } - return left; - } - - /* - * Fold constant addition immediately, to conserve node space and, what's - * more, so js_FoldConstants never sees mixed addition and concatenation - * operations with more than one leading non-string operand in a PN_LIST - * generated for expressions such as 1 + 2 + "pt" (which should evaluate - * to "3pt", not "12pt"). - */ - if (tt == TOK_PLUS && - left->pn_type == TOK_NUMBER && - right->pn_type == TOK_NUMBER) { - left->pn_dval += right->pn_dval; - left->pn_pos.end = right->pn_pos.end; - RecycleTree(right, tc); - return left; - } - - pn = NewOrRecycledNode(cx, tc); - if (!pn) - return NULL; - pn->pn_type = tt; - pn->pn_pos.begin = left->pn_pos.begin; - pn->pn_pos.end = right->pn_pos.end; - pn->pn_op = op; - pn->pn_arity = PN_BINARY; - pn->pn_left = left; - pn->pn_right = right; - pn->pn_next = NULL; - pn->pn_ts = NULL; - pn->pn_source = NULL; - return pn; -} - -#if JS_HAS_GETTER_SETTER -static JSTokenType -CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt) -{ - JSAtom *atom; - JSRuntime *rt; - JSOp op; - const char *name; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME); - atom = CURRENT_TOKEN(ts).t_atom; - rt = cx->runtime; - if (atom == rt->atomState.getterAtom) - op = JSOP_GETTER; - else if (atom == rt->atomState.setterAtom) - op = JSOP_SETTER; - else - return TOK_NAME; - if (js_PeekTokenSameLine(cx, ts) != tt) - return TOK_NAME; - (void) js_GetToken(cx, ts); - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_GETTER_OR_SETTER, - (op == JSOP_GETTER) - ? js_getter_str - : js_setter_str); - return TOK_ERROR; - } - CURRENT_TOKEN(ts).t_op = op; - if (JS_HAS_STRICT_OPTION(cx)) { - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_DEPRECATED_USAGE, - name)) { - return TOK_ERROR; - } - } - return tt; -} -#endif - -static void -MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, - JSStackFrame *newfp) -{ - /* - * Always push a new frame if the current frame is special, so that - * Variables gets the correct variables object: the one from the special - * frame's caller. - */ - if (oldfp && - oldfp->varobj && - oldfp->scopeChain == chain && - !(oldfp->flags & JSFRAME_SPECIAL)) { - return; - } - - memset(newfp, 0, sizeof *newfp); - - /* Default to sharing the same variables object and scope chain. */ - newfp->varobj = newfp->scopeChain = chain; - if (cx->options & JSOPTION_VAROBJFIX) { - while ((chain = JS_GetParent(cx, chain)) != NULL) - newfp->varobj = chain; - } - newfp->down = oldfp; - if (oldfp) { - /* - * In the case of eval and debugger frames, we need to dig down and find - * the real variables objects and function that our new stack frame is - * going to use. - */ - newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | - JSFRAME_SCRIPT_OBJECT); - while (oldfp->flags & JSFRAME_SPECIAL) { - oldfp = oldfp->down; - if (!oldfp) - break; - } - if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) { - newfp->varobj = oldfp->varobj; - newfp->vars = oldfp->vars; - newfp->fun = oldfp->fun; - } - } - cx->fp = newfp; -} - -/* - * Parse a top-level JS script. - */ -JS_FRIEND_API(JSParseNode *) -js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts) -{ - JSStackFrame *fp, frame; - JSTreeContext tc; - JSParseNode *pn; - - /* - * Push a compiler frame if we have no frames, or if the top frame is a - * lightweight function activation, or if its scope chain doesn't match - * the one passed to us. - */ - fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); - - /* - * Protect atoms from being collected by a GC activation, which might - * - nest on this thread due to out of memory (the so-called "last ditch" - * GC attempted within js_NewGCThing), or - * - run for any reason on another thread if this thread is suspended on - * an object lock before it finishes generating bytecode into a script - * protected from the GC by a root or a stack frame reference. - */ - JS_KEEP_ATOMS(cx->runtime); - TREE_CONTEXT_INIT(&tc); - pn = Statements(cx, ts, &tc); - if (pn) { - if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - pn = NULL; - } else { - pn->pn_type = TOK_LC; - if (!js_FoldConstants(cx, pn, &tc)) - pn = NULL; - } - } - - TREE_CONTEXT_FINISH(&tc); - JS_UNKEEP_ATOMS(cx->runtime); - cx->fp = fp; - return pn; -} - -/* - * Compile a top-level script. - */ -JS_FRIEND_API(JSBool) -js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSCodeGenerator *cg) -{ - JSStackFrame *fp, frame; - uint32 flags; - JSParseNode *pn; - JSBool ok; -#ifdef METER_PARSENODES - void *sbrk(ptrdiff_t), *before = sbrk(0); -#endif - - /* - * Push a compiler frame if we have no frames, or if the top frame is a - * lightweight function activation, or if its scope chain doesn't match - * the one passed to us. - */ - fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); - flags = cx->fp->flags; - cx->fp->flags = flags | - (JS_HAS_COMPILE_N_GO_OPTION(cx) - ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO - : JSFRAME_COMPILING); - - /* Prevent GC activation while compiling. */ - JS_KEEP_ATOMS(cx->runtime); - - pn = Statements(cx, ts, &cg->treeContext); - if (!pn) { - ok = JS_FALSE; - } else if (!js_MatchToken(cx, ts, TOK_EOF)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - ok = JS_FALSE; - } else { -#ifdef METER_PARSENODES - printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", - (char *)sbrk(0) - (char *)before, - parsenodes, - maxparsenodes, - parsenodes - recyclednodes); - before = sbrk(0); -#endif - - /* - * No need to emit bytecode here -- Statements already has, for each - * statement in turn. Search for TCF_COMPILING in Statements, below. - * That flag is set for every tc == &cg->treeContext, and it implies - * that the tc can be downcast to a cg and used to emit code during - * parsing, rather than at the end of the parse phase. - * - * Nowadays the threaded interpreter needs a stop instruction, so we - * do have to emit that here. - */ - JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); - ok = js_Emit1(cx, cg, JSOP_STOP) >= 0; - } - -#ifdef METER_PARSENODES - printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", - (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); -#endif -#ifdef JS_ARENAMETER - JS_DumpArenaStats(stdout); -#endif - JS_UNKEEP_ATOMS(cx->runtime); - cx->fp->flags = flags; - cx->fp = fp; - return ok; -} - -/* - * Insist on a final return before control flows out of pn. Try to be a bit - * smart about loops: do {...; return e2;} while(0) at the end of a function - * that contains an early return e1 will get a strict warning. Similarly for - * iloops: while (true){...} is treated as though ... returns. - */ -#define ENDS_IN_OTHER 0 -#define ENDS_IN_RETURN 1 -#define ENDS_IN_BREAK 2 - -static int -HasFinalReturn(JSParseNode *pn) -{ - JSParseNode *pn2, *pn3; - uintN rv, rv2, hasDefault; - - switch (pn->pn_type) { - case TOK_LC: - if (!pn->pn_head) - return ENDS_IN_OTHER; - return HasFinalReturn(PN_LAST(pn)); - - case TOK_IF: - if (!pn->pn_kid3) - return ENDS_IN_OTHER; - return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); - - case TOK_WHILE: - pn2 = pn->pn_left; - if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) - return ENDS_IN_RETURN; - if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) - return ENDS_IN_RETURN; - return ENDS_IN_OTHER; - - case TOK_DO: - pn2 = pn->pn_right; - if (pn2->pn_type == TOK_PRIMARY) { - if (pn2->pn_op == JSOP_FALSE) - return HasFinalReturn(pn->pn_left); - if (pn2->pn_op == JSOP_TRUE) - return ENDS_IN_RETURN; - } - if (pn2->pn_type == TOK_NUMBER) { - if (pn2->pn_dval == 0) - return HasFinalReturn(pn->pn_left); - return ENDS_IN_RETURN; - } - return ENDS_IN_OTHER; - - case TOK_FOR: - pn2 = pn->pn_left; - if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) - return ENDS_IN_RETURN; - return ENDS_IN_OTHER; - - case TOK_SWITCH: - rv = ENDS_IN_RETURN; - hasDefault = ENDS_IN_OTHER; - pn2 = pn->pn_right; - if (pn2->pn_type == TOK_LEXICALSCOPE) - pn2 = pn2->pn_expr; - for (pn2 = pn2->pn_head; rv && pn2; pn2 = pn2->pn_next) { - if (pn2->pn_type == TOK_DEFAULT) - hasDefault = ENDS_IN_RETURN; - pn3 = pn2->pn_right; - JS_ASSERT(pn3->pn_type == TOK_LC); - if (pn3->pn_head) { - rv2 = HasFinalReturn(PN_LAST(pn3)); - if (rv2 == ENDS_IN_OTHER && pn2->pn_next) - /* Falling through to next case or default. */; - else - rv &= rv2; - } - } - /* If a final switch has no default case, we judge it harshly. */ - rv &= hasDefault; - return rv; - - case TOK_BREAK: - return ENDS_IN_BREAK; - - case TOK_WITH: - return HasFinalReturn(pn->pn_right); - - case TOK_RETURN: - return ENDS_IN_RETURN; - - case TOK_COLON: - case TOK_LEXICALSCOPE: - return HasFinalReturn(pn->pn_expr); - - case TOK_THROW: - return ENDS_IN_RETURN; - - case TOK_TRY: - /* If we have a finally block that returns, we are done. */ - if (pn->pn_kid3) { - rv = HasFinalReturn(pn->pn_kid3); - if (rv == ENDS_IN_RETURN) - return rv; - } - - /* Else check the try block and any and all catch statements. */ - rv = HasFinalReturn(pn->pn_kid1); - if (pn->pn_kid2) { - JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); - for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) - rv &= HasFinalReturn(pn2); - } - return rv; - - case TOK_CATCH: - /* Check this catch block's body. */ - return HasFinalReturn(pn->pn_kid3); - - case TOK_LET: - /* Non-binary let statements are let declarations. */ - if (pn->pn_arity != PN_BINARY) - return ENDS_IN_OTHER; - return HasFinalReturn(pn->pn_right); - - default: - return ENDS_IN_OTHER; - } -} - -static JSBool -ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum, - uintN anonerrnum) -{ - JSFunction *fun; - const char *name; - - fun = cx->fp->fun; - if (fun->atom) { - name = js_AtomToPrintableString(cx, fun->atom); - } else { - errnum = anonerrnum; - name = NULL; - } - return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum, - name); -} - -static JSBool -CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) -{ - return HasFinalReturn(pn) == ENDS_IN_RETURN || - ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); -} - -static JSParseNode * -FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, - JSTreeContext *tc) -{ - JSStackFrame *fp, frame; - JSObject *funobj; - JSStmtInfo stmtInfo; - uintN oldflags, firstLine; - JSParseNode *pn; - - fp = cx->fp; - funobj = fun->object; - if (!fp || fp->fun != fun || fp->varobj != funobj || - fp->scopeChain != funobj) { - memset(&frame, 0, sizeof frame); - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - if (fp) - frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; - cx->fp = &frame; - } - - /* - * Set interpreted early so js_EmitTree can test it to decide whether to - * eliminate useless expressions. - */ - fun->flags |= JSFUN_INTERPRETED; - - js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); - stmtInfo.flags = SIF_BODY_BLOCK; - - oldflags = tc->flags; - tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); - tc->flags |= TCF_IN_FUNCTION; - - /* - * Save the body's first line, and store it in pn->pn_pos.begin.lineno - * later, because we may have not peeked in ts yet, so Statements won't - * acquire a valid pn->pn_pos.begin from the current token. - */ - firstLine = ts->lineno; - pn = Statements(cx, ts, tc); - - js_PopStatement(tc); - - /* Check for falling off the end of a function that returns a value. */ - if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { - if (!CheckFinalReturn(cx, ts, pn)) - pn = NULL; - } - - /* - * If we have a parse tree in pn and a code generator in tc, emit this - * function's code. We must do this here, not in js_CompileFunctionBody, - * in order to detect TCF_IN_FUNCTION among tc->flags. - */ - if (pn) { - pn->pn_pos.begin.lineno = firstLine; - if ((tc->flags & TCF_COMPILING)) { - JSCodeGenerator *cg = (JSCodeGenerator *) tc; - - if (!js_FoldConstants(cx, pn, tc) || - !js_EmitFunctionBytecode(cx, cg, pn)) { - pn = NULL; - } - } - } - - cx->fp = fp; - tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); - return pn; -} - -/* - * Compile a JS function body, which might appear as the value of an event - * handler attribute in an HTML tag. - */ -JSBool -js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun) -{ - JSArenaPool codePool, notePool; - JSCodeGenerator funcg; - JSStackFrame *fp, frame; - JSObject *funobj; - JSParseNode *pn; - - JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); - JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); - if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool, - ts->filename, ts->lineno, - ts->principals)) { - return JS_FALSE; - } - - /* Prevent GC activation while compiling. */ - JS_KEEP_ATOMS(cx->runtime); - - /* Push a JSStackFrame for use by FunctionBody. */ - fp = cx->fp; - funobj = fun->object; - JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && - fp->scopeChain != funobj)); - memset(&frame, 0, sizeof frame); - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) - ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO - : JSFRAME_COMPILING; - cx->fp = &frame; - - /* - * Farble the body so that it looks like a block statement to js_EmitTree, - * which is called beneath FunctionBody; see Statements, further below in - * this file. FunctionBody pushes a STMT_BLOCK record around its call to - * Statements, so Statements will not compile each statement as it loops - * to save JSParseNode space -- it will not compile at all, only build a - * JSParseNode tree. - * - * Therefore we must fold constants, allocate try notes, and generate code - * for this function, including a stop opcode at the end. - */ - CURRENT_TOKEN(ts).type = TOK_LC; - pn = FunctionBody(cx, ts, fun, &funcg.treeContext); - if (pn && !js_NewScriptFromCG(cx, &funcg, fun)) - pn = NULL; - - /* Restore saved state and release code generation arenas. */ - cx->fp = fp; - JS_UNKEEP_ATOMS(cx->runtime); - js_FinishCodeGenerator(cx, &funcg); - JS_FinishArenaPool(&codePool); - JS_FinishArenaPool(¬ePool); - return pn != NULL; -} - -/* - * Parameter block types for the several Binder functions. We use a common - * helper function signature in order to share code among destructuring and - * simple variable declaration parsers. In the destructuring case, the binder - * function is called indirectly from the variable declaration parser by way - * of CheckDestructuring and its friends. - */ -typedef struct BindData BindData; - -typedef JSBool -(*Binder)(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc); - -struct BindData { - JSParseNode *pn; /* error source coordinate */ - JSTokenStream *ts; /* fallback if pn is null */ - JSObject *obj; /* the variable object */ - JSOp op; /* prolog bytecode or nop */ - Binder binder; /* binder, discriminates u */ - union { - struct { - JSFunction *fun; /* must come first! see next */ - } arg; - struct { - JSFunction *fun; /* this overlays u.arg.fun */ - JSClass *clasp; - JSPropertyOp getter; - JSPropertyOp setter; - uintN attrs; - } var; - struct { - jsuint index; - uintN overflow; - } let; - } u; -}; - -/* - * Given BindData *data and JSREPORT_* flags, expand to the second and third - * actual parameters to js_ReportCompileErrorNumber. Prefer reporting via pn - * to reporting via ts, for better destructuring error pointers. - */ -#define BIND_DATA_REPORT_ARGS(data, flags) \ - (data)->pn ? (void *)(data)->pn : (void *)(data)->ts, \ - ((data)->pn ? JSREPORT_PN : JSREPORT_TS) | (flags) - -static JSBool -BumpFormalCount(JSContext *cx, JSFunction *fun) -{ - if (fun->nargs == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - return JS_FALSE; - } - fun->nargs++; - return JS_TRUE; -} - -static JSBool -BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSObject *obj, *pobj; - JSProperty *prop; - JSBool ok; - uintN dupflag; - JSFunction *fun; - const char *name; - - obj = data->obj; - ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - if (!ok) - return JS_FALSE; - - dupflag = 0; - if (prop) { - JS_ASSERT(pobj == obj); - name = js_AtomToPrintableString(cx, atom); - - /* - * A duplicate parameter name, a "feature" required by ECMA-262. - * We force a duplicate node on the SCOPE_LAST_PROP(scope) list - * with the same id, distinguished by the SPROP_IS_DUPLICATE flag, - * and not mapped by an entry in scope. - */ - ok = name && - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_DUPLICATE_FORMAL, - name); - - OBJ_DROP_PROPERTY(cx, pobj, prop); - if (!ok) - return JS_FALSE; - - dupflag = SPROP_IS_DUPLICATE; - } - - fun = data->u.arg.fun; - if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), - js_GetArgument, js_SetArgument, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - dupflag | SPROP_HAS_SHORTID, - fun->nargs)) { - return JS_FALSE; - } - - return BumpFormalCount(cx, fun); -} - -static JSBool -BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) -{ - JSFunction *fun; - - /* - * Can't increase fun->nvars in an active frame, so insist that getter is - * js_GetLocalVariable, not js_GetCallVariable or anything else. - */ - if (data->u.var.getter != js_GetLocalVariable) - return JS_TRUE; - - /* - * Don't bind a variable with the hidden name 'arguments', per ECMA-262. - * Instead 'var arguments' always restates the predefined property of the - * activation objects with unhidden name 'arguments'. Assignment to such - * a variable must be handled specially. - */ - if (atom == cx->runtime->atomState.argumentsAtom) - return JS_TRUE; - - fun = data->u.var.fun; - if (!js_AddHiddenProperty(cx, data->obj, ATOM_TO_JSID(atom), - data->u.var.getter, data->u.var.setter, - SPROP_INVALID_SLOT, - data->u.var.attrs | JSPROP_SHARED, - SPROP_HAS_SHORTID, fun->u.i.nvars)) { - return JS_FALSE; - } - if (fun->u.i.nvars == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_VARS); - return JS_FALSE; - } - fun->u.i.nvars++; - return JS_TRUE; -} - -#if JS_HAS_DESTRUCTURING -/* - * Forward declaration to maintain top-down presentation. - */ -static JSParseNode * -DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, - JSTokenType tt); - -static JSBool -BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, - JSTreeContext *tc) -{ - JSAtomListElement *ale; - JSFunction *fun; - JSObject *obj, *pobj; - JSProperty *prop; - const char *name; - - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (!ale) { - ale = js_IndexAtom(cx, atom, &tc->decls); - if (!ale) - return JS_FALSE; - ALE_SET_JSOP(ale, data->op); - } - - fun = data->u.var.fun; - obj = data->obj; - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) - return JS_FALSE; - - if (prop) { - JS_ASSERT(pobj == obj && OBJ_IS_NATIVE(pobj)); - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_DUPLICATE_FORMAL, - name)) { - return JS_FALSE; - } - OBJ_DROP_PROPERTY(cx, pobj, prop); - } else { - if (!BindLocalVariable(cx, data, atom)) - return JS_FALSE; - } - return JS_TRUE; -} -#endif /* JS_HAS_DESTRUCTURING */ - -static JSParseNode * -FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool lambda) -{ - JSOp op, prevop; - JSParseNode *pn, *body, *result; - JSTokenType tt; - JSAtom *funAtom, *objAtom; - JSStackFrame *fp; - JSObject *varobj, *pobj; - JSAtomListElement *ale; - JSProperty *prop; - JSFunction *fun; - JSTreeContext funtc; -#if JS_HAS_DESTRUCTURING - JSParseNode *item, *list = NULL; -#endif - - /* Make a TOK_FUNCTION node. */ -#if JS_HAS_GETTER_SETTER - op = CURRENT_TOKEN(ts).t_op; -#endif - pn = NewParseNode(cx, ts, PN_FUNC, tc); - if (!pn) - return NULL; - - /* Scan the optional function name into funAtom. */ - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_NAME) { - funAtom = CURRENT_TOKEN(ts).t_atom; - } else { - funAtom = NULL; - js_UngetToken(ts); - } - - /* Find the nearest variable-declaring scope and use it as our parent. */ - fp = cx->fp; - varobj = fp->varobj; - - /* - * Record names for function statements in tc->decls so we know when to - * avoid optimizing variable references that might name a function. - */ - if (!lambda && funAtom) { - ATOM_LIST_SEARCH(ale, &tc->decls, funAtom); - if (ale) { - prevop = ALE_JSOP(ale); - if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) { - const char *name = js_AtomToPrintableString(cx, funAtom); - if (!name || - !js_ReportCompileErrorNumber(cx, ts, - (prevop != JSOP_DEFCONST) - ? JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT - : JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REDECLARED_VAR, - (prevop == JSOP_DEFFUN || - prevop == JSOP_CLOSURE) - ? js_function_str - : (prevop == JSOP_DEFCONST) - ? js_const_str - : js_var_str, - name)) { - return NULL; - } - } - if (!AT_TOP_LEVEL(tc) && prevop == JSOP_DEFVAR) - tc->flags |= TCF_FUN_CLOSURE_VS_VAR; - } else { - ale = js_IndexAtom(cx, funAtom, &tc->decls); - if (!ale) - return NULL; - } - ALE_SET_JSOP(ale, AT_TOP_LEVEL(tc) ? JSOP_DEFFUN : JSOP_CLOSURE); - - /* - * A function nested at top level inside another's body needs only a - * local variable to bind its name to its value, and not an activation - * object property (it might also need the activation property, if the - * outer function contains with statements, e.g., but the stack slot - * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a - * JSOP_GETVAR bytecode). - */ - if (AT_TOP_LEVEL(tc) && (tc->flags & TCF_IN_FUNCTION)) { - JSScopeProperty *sprop; - - /* - * Define a property on the outer function so that BindNameToSlot - * can properly optimize accesses. - */ - JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); - JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj)); - if (!js_LookupHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), - &pobj, &prop)) { - return NULL; - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - sprop = NULL; - if (!prop || - pobj != varobj || - (sprop = (JSScopeProperty *)prop, - sprop->getter != js_GetLocalVariable)) { - uintN sflags; - - /* - * Use SPROP_IS_DUPLICATE if there is a formal argument of the - * same name, so the decompiler can find the parameter name. - */ - sflags = (sprop && sprop->getter == js_GetArgument) - ? SPROP_IS_DUPLICATE | SPROP_HAS_SHORTID - : SPROP_HAS_SHORTID; - if (!js_AddHiddenProperty(cx, varobj, ATOM_TO_JSID(funAtom), - js_GetLocalVariable, - js_SetLocalVariable, - SPROP_INVALID_SLOT, - JSPROP_PERMANENT | JSPROP_SHARED, - sflags, fp->fun->u.i.nvars)) { - return NULL; - } - if (fp->fun->u.i.nvars == JS_BITMASK(16)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_VARS); - return NULL; - } - fp->fun->u.i.nvars++; - } - } - } - - fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, varobj, - funAtom); - if (!fun) - return NULL; -#if JS_HAS_GETTER_SETTER - if (op != JSOP_NOP) - fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER; -#endif - - /* - * Atomize fun->object early to protect against a last-ditch GC under - * js_LookupHiddenProperty. - * - * Absent use of the new scoped local GC roots API around compiler calls, - * we need to atomize here to protect against a GC activation. Atoms are - * protected from GC during compilation by the JS_FRIEND_API entry points - * in this file. There doesn't seem to be any gain in switching from the - * atom-keeping method to the bulkier, slower scoped local roots method. - */ - objAtom = js_AtomizeObject(cx, fun->object, 0); - if (!objAtom) - return NULL; - - /* Initialize early for possible flags mutation via DestructuringExpr. */ - TREE_CONTEXT_INIT(&funtc); - - /* Now parse formal argument list and compute fun->nargs. */ - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); - if (!js_MatchToken(cx, ts, TOK_RP)) { - BindData data; - - data.pn = NULL; - data.ts = ts; - data.obj = fun->object; - data.op = JSOP_NOP; - data.binder = BindArg; - data.u.arg.fun = fun; - - do { - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - { - JSParseNode *lhs, *rhs; - jsint slot; - - /* - * A destructuring formal parameter turns into one or more - * local variables initialized from properties of a single - * anonymous positional parameter, so here we must tweak our - * binder and its data. - */ - data.op = JSOP_DEFVAR; - data.binder = BindDestructuringArg; - data.u.var.clasp = &js_FunctionClass; - data.u.var.getter = js_GetLocalVariable; - data.u.var.setter = js_SetLocalVariable; - data.u.var.attrs = JSPROP_PERMANENT; - - /* - * Temporarily transfer the owneship of the recycle list to - * funtc. See bug 313967. - */ - funtc.nodeList = tc->nodeList; - tc->nodeList = NULL; - lhs = DestructuringExpr(cx, &data, &funtc, tt); - tc->nodeList = funtc.nodeList; - funtc.nodeList = NULL; - if (!lhs) - return NULL; - - /* - * Restore the formal parameter binder in case there are more - * non-destructuring formals in the parameter list. - */ - data.binder = BindArg; - - /* - * Adjust fun->nargs to count the single anonymous positional - * parameter that is to be destructured. - */ - slot = fun->nargs; - if (!BumpFormalCount(cx, fun)) - return NULL; - - /* - * Synthesize a destructuring assignment from the single - * anonymous positional parameter into the destructuring - * left-hand-side expression and accumulate it in list. - */ - rhs = NewParseNode(cx, ts, PN_NAME, tc); - if (!rhs) - return NULL; - rhs->pn_type = TOK_NAME; - rhs->pn_op = JSOP_GETARG; - rhs->pn_atom = cx->runtime->atomState.emptyAtom; - rhs->pn_expr = NULL; - rhs->pn_slot = slot; - rhs->pn_attrs = 0; - - item = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, lhs, rhs, tc); - if (!item) - return NULL; - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_COMMA; - PN_INIT_LIST(list); - } - PN_APPEND(list, item); - break; - } -#endif /* JS_HAS_DESTRUCTURING */ - - case TOK_NAME: - if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc)) - return NULL; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_FORMAL); - return NULL; - } - } while (js_MatchToken(cx, ts, TOK_COMMA)); - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL); - } - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY); - pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; - - /* - * Temporarily transfer the owneship of the recycle list to funtc. - * See bug 313967. - */ - funtc.nodeList = tc->nodeList; - tc->nodeList = NULL; - body = FunctionBody(cx, ts, fun, &funtc); - tc->nodeList = funtc.nodeList; - funtc.nodeList = NULL; - - if (!body) - return NULL; - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY); - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - -#if JS_HAS_DESTRUCTURING - /* - * If there were destructuring formal parameters, prepend the initializing - * comma expression that we synthesized to body. If the body is a lexical - * scope node, we must make a special TOK_BODY node, to prepend the formal - * parameter destructuring code without bracing the decompilation of the - * function body's lexical scope. - */ - if (list) { - if (body->pn_arity != PN_LIST) { - JSParseNode *block; - - JS_ASSERT(body->pn_type == TOK_LEXICALSCOPE); - JS_ASSERT(body->pn_arity == PN_NAME); - - block = NewParseNode(cx, ts, PN_LIST, tc); - if (!block) - return NULL; - block->pn_type = TOK_BODY; - block->pn_pos = body->pn_pos; - PN_INIT_LIST_1(block, body); - - body = block; - } - - item = NewParseNode(cx, ts, PN_UNARY, tc); - if (!item) - return NULL; - - item->pn_type = TOK_SEMI; - item->pn_pos.begin = item->pn_pos.end = body->pn_pos.begin; - item->pn_kid = list; - item->pn_next = body->pn_head; - body->pn_head = item; - if (body->pn_tail == &body->pn_head) - body->pn_tail = &item->pn_next; - ++body->pn_count; - } -#endif - - /* - * If we collected flags that indicate nested heavyweight functions, or - * this function contains heavyweight-making statements (references to - * __parent__ or __proto__; use of with, eval, import, or export; and - * assignment to arguments), flag the function as heavyweight (requiring - * a call object per invocation). - */ - if (funtc.flags & TCF_FUN_HEAVYWEIGHT) { - fun->flags |= JSFUN_HEAVYWEIGHT; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { - /* - * If this function is a named statement function not at top-level - * (i.e. a JSOP_CLOSURE, not a function definiton or expression), then - * our enclosing function, if any, must be heavyweight. - * - * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, - * so it won't be set here. Assert that it's not. We have to check - * it later, in js_EmitTree, after js_EmitFunctionBody has traversed - * the function's body - */ - JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); - if (!lambda && funAtom && !AT_TOP_LEVEL(tc)) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } - - result = pn; - if (lambda) { - /* - * ECMA ed. 3 standard: function expression, possibly anonymous. - */ - op = funAtom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ; - } else if (!funAtom) { - /* - * If this anonymous function definition is *not* embedded within a - * larger expression, we treat it as an expression statement, not as - * a function declaration -- and not as a syntax error (as ECMA-262 - * Edition 3 would have it). Backward compatibility trumps all. - */ - result = NewParseNode(cx, ts, PN_UNARY, tc); - if (!result) - return NULL; - result->pn_type = TOK_SEMI; - result->pn_pos = pn->pn_pos; - result->pn_kid = pn; - op = JSOP_ANONFUNOBJ; - } else if (!AT_TOP_LEVEL(tc)) { - /* - * ECMA ed. 3 extension: a function expression statement not at the - * top level, e.g., in a compound statement such as the "then" part - * of an "if" statement, binds a closure only if control reaches that - * sub-statement. - */ - op = JSOP_CLOSURE; - } else { - op = JSOP_NOP; - } - - pn->pn_funAtom = objAtom; - pn->pn_op = op; - pn->pn_body = body; - pn->pn_flags = funtc.flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS); - pn->pn_tryCount = funtc.tryCount; - TREE_CONTEXT_FINISH(&funtc); - return result; -} - -static JSParseNode * -FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - return FunctionDef(cx, ts, tc, JS_FALSE); -} - -static JSParseNode * -FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - return FunctionDef(cx, ts, tc, JS_TRUE); -} - -/* - * Parse the statements in a block, creating a TOK_LC node that lists the - * statements' trees. If called from block-parsing code, the caller must - * match { before and } after. - */ -static JSParseNode * -Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2, *saveBlock; - JSTokenType tt; - - CHECK_RECURSION(); - - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - saveBlock = tc->blockNode; - tc->blockNode = pn; - PN_INIT_LIST(pn); - - ts->flags |= TSF_OPERAND; - while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) { - ts->flags &= ~TSF_OPERAND; - pn2 = Statement(cx, ts, tc); - if (!pn2) { - if (ts->flags & TSF_EOF) - ts->flags |= TSF_UNEXPECTED_EOF; - return NULL; - } - ts->flags |= TSF_OPERAND; - - /* Detect a function statement for the TOK_LC case in Statement. */ - if (pn2->pn_type == TOK_FUNCTION && !AT_TOP_LEVEL(tc)) - tc->flags |= TCF_HAS_FUNCTION_STMT; - - /* If compiling top-level statements, emit as we go to save space. */ - if (!tc->topStmt && (tc->flags & TCF_COMPILING)) { - if (cx->fp->fun && - JS_HAS_STRICT_OPTION(cx) && - (tc->flags & TCF_RETURN_EXPR)) { - /* - * Check pn2 for lack of a final return statement if it is the - * last statement in the block. - */ - tt = js_PeekToken(cx, ts); - if ((tt == TOK_EOF || tt == TOK_RC) && - !CheckFinalReturn(cx, ts, pn2)) { - tt = TOK_ERROR; - break; - } - - /* - * Clear TCF_RETURN_EXPR so FunctionBody doesn't try to - * CheckFinalReturn again. - */ - tc->flags &= ~TCF_RETURN_EXPR; - } - if (!js_FoldConstants(cx, pn2, tc) || - !js_AllocTryNotes(cx, (JSCodeGenerator *)tc) || - !js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) { - tt = TOK_ERROR; - break; - } - RecycleTree(pn2, tc); - } else { - PN_APPEND(pn, pn2); - } - } - - /* - * Handle the case where there was a let declaration under this block. If - * it replaced tc->blockNode with a new block node then we must refresh pn - * and then restore tc->blockNode. - */ - if (tc->blockNode != pn) - pn = tc->blockNode; - tc->blockNode = saveBlock; - - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - return pn; -} - -static JSParseNode * -Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND); - pn = Expr(cx, ts, tc); - if (!pn) - return NULL; - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND); - - /* - * Check for (a = b) and "correct" it to (a == b) iff b's operator has - * greater precedence than ==. - * XXX not ECMA, but documented in several books -- now a strict warning. - */ - if (pn->pn_type == TOK_ASSIGN && - pn->pn_op == JSOP_NOP && - pn->pn_right->pn_type > TOK_EQOP) - { - JSBool rewrite = !JS_VERSION_IS_ECMA(cx); - if (!js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_EQUAL_AS_ASSIGN, - rewrite - ? "\nAssuming equality test" - : "")) { - return NULL; - } - if (rewrite) { - pn->pn_type = TOK_EQOP; - pn->pn_op = (JSOp)cx->jsop_eq; - pn2 = pn->pn_left; - switch (pn2->pn_op) { - case JSOP_SETNAME: - pn2->pn_op = JSOP_NAME; - break; - case JSOP_SETPROP: - pn2->pn_op = JSOP_GETPROP; - break; - case JSOP_SETELEM: - pn2->pn_op = JSOP_GETELEM; - break; - default: - JS_ASSERT(0); - } - } - } - return pn; -} - -static JSBool -MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) -{ - JSAtom *label; - JSTokenType tt; - - tt = js_PeekTokenSameLine(cx, ts); - if (tt == TOK_ERROR) - return JS_FALSE; - if (tt == TOK_NAME) { - (void) js_GetToken(cx, ts); - label = CURRENT_TOKEN(ts).t_atom; - } else { - label = NULL; - } - pn->pn_atom = label; - return JS_TRUE; -} - -#if JS_HAS_EXPORT_IMPORT -static JSParseNode * -ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - JSTokenType tt; - - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME); - pn = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn) - return NULL; - pn->pn_op = JSOP_NAME; - pn->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - - ts->flags |= TSF_OPERAND; - while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) { - ts->flags &= ~TSF_OPERAND; - if (pn->pn_op == JSOP_IMPORTALL) - goto bad_import; - - if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - ts->flags |= TSF_KEYWORD_IS_NAME; - if (js_MatchToken(cx, ts, TOK_STAR)) { - pn2->pn_op = JSOP_IMPORTALL; - pn2->pn_atom = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - } else { - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - pn2->pn_op = JSOP_GETPROP; - pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - } - ts->flags &= ~TSF_KEYWORD_IS_NAME; - pn2->pn_expr = pn; - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - } else { - /* Make a TOK_LB binary node. */ - pn2 = NewBinary(cx, tt, JSOP_GETELEM, pn, Expr(cx, ts, tc), tc); - if (!pn2) - return NULL; - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); - } - - pn = pn2; - ts->flags |= TSF_OPERAND; - } - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - js_UngetToken(ts); - - switch (pn->pn_op) { - case JSOP_GETPROP: - pn->pn_op = JSOP_IMPORTPROP; - break; - case JSOP_GETELEM: - pn->pn_op = JSOP_IMPORTELEM; - break; - case JSOP_IMPORTALL: - break; - default: - goto bad_import; - } - return pn; - - bad_import: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_IMPORT); - return NULL; -} -#endif /* JS_HAS_EXPORT_IMPORT */ - -static JSBool -BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSObject *blockObj; - JSScopeProperty *sprop; - JSAtomListElement *ale; - - blockObj = data->obj; - sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { - const char *name; - - if (sprop) { - JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID); - JS_ASSERT((uint16)sprop->shortid < data->u.let.index); - } - - name = js_AtomToPrintableString(cx, atom); - if (name) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_VAR, - (ale && ALE_JSOP(ale) == JSOP_DEFCONST) - ? js_const_str - : "variable", - name); - } - return JS_FALSE; - } - - if (data->u.let.index == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, JSREPORT_ERROR), - data->u.let.overflow); - return JS_FALSE; - } - - /* Use JSPROP_ENUMERATE to aid the disassembler. */ - return js_DefineNativeProperty(cx, blockObj, ATOM_TO_JSID(atom), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE | JSPROP_PERMANENT, - SPROP_HAS_SHORTID, - (intN)data->u.let.index++, - NULL); -} - -static JSBool -BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) -{ - JSStmtInfo *stmt; - JSAtomListElement *ale; - JSOp op, prevop; - const char *name; - JSFunction *fun; - JSObject *obj, *pobj; - JSProperty *prop; - JSBool ok; - JSPropertyOp getter, setter; - JSScopeProperty *sprop; - - stmt = js_LexicalLookup(tc, atom, NULL, JS_FALSE); - ATOM_LIST_SEARCH(ale, &tc->decls, atom); - op = data->op; - if ((stmt && stmt->type != STMT_WITH) || ale) { - prevop = ale ? ALE_JSOP(ale) : JSOP_DEFVAR; - if (JS_HAS_STRICT_OPTION(cx) - ? op != JSOP_DEFVAR || prevop != JSOP_DEFVAR - : op == JSOP_DEFCONST || prevop == JSOP_DEFCONST) { - name = js_AtomToPrintableString(cx, atom); - if (!name || - !js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - (op != JSOP_DEFCONST && - prevop != JSOP_DEFCONST) - ? JSREPORT_WARNING | - JSREPORT_STRICT - : JSREPORT_ERROR), - JSMSG_REDECLARED_VAR, - (prevop == JSOP_DEFFUN || - prevop == JSOP_CLOSURE) - ? js_function_str - : (prevop == JSOP_DEFCONST) - ? js_const_str - : js_var_str, - name)) { - return JS_FALSE; - } - } - if (op == JSOP_DEFVAR && prevop == JSOP_CLOSURE) - tc->flags |= TCF_FUN_CLOSURE_VS_VAR; - } - if (!ale) { - ale = js_IndexAtom(cx, atom, &tc->decls); - if (!ale) - return JS_FALSE; - } - ALE_SET_JSOP(ale, op); - - fun = data->u.var.fun; - obj = data->obj; - if (!fun) { - /* Don't lookup global variables at compile time. */ - prop = NULL; - } else { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), - &pobj, &prop)) { - return JS_FALSE; - } - } - - ok = JS_TRUE; - getter = data->u.var.getter; - setter = data->u.var.setter; - - if (prop && pobj == obj && OBJ_IS_NATIVE(pobj)) { - sprop = (JSScopeProperty *)prop; - if (sprop->getter == js_GetArgument) { - name = js_AtomToPrintableString(cx, atom); - if (!name) { - ok = JS_FALSE; - } else if (op == JSOP_DEFCONST) { - js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_ERROR), - JSMSG_REDECLARED_PARAM, - name); - ok = JS_FALSE; - } else { - getter = js_GetArgument; - setter = js_SetArgument; - ok = js_ReportCompileErrorNumber(cx, - BIND_DATA_REPORT_ARGS(data, - JSREPORT_WARNING | - JSREPORT_STRICT), - JSMSG_VAR_HIDES_ARG, - name); - } - } else { - JS_ASSERT(getter == js_GetLocalVariable); - - if (fun) { - /* Not an argument, must be a redeclared local var. */ - if (data->u.var.clasp == &js_FunctionClass) { - JS_ASSERT(sprop->getter == js_GetLocalVariable); - JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - (uint16) sprop->shortid < fun->u.i.nvars); - } else if (data->u.var.clasp == &js_CallClass) { - if (sprop->getter == js_GetCallVariable) { - /* - * Referencing a name introduced by a var statement in - * the enclosing function. Check that the slot number - * we have is in range. - */ - JS_ASSERT((sprop->flags & SPROP_HAS_SHORTID) && - (uint16) sprop->shortid < fun->u.i.nvars); - } else { - /* - * A variable introduced through another eval: don't - * use the special getters and setters since we can't - * allocate a slot in the frame. - */ - getter = sprop->getter; - setter = sprop->setter; - } - } - - /* Override the old getter and setter, to handle eval. */ - sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, - 0, sprop->attrs, - getter, setter); - if (!sprop) - ok = JS_FALSE; - } - } - if (prop) - OBJ_DROP_PROPERTY(cx, pobj, prop); - } else { - /* - * Property not found in current variable scope: we have not seen this - * variable before. Define a new local variable by adding a property - * to the function's scope, allocating one slot in the function's vars - * frame. Global variables and any locals declared in with statement - * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes - * generated for slot-less vars. - */ - sprop = NULL; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - prop = NULL; - } - - if (cx->fp->scopeChain == obj && - !js_InWithStatement(tc) && - !BindLocalVariable(cx, data, atom)) { - return JS_FALSE; - } - } - return ok; -} - -#if JS_HAS_DESTRUCTURING - -static JSBool -BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, - JSTreeContext *tc) -{ - JSAtom *atom; - - /* - * Destructuring is a form of assignment, so just as for an initialized - * simple variable, we must check for assignment to 'arguments' and flag - * the enclosing function (if any) as heavyweight. - */ - JS_ASSERT(pn->pn_type == TOK_NAME); - atom = pn->pn_atom; - if (atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - - data->pn = pn; - if (!data->binder(cx, data, atom, tc)) - return JS_FALSE; - data->pn = NULL; - - /* - * Select the appropriate name-setting opcode, which may be specialized - * further for local variable and argument slot optimizations. At this - * point, we can't select the optimal final opcode, yet we must preserve - * the CONST bit and convey "set", not "get". - */ - pn->pn_op = (data->op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - pn->pn_attrs = data->u.var.attrs; - return JS_TRUE; -} - -/* - * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any - * LHS expression except a destructuring initialiser, and R is on the stack. - * Because R is already evaluated, the usual LHS-specialized bytecodes won't - * work. After pushing R[P] we need to evaluate Q's "reference base" QB and - * then push its property name QN. At this point the stack looks like - * - * [... R, R[P], QB, QN] - * - * We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes - * its operands with left-hand side above right-hand side: - * - * [rval, lval, xval] - * - * and pops all three values, setting lval[xval] = rval. But we cannot select - * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var, - * which can be optimized further. So we select JSOP_SETNAME. - */ -static JSBool -BindDestructuringLHS(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - - switch (pn->pn_type) { - case TOK_NAME: - if (pn->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - /* FALL THROUGH */ - case TOK_DOT: - case TOK_LB: - pn->pn_op = JSOP_SETNAME; - break; - -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(pn->pn_op == JSOP_CALL || pn->pn_op == JSOP_EVAL); - pn->pn_op = JSOP_SETCALL; - break; -#endif - -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (pn->pn_op == JSOP_XMLNAME) { - pn->pn_op = JSOP_BINDXMLNAME; - break; - } - /* FALL THROUGH */ -#endif - - default: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_LEFTSIDE_OF_ASS); - return JS_FALSE; - } - - return JS_TRUE; -} - -typedef struct FindPropValData { - uint32 numvars; /* # of destructuring vars in left side */ - uint32 maxstep; /* max # of steps searching right side */ - JSDHashTable table; /* hash table for O(1) right side search */ -} FindPropValData; - -typedef struct FindPropValEntry { - JSDHashEntryHdr hdr; - JSParseNode *pnkey; - JSParseNode *pnval; -} FindPropValEntry; - -#define ASSERT_VALID_PROPERTY_KEY(pnkey) \ - JS_ASSERT((pnkey)->pn_arity == PN_NULLARY && \ - ((pnkey)->pn_type == TOK_NUMBER || \ - (pnkey)->pn_type == TOK_STRING || \ - (pnkey)->pn_type == TOK_NAME)) - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -HashFindPropValKey(JSDHashTable *table, const void *key) -{ - const JSParseNode *pnkey = (const JSParseNode *)key; - - ASSERT_VALID_PROPERTY_KEY(pnkey); - return (pnkey->pn_type == TOK_NUMBER) - ? (JSDHashNumber) (JSDOUBLE_HI32(pnkey->pn_dval) ^ - JSDOUBLE_LO32(pnkey->pn_dval)) - : (JSDHashNumber) pnkey->pn_atom->number; -} - -JS_STATIC_DLL_CALLBACK(JSBool) -MatchFindPropValEntry(JSDHashTable *table, - const JSDHashEntryHdr *entry, - const void *key) -{ - const FindPropValEntry *fpve = (const FindPropValEntry *)entry; - const JSParseNode *pnkey = (const JSParseNode *)key; - - ASSERT_VALID_PROPERTY_KEY(pnkey); - return pnkey->pn_type == fpve->pnkey->pn_type && - ((pnkey->pn_type == TOK_NUMBER) - ? pnkey->pn_dval == fpve->pnkey->pn_dval - : pnkey->pn_atom == fpve->pnkey->pn_atom); -} - -static const JSDHashTableOps FindPropValOps = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - HashFindPropValKey, - MatchFindPropValEntry, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -#define STEP_HASH_THRESHOLD 10 -#define BIG_DESTRUCTURING 5 -#define BIG_OBJECT_INIT 20 - -static JSParseNode * -FindPropertyValue(JSParseNode *pn, JSParseNode *pnid, FindPropValData *data) -{ - FindPropValEntry *entry; - JSParseNode *pnhit, *pnprop, *pnkey; - uint32 step; - - /* If we have a hash table, use it as the sole source of truth. */ - if (data->table.ops) { - entry = (FindPropValEntry *) - JS_DHashTableOperate(&data->table, pnid, JS_DHASH_LOOKUP); - return JS_DHASH_ENTRY_IS_BUSY(&entry->hdr) ? entry->pnval : NULL; - } - - /* If pn is not an object initialiser node, we can't do anything here. */ - if (pn->pn_type != TOK_RC) - return NULL; - - /* - * We must search all the way through pn's list, to handle the case of an - * id duplicated for two or more property initialisers. - */ - pnhit = NULL; - step = 0; - ASSERT_VALID_PROPERTY_KEY(pnid); - if (pnid->pn_type == TOK_NUMBER) { - for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { - JS_ASSERT(pnprop->pn_type == TOK_COLON); - if (pnprop->pn_op == JSOP_NOP) { - pnkey = pnprop->pn_left; - ASSERT_VALID_PROPERTY_KEY(pnkey); - if (pnkey->pn_type == TOK_NUMBER && - pnkey->pn_dval == pnid->pn_dval) { - pnhit = pnprop; - } - ++step; - } - } - } else { - for (pnprop = pn->pn_head; pnprop; pnprop = pnprop->pn_next) { - JS_ASSERT(pnprop->pn_type == TOK_COLON); - if (pnprop->pn_op == JSOP_NOP) { - pnkey = pnprop->pn_left; - ASSERT_VALID_PROPERTY_KEY(pnkey); - if (pnkey->pn_type == pnid->pn_type && - pnkey->pn_atom == pnid->pn_atom) { - pnhit = pnprop; - } - ++step; - } - } - } - if (!pnhit) - return NULL; - - /* Hit via full search -- see whether it's time to create the hash table. */ - JS_ASSERT(!data->table.ops); - if (step > data->maxstep) { - data->maxstep = step; - if (step >= STEP_HASH_THRESHOLD && - data->numvars >= BIG_DESTRUCTURING && - pn->pn_count >= BIG_OBJECT_INIT && - JS_DHashTableInit(&data->table, &FindPropValOps, pn, - sizeof(FindPropValEntry), pn->pn_count)) { - - for (pn = pn->pn_head; pn; pn = pn->pn_next) { - ASSERT_VALID_PROPERTY_KEY(pn->pn_left); - entry = (FindPropValEntry *) - JS_DHashTableOperate(&data->table, pn->pn_left, - JS_DHASH_ADD); - entry->pnval = pn->pn_right; - } - } - } - return pnhit->pn_right; -} - -/* - * If data is null, the caller is AssignExpr and instead of binding variables, - * we specialize lvalues in the propery value positions of the left-hand side. - * If right is null, just check for well-formed lvalues. - */ -static JSBool -CheckDestructuring(JSContext *cx, BindData *data, - JSParseNode *left, JSParseNode *right, - JSTreeContext *tc) -{ - JSBool ok; - FindPropValData fpvd; - JSParseNode *lhs, *rhs, *pn, *pn2; - - if (left->pn_type == TOK_ARRAYCOMP) { - js_ReportCompileErrorNumber(cx, left, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_ARRAY_COMP_LEFTSIDE); - return JS_FALSE; - } - - ok = JS_TRUE; - fpvd.table.ops = NULL; - lhs = left->pn_head; - if (lhs && lhs->pn_type == TOK_DEFSHARP) { - pn = lhs; - goto no_var_name; - } - - if (left->pn_type == TOK_RB) { - rhs = (right && right->pn_type == left->pn_type) - ? right->pn_head - : NULL; - - while (lhs) { - pn = lhs, pn2 = rhs; - if (!data) { - /* Skip parenthesization if not in a variable declaration. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - if (pn2) { - while (pn2->pn_type == TOK_RP) - pn2 = pn2->pn_kid; - } - } - - /* Nullary comma is an elision; binary comma is an expression.*/ - if (pn->pn_type != TOK_COMMA || pn->pn_arity != PN_NULLARY) { - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - ok = CheckDestructuring(cx, data, pn, pn2, tc); - } else { - if (data) { - if (pn->pn_type != TOK_NAME) - goto no_var_name; - - ok = BindDestructuringVar(cx, data, pn, tc); - } else { - ok = BindDestructuringLHS(cx, pn, tc); - } - } - if (!ok) - goto out; - } - - lhs = lhs->pn_next; - if (rhs) - rhs = rhs->pn_next; - } - } else { - JS_ASSERT(left->pn_type == TOK_RC); - fpvd.numvars = left->pn_count; - fpvd.maxstep = 0; - rhs = NULL; - - while (lhs) { - JS_ASSERT(lhs->pn_type == TOK_COLON); - pn = lhs->pn_right; - if (!data) { - /* Skip parenthesization if not in a variable declaration. */ - while (pn->pn_type == TOK_RP) - pn = pn->pn_kid; - } - - if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) { - if (right) { - rhs = FindPropertyValue(right, lhs->pn_left, &fpvd); - if (rhs && !data) { - while (rhs->pn_type == TOK_RP) - rhs = rhs->pn_kid; - } - } - - ok = CheckDestructuring(cx, data, pn, rhs, tc); - } else if (data) { - if (pn->pn_type != TOK_NAME) - goto no_var_name; - - ok = BindDestructuringVar(cx, data, pn, tc); - } else { - ok = BindDestructuringLHS(cx, pn, tc); - } - if (!ok) - goto out; - - lhs = lhs->pn_next; - } - } - -out: - if (fpvd.table.ops) - JS_DHashTableFinish(&fpvd.table); - return ok; - -no_var_name: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - ok = JS_FALSE; - goto out; -} - -static JSParseNode * -DestructuringExpr(JSContext *cx, BindData *data, JSTreeContext *tc, - JSTokenType tt) -{ - JSParseNode *pn; - - pn = PrimaryExpr(cx, data->ts, tc, tt, JS_FALSE); - if (!pn) - return NULL; - if (!CheckDestructuring(cx, data, pn, NULL, tc)) - return NULL; - return pn; -} - -#endif /* JS_HAS_DESTRUCTURING */ - -extern const char js_with_statement_str[]; - -static JSParseNode * -ContainsStmt(JSParseNode *pn, JSTokenType tt) -{ - JSParseNode *pn2, *pnt; - - if (!pn) - return NULL; - if (pn->pn_type == tt) - return pn; - switch (pn->pn_arity) { - case PN_LIST: - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - pnt = ContainsStmt(pn2, tt); - if (pnt) - return pnt; - } - break; - case PN_TERNARY: - pnt = ContainsStmt(pn->pn_kid1, tt); - if (pnt) - return pnt; - pnt = ContainsStmt(pn->pn_kid2, tt); - if (pnt) - return pnt; - return ContainsStmt(pn->pn_kid3, tt); - case PN_BINARY: - /* - * Limit recursion if pn is a binary expression, which can't contain a - * var statement. - */ - if (pn->pn_op != JSOP_NOP) - return NULL; - pnt = ContainsStmt(pn->pn_left, tt); - if (pnt) - return pnt; - return ContainsStmt(pn->pn_right, tt); - case PN_UNARY: - if (pn->pn_op != JSOP_NOP) - return NULL; - return ContainsStmt(pn->pn_kid, tt); - case PN_NAME: - return ContainsStmt(pn->pn_expr, tt); - default:; - } - return NULL; -} - -static JSParseNode * -ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParser operandParser) -{ - JSTokenType tt, tt2; - JSParseNode *pn, *pn2; - - tt = CURRENT_TOKEN(ts).type; - if (!(tc->flags & TCF_IN_FUNCTION)) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_RETURN_OR_YIELD, -#if JS_HAS_GENERATORS - (tt == TOK_YIELD) ? js_yield_str : -#endif - js_return_str); - return NULL; - } - - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - -#if JS_HAS_GENERATORS - if (tt == TOK_YIELD) - tc->flags |= TCF_FUN_IS_GENERATOR; -#endif - - /* This is ugly, but we don't want to require a semicolon. */ - ts->flags |= TSF_OPERAND; - tt2 = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt2 == TOK_ERROR) - return NULL; - - if (tt2 != TOK_EOF && tt2 != TOK_EOL && tt2 != TOK_SEMI && tt2 != TOK_RC -#if JS_HAS_GENERATORS - && (tt != TOK_YIELD || (tt2 != tt && tt2 != TOK_RB && tt2 != TOK_RP)) -#endif - ) { - pn2 = operandParser(cx, ts, tc); - if (!pn2) - return NULL; -#if JS_HAS_GENERATORS - if (tt == TOK_RETURN) -#endif - tc->flags |= TCF_RETURN_EXPR; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - } else { -#if JS_HAS_GENERATORS - if (tt == TOK_RETURN) -#endif - tc->flags |= TCF_RETURN_VOID; - pn->pn_kid = NULL; - } - - if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { - /* As in Python (see PEP-255), disallow return v; in generators. */ - ReportBadReturn(cx, ts, JSREPORT_ERROR, - JSMSG_BAD_GENERATOR_RETURN, - JSMSG_BAD_ANON_GENERATOR_RETURN); - return NULL; - } - - if (JS_HAS_STRICT_OPTION(cx) && - (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && - !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, - JSMSG_NO_RETURN_VALUE, - JSMSG_ANON_NO_RETURN_VALUE)) { - return NULL; - } - - return pn; -} - -static JSParseNode * -PushLexicalScope(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSStmtInfo *stmtInfo) -{ - JSParseNode *pn; - JSObject *obj; - JSAtom *atom; - - pn = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn) - return NULL; - - obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return NULL; - - js_PushBlockScope(tc, stmtInfo, atom, -1); - pn->pn_type = TOK_LEXICALSCOPE; - pn->pn_op = JSOP_LEAVEBLOCK; - pn->pn_atom = atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - return pn; -} - -#if JS_HAS_BLOCK_SCOPE - -static JSParseNode * -LetBlock(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool statement) -{ - JSParseNode *pn, *pnblock, *pnlet; - JSStmtInfo stmtInfo; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LET); - - /* Create the let binary node. */ - pnlet = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pnlet) - return NULL; - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_LET); - - /* This is a let block or expression of the form: let (a, b, c) .... */ - pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pnblock) - return NULL; - pn = pnblock; - pn->pn_expr = pnlet; - - pnlet->pn_left = Variables(cx, ts, tc); - if (!pnlet->pn_left) - return NULL; - pnlet->pn_left->pn_extra = PNX_POPVAR; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET); - - ts->flags |= TSF_OPERAND; - if (statement && !js_MatchToken(cx, ts, TOK_LC)) { - /* - * If this is really an expression in let statement guise, then we - * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop - * the return value of the expression. - */ - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_num = -1; - pn->pn_kid = pnblock; - - statement = JS_FALSE; - } - ts->flags &= ~TSF_OPERAND; - - if (statement) { - pnlet->pn_right = Statements(cx, ts, tc); - if (!pnlet->pn_right) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET); - } else { - /* - * Change pnblock's opcode to the variant that propagates the last - * result down after popping the block, and clear statement. - */ - pnblock->pn_op = JSOP_LEAVEBLOCKEXPR; - pnlet->pn_right = Expr(cx, ts, tc); - if (!pnlet->pn_right) - return NULL; - } - - js_PopStatement(tc); - return pn; -} - -#endif /* JS_HAS_BLOCK_SCOPE */ - -static JSParseNode * -Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSTokenType tt; - JSParseNode *pn, *pn1, *pn2, *pn3, *pn4; - JSStmtInfo stmtInfo, *stmt, *stmt2; - JSAtom *label; - - CHECK_RECURSION(); - - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); - if (tt == TOK_ERROR) - return NULL; - } -#endif - - switch (tt) { -#if JS_HAS_EXPORT_IMPORT - case TOK_EXPORT: - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - PN_INIT_LIST(pn); - if (js_MatchToken(cx, ts, TOK_STAR)) { - pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - } else { - do { - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_EXPORT_NAME); - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - pn2->pn_op = JSOP_NAME; - pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn2->pn_expr = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - PN_APPEND(pn, pn2); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - } - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - - case TOK_IMPORT: - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - PN_INIT_LIST(pn); - do { - pn2 = ImportExpr(cx, ts, tc); - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; -#endif /* JS_HAS_EXPORT_IMPORT */ - - case TOK_FUNCTION: -#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_DBLCOLON) - goto expression; -#endif - return FunctionStmt(cx, ts, tc); - - case TOK_IF: - /* An IF node has three kids: condition, then, and optional else. */ - pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; - pn1 = Condition(cx, ts, tc); - if (!pn1) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_IF, -1); - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - ts->flags |= TSF_OPERAND; - if (js_MatchToken(cx, ts, TOK_ELSE)) { - ts->flags &= ~TSF_OPERAND; - stmtInfo.type = STMT_ELSE; - pn3 = Statement(cx, ts, tc); - if (!pn3) - return NULL; - pn->pn_pos.end = pn3->pn_pos.end; - } else { - ts->flags &= ~TSF_OPERAND; - pn3 = NULL; - pn->pn_pos.end = pn2->pn_pos.end; - } - js_PopStatement(tc); - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; - return pn; - - case TOK_SWITCH: - { - JSParseNode *pn5, *saveBlock; - JSBool seenDefault = JS_FALSE; - - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_SWITCH); - - /* pn1 points to the switch's discriminant. */ - pn1 = Expr(cx, ts, tc); - if (!pn1) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_SWITCH); - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_SWITCH); - - /* pn2 is a list of case nodes. The default case has pn_left == NULL */ - pn2 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn2) - return NULL; - saveBlock = tc->blockNode; - tc->blockNode = pn2; - PN_INIT_LIST(pn2); - - js_PushStatement(tc, &stmtInfo, STMT_SWITCH, -1); - - while ((tt = js_GetToken(cx, ts)) != TOK_RC) { - switch (tt) { - case TOK_DEFAULT: - if (seenDefault) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TOO_MANY_DEFAULTS); - return NULL; - } - seenDefault = JS_TRUE; - /* fall through */ - - case TOK_CASE: - pn3 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn3) - return NULL; - if (tt == TOK_DEFAULT) { - pn3->pn_left = NULL; - } else { - pn3->pn_left = Expr(cx, ts, tc); - if (!pn3->pn_left) - return NULL; - } - PN_APPEND(pn2, pn3); - if (pn2->pn_count == JS_BIT(16)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TOO_MANY_CASES); - return NULL; - } - break; - - case TOK_ERROR: - return NULL; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_SWITCH); - return NULL; - } - MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE); - - pn4 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn4) - return NULL; - pn4->pn_type = TOK_LC; - PN_INIT_LIST(pn4); - ts->flags |= TSF_OPERAND; - while ((tt = js_PeekToken(cx, ts)) != TOK_RC && - tt != TOK_CASE && tt != TOK_DEFAULT) { - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - pn5 = Statement(cx, ts, tc); - if (!pn5) - return NULL; - pn4->pn_pos.end = pn5->pn_pos.end; - PN_APPEND(pn4, pn5); - ts->flags |= TSF_OPERAND; - } - ts->flags &= ~TSF_OPERAND; - - /* Fix the PN_LIST so it doesn't begin at the TOK_COLON. */ - if (pn4->pn_head) - pn4->pn_pos.begin = pn4->pn_head->pn_pos.begin; - pn3->pn_pos.end = pn4->pn_pos.end; - pn3->pn_right = pn4; - } - - /* - * Handle the case where there was a let declaration in any case in - * the switch body, but not within an inner block. If it replaced - * tc->blockNode with a new block node then we must refresh pn2 and - * then restore tc->blockNode. - */ - if (tc->blockNode != pn2) - pn2 = tc->blockNode; - tc->blockNode = saveBlock; - js_PopStatement(tc); - - pn->pn_pos.end = pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - pn->pn_left = pn1; - pn->pn_right = pn2; - return pn; - } - - case TOK_WHILE: - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_WHILE_LOOP, -1); - pn2 = Condition(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_left = pn2; - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - js_PopStatement(tc); - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_right = pn2; - return pn; - - case TOK_DO: - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_DO_LOOP, -1); - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_left = pn2; - MUST_MATCH_TOKEN(TOK_WHILE, JSMSG_WHILE_AFTER_DO); - pn2 = Condition(cx, ts, tc); - if (!pn2) - return NULL; - js_PopStatement(tc); - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_right = pn2; - if ((cx->version & JSVERSION_MASK) != JSVERSION_ECMA_3) { - /* - * All legacy and extended versions must do automatic semicolon - * insertion after do-while. See the testcase and discussion in - * http://bugzilla.mozilla.org/show_bug.cgi?id=238945. - */ - (void) js_MatchToken(cx, ts, TOK_SEMI); - return pn; - } - break; - - case TOK_FOR: - { -#if JS_HAS_BLOCK_SCOPE - JSParseNode *pnlet; - JSStmtInfo blockInfo; - - pnlet = NULL; -#endif - - /* A FOR node is binary, left is loop control and right is the body. */ - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1); - - pn->pn_op = JSOP_FORIN; - if (js_MatchToken(cx, ts, TOK_NAME)) { - if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom) - pn->pn_op = JSOP_FOREACH; - else - js_UngetToken(ts); - } - - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_SEMI) { - if (pn->pn_op == JSOP_FOREACH) - goto bad_for_each; - - /* No initializer -- set first kid of left sub-node to null. */ - pn1 = NULL; - } else { - /* - * Set pn1 to a var list or an initializing expression. - * - * Set the TCF_IN_FOR_INIT flag during parsing of the first clause - * of the for statement. This flag will be used by the RelExpr - * production; if it is set, then the 'in' keyword will not be - * recognized as an operator, leaving it available to be parsed as - * part of a for/in loop. - * - * A side effect of this restriction is that (unparenthesized) - * expressions involving an 'in' operator are illegal in the init - * clause of an ordinary for loop. - */ - tc->flags |= TCF_IN_FOR_INIT; - if (tt == TOK_VAR) { - (void) js_GetToken(cx, ts); - pn1 = Variables(cx, ts, tc); -#if JS_HAS_BLOCK_SCOPE - } else if (tt == TOK_LET) { - (void) js_GetToken(cx, ts); - if (js_PeekToken(cx, ts) == TOK_LP) { - pn1 = LetBlock(cx, ts, tc, JS_FALSE); - tt = TOK_LEXICALSCOPE; - } else { - pnlet = PushLexicalScope(cx, ts, tc, &blockInfo); - if (!pnlet) - return NULL; - pn1 = Variables(cx, ts, tc); - } -#endif - } else { - pn1 = Expr(cx, ts, tc); - if (pn1) { - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - } - } - tc->flags &= ~TCF_IN_FOR_INIT; - if (!pn1) - return NULL; - } - - /* - * We can be sure that it's a for/in loop if there's still an 'in' - * keyword here, even if JavaScript recognizes 'in' as an operator, - * as we've excluded 'in' from being parsed in RelExpr by setting - * the TCF_IN_FOR_INIT flag in our JSTreeContext. - */ - if (pn1 && js_MatchToken(cx, ts, TOK_IN)) { - stmtInfo.type = STMT_FOR_IN_LOOP; - - /* Check that the left side of the 'in' is valid. */ - JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt); - if (TOKEN_TYPE_IS_DECL(tt) - ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST -#if JS_HAS_DESTRUCTURING - || (pn->pn_op == JSOP_FORIN && - (pn1->pn_head->pn_type == TOK_RC || - (pn1->pn_head->pn_type == TOK_RB && - pn1->pn_head->pn_count != 2) || - (pn1->pn_head->pn_type == TOK_ASSIGN && - (pn1->pn_head->pn_left->pn_type != TOK_RB || - pn1->pn_head->pn_left->pn_count != 2)))) -#endif - ) - : (pn1->pn_type != TOK_NAME && - pn1->pn_type != TOK_DOT && -#if JS_HAS_DESTRUCTURING - ((pn->pn_op == JSOP_FORIN) - ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2) - : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) && -#endif -#if JS_HAS_LVALUE_RETURN - pn1->pn_type != TOK_LP && -#endif -#if JS_HAS_XML_SUPPORT - (pn1->pn_type != TOK_UNARYOP || - pn1->pn_op != JSOP_XMLNAME) && -#endif - pn1->pn_type != TOK_LB)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return NULL; - } - - if (TOKEN_TYPE_IS_DECL(tt)) { - /* Tell js_EmitTree(TOK_VAR) that pn1 is part of a for/in. */ - pn1->pn_extra |= PNX_FORINVAR; - - /* - * Generate a final POP only if the variable is a simple name - * (which means it is not a destructuring left-hand side) and - * it has an initializer. - */ - pn2 = pn1->pn_head; - if (pn2->pn_type == TOK_NAME && pn2->pn_expr) - pn1->pn_extra |= PNX_POPVAR; - } else { - pn2 = pn1; -#if JS_HAS_LVALUE_RETURN - if (pn2->pn_type == TOK_LP) - pn2->pn_op = JSOP_SETCALL; -#endif -#if JS_HAS_XML_SUPPORT - if (pn2->pn_type == TOK_UNARYOP) - pn2->pn_op = JSOP_BINDXMLNAME; -#endif - } - - switch (pn2->pn_type) { - case TOK_NAME: - /* Beware 'for (arguments in ...)' with or without a 'var'. */ - if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - -#if JS_HAS_DESTRUCTURING - case TOK_ASSIGN: - pn2 = pn2->pn_left; - JS_ASSERT(pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC); - /* FALL THROUGH */ - case TOK_RB: - case TOK_RC: - /* Check for valid lvalues in var-less destructuring for-in. */ - if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc)) - return NULL; - - /* Destructuring for-in requires [key, value] enumeration. */ - if (pn->pn_op != JSOP_FOREACH) - pn->pn_op = JSOP_FOREACHKEYVAL; - break; -#endif - - default:; - } - - /* Parse the object expression as the right operand of 'in'. */ - pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc); - if (!pn2) - return NULL; - pn->pn_left = pn2; - } else { - if (pn->pn_op == JSOP_FOREACH) - goto bad_for_each; - pn->pn_op = JSOP_NOP; - - /* Parse the loop condition or null into pn2. */ - MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_SEMI) { - pn2 = NULL; - } else { - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - } - - /* Parse the update expression or null into pn3. */ - MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND); - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_RP) { - pn3 = NULL; - } else { - pn3 = Expr(cx, ts, tc); - if (!pn3) - return NULL; - } - - /* Build the RESERVED node to use as the left kid of pn. */ - pn4 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn4) - return NULL; - pn4->pn_type = TOK_RESERVED; - pn4->pn_op = JSOP_NOP; - pn4->pn_kid1 = pn1; - pn4->pn_kid2 = pn2; - pn4->pn_kid3 = pn3; - pn->pn_left = pn4; - } - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); - - /* Parse the loop body into pn->pn_right. */ - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_right = pn2; - - /* Record the absolute line number for source note emission. */ - pn->pn_pos.end = pn2->pn_pos.end; - -#if JS_HAS_BLOCK_SCOPE - if (pnlet) { - js_PopStatement(tc); - pnlet->pn_expr = pn; - pn = pnlet; - } -#endif - js_PopStatement(tc); - return pn; - - bad_for_each: - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_FOR_EACH_LOOP); - return NULL; - } - - case TOK_TRY: { - JSParseNode *catchList, *lastCatch; - - /* - * try nodes are ternary. - * kid1 is the try Statement - * kid2 is the catch node list or null - * kid3 is the finally Statement - * - * catch nodes are ternary. - * kid1 is the lvalue (TOK_NAME, TOK_LB, or TOK_LC) - * kid2 is the catch guard or null if no guard - * kid3 is the catch block - * - * catch lvalue nodes are either: - * TOK_NAME for a single identifier - * TOK_RB or TOK_RC for a destructuring left-hand side - * - * finally nodes are TOK_LC Statement lists. - */ - pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; - pn->pn_op = JSOP_NOP; - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY); - js_PushStatement(tc, &stmtInfo, STMT_TRY, -1); - pn->pn_kid1 = Statements(cx, ts, tc); - if (!pn->pn_kid1) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY); - js_PopStatement(tc); - - catchList = NULL; - tt = js_GetToken(cx, ts); - if (tt == TOK_CATCH) { - catchList = NewParseNode(cx, ts, PN_LIST, tc); - if (!catchList) - return NULL; - catchList->pn_type = TOK_RESERVED; - PN_INIT_LIST(catchList); - lastCatch = NULL; - - do { - JSParseNode *pnblock; - BindData data; - - /* Check for another catch after unconditional catch. */ - if (lastCatch && !lastCatch->pn_kid2) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_AFTER_GENERAL); - return NULL; - } - - /* - * Create a lexical scope node around the whole catch clause, - * including the head. - */ - pnblock = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pnblock) - return NULL; - stmtInfo.type = STMT_CATCH; - - /* - * Legal catch forms are: - * catch (lhs) - * catch (lhs if ) - * where lhs is a name or a destructuring left-hand side. - * (the latter is legal only #ifdef JS_HAS_CATCH_GUARD) - */ - pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn2) - return NULL; - pnblock->pn_expr = pn2; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH); - - /* - * Contrary to ECMA Ed. 3, the catch variable is lexically - * scoped, not a property of a new Object instance. This is - * an intentional change that anticipates ECMA Ed. 4. - */ - data.pn = NULL; - data.ts = ts; - data.obj = tc->blockChain; - data.op = JSOP_NOP; - data.binder = BindLet; - data.u.let.index = 0; - data.u.let.overflow = JSMSG_TOO_MANY_CATCH_VARS; - - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - pn3 = DestructuringExpr(cx, &data, tc, tt); - if (!pn3) - return NULL; - break; -#endif - - case TOK_NAME: - label = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, label, tc)) - return NULL; - - pn3 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn3) - return NULL; - pn3->pn_atom = label; - pn3->pn_expr = NULL; - pn3->pn_slot = 0; - pn3->pn_attrs = 0; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_IDENTIFIER); - return NULL; - } - - pn2->pn_kid1 = pn3; - pn2->pn_kid2 = NULL; -#if JS_HAS_CATCH_GUARD - /* - * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)') - * to avoid conflicting with the JS2/ECMAv4 type annotation - * catchguard syntax. - */ - if (js_MatchToken(cx, ts, TOK_IF)) { - pn2->pn_kid2 = Expr(cx, ts, tc); - if (!pn2->pn_kid2) - return NULL; - } -#endif - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH); - - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH); - pn2->pn_kid3 = Statements(cx, ts, tc); - if (!pn2->pn_kid3) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH); - js_PopStatement(tc); - - PN_APPEND(catchList, pnblock); - lastCatch = pn2; - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - } while (tt == TOK_CATCH); - } - pn->pn_kid2 = catchList; - - if (tt == TOK_FINALLY) { - tc->tryCount++; - MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY); - js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1); - pn->pn_kid3 = Statements(cx, ts, tc); - if (!pn->pn_kid3) - return NULL; - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_FINALLY); - js_PopStatement(tc); - } else { - js_UngetToken(ts); - pn->pn_kid3 = NULL; - } - if (!catchList && !pn->pn_kid3) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_OR_FINALLY); - return NULL; - } - tc->tryCount++; - return pn; - } - - case TOK_THROW: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - - /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ - ts->flags |= TSF_OPERAND; - tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - if (tt == TOK_EOF || tt == TOK_EOL || tt == TOK_SEMI || tt == TOK_RC) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_op = JSOP_THROW; - pn->pn_kid = pn2; - break; - - /* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */ - case TOK_CATCH: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CATCH_WITHOUT_TRY); - return NULL; - - case TOK_FINALLY: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_FINALLY_WITHOUT_TRY); - return NULL; - - case TOK_BREAK: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - if (!MatchLabel(cx, ts, pn)) - return NULL; - stmt = tc->topStmt; - label = pn->pn_atom; - if (label) { - for (; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_LABEL_NOT_FOUND); - return NULL; - } - if (stmt->type == STMT_LABEL && stmt->atom == label) - break; - } - } else { - for (; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TOUGH_BREAK); - return NULL; - } - if (STMT_IS_LOOP(stmt) || stmt->type == STMT_SWITCH) - break; - } - } - if (label) - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - break; - - case TOK_CONTINUE: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - if (!MatchLabel(cx, ts, pn)) - return NULL; - stmt = tc->topStmt; - label = pn->pn_atom; - if (label) { - for (stmt2 = NULL; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_LABEL_NOT_FOUND); - return NULL; - } - if (stmt->type == STMT_LABEL) { - if (stmt->atom == label) { - if (!stmt2 || !STMT_IS_LOOP(stmt2)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_CONTINUE); - return NULL; - } - break; - } - } else { - stmt2 = stmt; - } - } - } else { - for (; ; stmt = stmt->down) { - if (!stmt) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_CONTINUE); - return NULL; - } - if (STMT_IS_LOOP(stmt)) - break; - } - } - if (label) - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - break; - - case TOK_WITH: - pn = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn) - return NULL; - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH); - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_WITH); - pn->pn_left = pn2; - - js_PushStatement(tc, &stmtInfo, STMT_WITH, -1); - pn2 = Statement(cx, ts, tc); - if (!pn2) - return NULL; - js_PopStatement(tc); - - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_right = pn2; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - return pn; - - case TOK_VAR: - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - - /* Tell js_EmitTree to generate a final POP. */ - pn->pn_extra |= PNX_POPVAR; - break; - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - { - JSStmtInfo **sip; - JSObject *obj; - JSAtom *atom; - - /* Check for a let statement or let expression. */ - if (js_PeekToken(cx, ts) == TOK_LP) { - pn = LetBlock(cx, ts, tc, JS_TRUE); - if (!pn || pn->pn_op == JSOP_LEAVEBLOCK) - return pn; - - /* Let expressions require automatic semicolon insertion. */ - JS_ASSERT(pn->pn_type == TOK_SEMI || - pn->pn_op == JSOP_LEAVEBLOCKEXPR); - break; - } - - /* - * This is a let declaration. We must convert the nearest JSStmtInfo - * that is a block or a switch body to be our scope statement. Further - * let declarations in this block will find this scope statement and - * use the same block object. If we are the first let declaration in - * this block (i.e., when the nearest maybe-scope JSStmtInfo isn't a - * scope statement) then we also need to set tc->blockNode to be our - * TOK_LEXICALSCOPE. - */ - sip = &tc->topScopeStmt; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (STMT_MAYBE_SCOPE(stmt)) - break; - if (stmt == *sip) - sip = &stmt->downScope; - } - - if (stmt && (stmt->flags & SIF_SCOPE)) { - JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(stmt->atom)); - obj = tc->blockChain; - } else { - if (!stmt) { - /* - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346749 - * - * This is a hard case that requires more work. In particular, - * in many cases, we're trying to emit code as we go. However, - * this means that we haven't necessarily finished processing - * all let declarations in the implicit top-level block when - * we emit a reference to one of them. For now, punt on this - * and pretend this is a var declaration. - */ - CURRENT_TOKEN(ts).type = TOK_VAR; - CURRENT_TOKEN(ts).t_op = JSOP_DEFVAR; - - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - pn->pn_extra |= PNX_POPVAR; - break; - } - - /* Convert the block statement into a scope statement. */ - obj = js_NewBlockObject(cx); - if (!obj) - return NULL; - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return NULL; - - /* - * Insert stmt on the tc->topScopeStmt/stmtInfo.downScope linked - * list stack, if it isn't already there. If it is there, but it - * lacks the SIF_SCOPE flag, it must be a try, catch, or finally - * block. - */ - JS_ASSERT(!(stmt->flags & SIF_SCOPE)); - stmt->flags |= SIF_SCOPE; - if (stmt != *sip) { - JS_ASSERT(!stmt->downScope); - JS_ASSERT(stmt->type == STMT_BLOCK || - stmt->type == STMT_SWITCH || - stmt->type == STMT_TRY || - stmt->type == STMT_FINALLY); - stmt->downScope = *sip; - *sip = stmt; - } else { - JS_ASSERT(stmt->type == STMT_CATCH); - JS_ASSERT(stmt->downScope); - } - - obj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain); - tc->blockChain = obj; - stmt->atom = atom; - -#ifdef DEBUG - pn1 = tc->blockNode; - JS_ASSERT(!pn1 || pn1->pn_type != TOK_LEXICALSCOPE); -#endif - - /* Create a new lexical scope node for these statements. */ - pn1 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn1) - return NULL; - - pn1->pn_type = TOK_LEXICALSCOPE; - pn1->pn_op = JSOP_LEAVEBLOCK; - pn1->pn_pos = tc->blockNode->pn_pos; - pn1->pn_atom = atom; - pn1->pn_expr = tc->blockNode; - pn1->pn_slot = -1; - pn1->pn_attrs = 0; - tc->blockNode = pn1; - } - - pn = Variables(cx, ts, tc); - if (!pn) - return NULL; - pn->pn_extra = PNX_POPVAR; - break; - } -#endif /* JS_HAS_BLOCK_SCOPE */ - - case TOK_RETURN: - pn = ReturnOrYield(cx, ts, tc, Expr); - if (!pn) - return NULL; - break; - - case TOK_LC: - { - uintN oldflags; - - oldflags = tc->flags; - tc->flags = oldflags & ~TCF_HAS_FUNCTION_STMT; - js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); - pn = Statements(cx, ts, tc); - if (!pn) - return NULL; - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_COMPOUND); - js_PopStatement(tc); - - /* - * If we contain a function statement and our container is top-level - * or another block, flag pn to preserve braces when decompiling. - */ - if ((tc->flags & TCF_HAS_FUNCTION_STMT) && - (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)) { - pn->pn_extra |= PNX_NEEDBRACES; - } - tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_RETURN_FLAGS)); - return pn; - } - - case TOK_EOL: - case TOK_SEMI: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_kid = NULL; - return pn; - -#if JS_HAS_DEBUGGER_KEYWORD - case TOK_DEBUGGER: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_DEBUGGER; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; -#endif /* JS_HAS_DEBUGGER_KEYWORD */ - -#if JS_HAS_XML_SUPPORT - case TOK_DEFAULT: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - if (!js_MatchToken(cx, ts, TOK_NAME) || - CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.xmlAtom || - !js_MatchToken(cx, ts, TOK_NAME) || - CURRENT_TOKEN(ts).t_atom != cx->runtime->atomState.namespaceAtom || - !js_MatchToken(cx, ts, TOK_ASSIGN) || - CURRENT_TOKEN(ts).t_op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_DEFAULT_XML_NAMESPACE); - return NULL; - } - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_op = JSOP_DEFXMLNS; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - tc->flags |= TCF_HAS_DEFXMLNS; - break; -#endif - - case TOK_ERROR: - return NULL; - - default: -#if JS_HAS_XML_SUPPORT - expression: -#endif - js_UngetToken(ts); - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - - if (js_PeekToken(cx, ts) == TOK_COLON) { - if (pn2->pn_type != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_LABEL); - return NULL; - } - label = pn2->pn_atom; - for (stmt = tc->topStmt; stmt; stmt = stmt->down) { - if (stmt->type == STMT_LABEL && stmt->atom == label) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_DUPLICATE_LABEL); - return NULL; - } - } - (void) js_GetToken(cx, ts); - - /* Push a label struct and parse the statement. */ - js_PushStatement(tc, &stmtInfo, STMT_LABEL, -1); - stmtInfo.atom = label; - pn = Statement(cx, ts, tc); - if (!pn) - return NULL; - - /* Normalize empty statement to empty block for the decompiler. */ - if (pn->pn_type == TOK_SEMI && !pn->pn_kid) { - pn->pn_type = TOK_LC; - pn->pn_arity = PN_LIST; - PN_INIT_LIST(pn); - } - - /* Pop the label, set pn_expr, and return early. */ - js_PopStatement(tc); - pn2->pn_type = TOK_COLON; - pn2->pn_pos.end = pn->pn_pos.end; - pn2->pn_expr = pn; - return pn2; - } - - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_SEMI; - pn->pn_pos = pn2->pn_pos; - pn->pn_kid = pn2; - break; - } - - /* Check termination of this primitive statement. */ - if (ON_CURRENT_LINE(ts, pn->pn_pos)) { - ts->flags |= TSF_OPERAND; - tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_ERROR) - return NULL; - if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SEMI_BEFORE_STMNT); - return NULL; - } - } - - (void) js_MatchToken(cx, ts, TOK_SEMI); - return pn; -} - -static JSParseNode * -Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSTokenType tt; - JSBool let; - JSStmtInfo *scopeStmt; - BindData data; - JSParseNode *pn, *pn2; - JSStackFrame *fp; - JSAtom *atom; - - /* - * The three options here are: - * - TOK_LET: We are parsing a let declaration. - * - TOK_LP: We are parsing the head of a let block. - * - Otherwise, we're parsing var declarations. - */ - tt = CURRENT_TOKEN(ts).type; - let = (tt == TOK_LET || tt == TOK_LP); - JS_ASSERT(let || tt == TOK_VAR); - - /* Make sure that Statement set the tree context up correctly. */ - scopeStmt = tc->topScopeStmt; - if (let) { - while (scopeStmt && !(scopeStmt->flags & SIF_SCOPE)) { - JS_ASSERT(!STMT_MAYBE_SCOPE(scopeStmt)); - scopeStmt = scopeStmt->downScope; - } - JS_ASSERT(scopeStmt); - } - - data.pn = NULL; - data.ts = ts; - data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op; - data.binder = let ? BindLet : BindVarOrConst; - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_op = data.op; - PN_INIT_LIST(pn); - - /* - * The tricky part of this code is to create special parsenode opcodes for - * getting and setting variables (which will be stored as special slots in - * the frame). The most complicated case is an eval() inside a function. - * If the evaluated string references variables in the enclosing function, - * then we need to generate the special variable opcodes. We determine - * this by looking up the variable's id in the current variable object. - * Fortunately, we can avoid doing this for let declared variables. - */ - fp = cx->fp; - if (let) { - JS_ASSERT(tc->blockChain == ATOM_TO_OBJECT(scopeStmt->atom)); - data.obj = tc->blockChain; - data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj); - data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS; - } else { - data.obj = fp->varobj; - data.u.var.fun = fp->fun; - data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj); - if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) { - /* We are compiling code inside a function */ - data.u.var.getter = js_GetLocalVariable; - data.u.var.setter = js_SetLocalVariable; - } else if (data.u.var.fun && data.u.var.clasp == &js_CallClass) { - /* We are compiling code from an eval inside a function */ - data.u.var.getter = js_GetCallVariable; - data.u.var.setter = js_SetCallVariable; - } else { - data.u.var.getter = data.u.var.clasp->getProperty; - data.u.var.setter = data.u.var.clasp->setProperty; - } - - data.u.var.attrs = (data.op == JSOP_DEFCONST) - ? JSPROP_PERMANENT | JSPROP_READONLY - : JSPROP_PERMANENT; - } - - do { - tt = js_GetToken(cx, ts); -#if JS_HAS_DESTRUCTURING - if (tt == TOK_LB || tt == TOK_LC) { - pn2 = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); - if (!pn2) - return NULL; - - if ((tc->flags & TCF_IN_FOR_INIT) && - js_PeekToken(cx, ts) == TOK_IN) { - if (!CheckDestructuring(cx, &data, pn2, NULL, tc)) - return NULL; - PN_APPEND(pn, pn2); - continue; - } - - MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL); - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) - goto bad_var_init; - - pn2 = NewBinary(cx, TOK_ASSIGN, JSOP_NOP, - pn2, AssignExpr(cx, ts, tc), - tc); - if (!pn2 || - !CheckDestructuring(cx, &data, - pn2->pn_left, pn2->pn_right, - tc)) { - return NULL; - } - PN_APPEND(pn, pn2); - continue; - } -#endif - - if (tt != TOK_NAME) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - return NULL; - } - atom = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, atom, tc)) - return NULL; - - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - pn2->pn_op = JSOP_NAME; - pn2->pn_atom = atom; - pn2->pn_expr = NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = let ? 0 : data.u.var.attrs; - PN_APPEND(pn, pn2); - - if (js_MatchToken(cx, ts, TOK_ASSIGN)) { - if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) - goto bad_var_init; - - pn2->pn_expr = AssignExpr(cx, ts, tc); - if (!pn2->pn_expr) - return NULL; - pn2->pn_op = (!let && data.op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - if (!let && atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } - } while (js_MatchToken(cx, ts, TOK_COMMA)); - - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - return pn; - -bad_var_init: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_VAR_INIT); - return NULL; -} - -static JSParseNode * -Expr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - - pn = AssignExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_COMMA)) { - pn2 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn2) - return NULL; - pn2->pn_pos.begin = pn->pn_pos.begin; - PN_INIT_LIST_1(pn2, pn); - pn = pn2; - do { -#if JS_HAS_GENERATORS - pn2 = PN_LAST(pn); - if (pn2->pn_type == TOK_YIELD) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_YIELD_SYNTAX); - return NULL; - } -#endif - pn2 = AssignExpr(cx, ts, tc); - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - } - return pn; -} - -static JSParseNode * -AssignExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - JSTokenType tt; - JSOp op; - - CHECK_RECURSION(); - -#if JS_HAS_GENERATORS - ts->flags |= TSF_OPERAND; - if (js_MatchToken(cx, ts, TOK_YIELD)) { - ts->flags &= ~TSF_OPERAND; - return ReturnOrYield(cx, ts, tc, AssignExpr); - } - ts->flags &= ~TSF_OPERAND; -#endif - - pn = CondExpr(cx, ts, tc); - if (!pn) - return NULL; - - tt = js_GetToken(cx, ts); -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_ASSIGN); - if (tt == TOK_ERROR) - return NULL; - } -#endif - if (tt != TOK_ASSIGN) { - js_UngetToken(ts); - return pn; - } - - op = CURRENT_TOKEN(ts).t_op; - for (pn2 = pn; pn2->pn_type == TOK_RP; pn2 = pn2->pn_kid) - continue; - switch (pn2->pn_type) { - case TOK_NAME: - pn2->pn_op = JSOP_SETNAME; - if (pn2->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - case TOK_DOT: - pn2->pn_op = (pn2->pn_op == JSOP_GETMETHOD) - ? JSOP_SETMETHOD - : JSOP_SETPROP; - break; - case TOK_LB: - pn2->pn_op = JSOP_SETELEM; - break; -#if JS_HAS_DESTRUCTURING - case TOK_RB: - case TOK_RC: - if (op != JSOP_NOP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_DESTRUCT_ASS); - return NULL; - } - pn = AssignExpr(cx, ts, tc); - if (!pn || !CheckDestructuring(cx, NULL, pn2, pn, tc)) - return NULL; - return NewBinary(cx, TOK_ASSIGN, op, pn2, pn, tc); -#endif -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL); - pn2->pn_op = JSOP_SETCALL; - break; -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (pn2->pn_op == JSOP_XMLNAME) { - pn2->pn_op = JSOP_SETXMLNAME; - break; - } - /* FALL THROUGH */ -#endif - default: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_LEFTSIDE_OF_ASS); - return NULL; - } - - return NewBinary(cx, TOK_ASSIGN, op, pn2, AssignExpr(cx, ts, tc), tc); -} - -static JSParseNode * -CondExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn1, *pn2, *pn3; - uintN oldflags; - - pn = OrExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_HOOK)) { - pn1 = pn; - pn = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn) - return NULL; - /* - * Always accept the 'in' operator in the middle clause of a ternary, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; - pn2 = AssignExpr(cx, ts, tc); - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); - - if (!pn2) - return NULL; - MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); - pn3 = AssignExpr(cx, ts, tc); - if (!pn3) - return NULL; - pn->pn_pos.begin = pn1->pn_pos.begin; - pn->pn_pos.end = pn3->pn_pos.end; - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; - } - return pn; -} - -static JSParseNode * -OrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = AndExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_OR)) - pn = NewBinary(cx, TOK_OR, JSOP_OR, pn, OrExpr(cx, ts, tc), tc); - return pn; -} - -static JSParseNode * -AndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BitOrExpr(cx, ts, tc); - if (pn && js_MatchToken(cx, ts, TOK_AND)) - pn = NewBinary(cx, TOK_AND, JSOP_AND, pn, AndExpr(cx, ts, tc), tc); - return pn; -} - -static JSParseNode * -BitOrExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BitXorExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_BITOR)) { - pn = NewBinary(cx, TOK_BITOR, JSOP_BITOR, pn, BitXorExpr(cx, ts, tc), - tc); - } - return pn; -} - -static JSParseNode * -BitXorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BitAndExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_BITXOR)) { - pn = NewBinary(cx, TOK_BITXOR, JSOP_BITXOR, pn, BitAndExpr(cx, ts, tc), - tc); - } - return pn; -} - -static JSParseNode * -BitAndExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = EqExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_BITAND)) - pn = NewBinary(cx, TOK_BITAND, JSOP_BITAND, pn, EqExpr(cx, ts, tc), tc); - return pn; -} - -static JSParseNode * -EqExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSOp op; - - pn = RelExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_EQOP)) { - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, TOK_EQOP, op, pn, RelExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -RelExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSTokenType tt; - JSOp op; - uintN inForInitFlag = tc->flags & TCF_IN_FOR_INIT; - - /* - * Uses of the in operator in ShiftExprs are always unambiguous, - * so unset the flag that prohibits recognizing it. - */ - tc->flags &= ~TCF_IN_FOR_INIT; - - pn = ShiftExpr(cx, ts, tc); - while (pn && - (js_MatchToken(cx, ts, TOK_RELOP) || - /* - * Recognize the 'in' token as an operator only if we're not - * currently in the init expr of a for loop. - */ - (inForInitFlag == 0 && js_MatchToken(cx, ts, TOK_IN)) || - js_MatchToken(cx, ts, TOK_INSTANCEOF))) { - tt = CURRENT_TOKEN(ts).type; - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, tt, op, pn, ShiftExpr(cx, ts, tc), tc); - } - /* Restore previous state of inForInit flag. */ - tc->flags |= inForInitFlag; - - return pn; -} - -static JSParseNode * -ShiftExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSOp op; - - pn = AddExpr(cx, ts, tc); - while (pn && js_MatchToken(cx, ts, TOK_SHOP)) { - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, TOK_SHOP, op, pn, AddExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -AddExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSTokenType tt; - JSOp op; - - pn = MulExpr(cx, ts, tc); - while (pn && - (js_MatchToken(cx, ts, TOK_PLUS) || - js_MatchToken(cx, ts, TOK_MINUS))) { - tt = CURRENT_TOKEN(ts).type; - op = (tt == TOK_PLUS) ? JSOP_ADD : JSOP_SUB; - pn = NewBinary(cx, tt, op, pn, MulExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -MulExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSTokenType tt; - JSOp op; - - pn = UnaryExpr(cx, ts, tc); - while (pn && - (js_MatchToken(cx, ts, TOK_STAR) || - js_MatchToken(cx, ts, TOK_DIVOP))) { - tt = CURRENT_TOKEN(ts).type; - op = CURRENT_TOKEN(ts).t_op; - pn = NewBinary(cx, tt, op, pn, UnaryExpr(cx, ts, tc), tc); - } - return pn; -} - -static JSParseNode * -SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid, - const char *name) -{ - while (kid->pn_type == TOK_RP) - kid = kid->pn_kid; - if (kid->pn_type != TOK_NAME && - kid->pn_type != TOK_DOT && -#if JS_HAS_LVALUE_RETURN - (kid->pn_type != TOK_LP || kid->pn_op != JSOP_CALL) && -#endif -#if JS_HAS_XML_SUPPORT - (kid->pn_type != TOK_UNARYOP || kid->pn_op != JSOP_XMLNAME) && -#endif - kid->pn_type != TOK_LB) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_OPERAND, name); - return NULL; - } - pn->pn_kid = kid; - return kid; -} - -static const char incop_name_str[][10] = {"increment", "decrement"}; - -static JSBool -SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParseNode *pn, JSParseNode *kid, - JSTokenType tt, JSBool preorder) -{ - JSOp op; - - kid = SetLvalKid(cx, ts, pn, kid, incop_name_str[tt == TOK_DEC]); - if (!kid) - return JS_FALSE; - switch (kid->pn_type) { - case TOK_NAME: - op = (tt == TOK_INC) - ? (preorder ? JSOP_INCNAME : JSOP_NAMEINC) - : (preorder ? JSOP_DECNAME : JSOP_NAMEDEC); - if (kid->pn_atom == cx->runtime->atomState.argumentsAtom) - tc->flags |= TCF_FUN_HEAVYWEIGHT; - break; - - case TOK_DOT: - op = (tt == TOK_INC) - ? (preorder ? JSOP_INCPROP : JSOP_PROPINC) - : (preorder ? JSOP_DECPROP : JSOP_PROPDEC); - break; - -#if JS_HAS_LVALUE_RETURN - case TOK_LP: - JS_ASSERT(kid->pn_op == JSOP_CALL); - kid->pn_op = JSOP_SETCALL; - /* FALL THROUGH */ -#endif -#if JS_HAS_XML_SUPPORT - case TOK_UNARYOP: - if (kid->pn_op == JSOP_XMLNAME) - kid->pn_op = JSOP_SETXMLNAME; - /* FALL THROUGH */ -#endif - case TOK_LB: - op = (tt == TOK_INC) - ? (preorder ? JSOP_INCELEM : JSOP_ELEMINC) - : (preorder ? JSOP_DECELEM : JSOP_ELEMDEC); - break; - - default: - JS_ASSERT(0); - op = JSOP_NOP; - } - pn->pn_op = op; - return JS_TRUE; -} - -static JSParseNode * -UnaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSTokenType tt; - JSParseNode *pn, *pn2; - - CHECK_RECURSION(); - - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - - switch (tt) { - case TOK_UNARYOP: - case TOK_PLUS: - case TOK_MINUS: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_UNARYOP; /* PLUS and MINUS are binary */ - pn->pn_op = CURRENT_TOKEN(ts).t_op; - pn2 = UnaryExpr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - pn->pn_kid = pn2; - break; - - case TOK_INC: - case TOK_DEC: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn2 = MemberExpr(cx, ts, tc, JS_TRUE); - if (!pn2) - return NULL; - if (!SetIncOpKid(cx, ts, tc, pn, pn2, tt, JS_TRUE)) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - break; - - case TOK_DELETE: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn2 = UnaryExpr(cx, ts, tc); - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - - /* - * Under ECMA3, deleting any unary expression is valid -- it simply - * returns true. Here we strip off any parentheses. - */ - while (pn2->pn_type == TOK_RP) - pn2 = pn2->pn_kid; - pn->pn_kid = pn2; - break; - - case TOK_ERROR: - return NULL; - - default: - js_UngetToken(ts); - pn = MemberExpr(cx, ts, tc, JS_TRUE); - if (!pn) - return NULL; - - /* Don't look across a newline boundary for a postfix incop. */ - if (ON_CURRENT_LINE(ts, pn->pn_pos)) { - ts->flags |= TSF_OPERAND; - tt = js_PeekTokenSameLine(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_INC || tt == TOK_DEC) { - (void) js_GetToken(cx, ts); - pn2 = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn2) - return NULL; - if (!SetIncOpKid(cx, ts, tc, pn2, pn, tt, JS_FALSE)) - return NULL; - pn2->pn_pos.begin = pn->pn_pos.begin; - pn = pn2; - } - } - break; - } - return pn; -} - -static JSBool -ArgumentList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSParseNode *listNode) -{ - JSBool matched; - - ts->flags |= TSF_OPERAND; - matched = js_MatchToken(cx, ts, TOK_RP); - ts->flags &= ~TSF_OPERAND; - if (!matched) { - do { - JSParseNode *argNode = AssignExpr(cx, ts, tc); - if (!argNode) - return JS_FALSE; -#if JS_HAS_GENERATORS - if (argNode->pn_type == TOK_YIELD) { - js_ReportCompileErrorNumber(cx, argNode, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_YIELD_SYNTAX); - return JS_FALSE; - } -#endif - PN_APPEND(listNode, argNode); - } while (js_MatchToken(cx, ts, TOK_COMMA)); - - if (js_GetToken(cx, ts) != TOK_RP) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_PAREN_AFTER_ARGS); - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSParseNode * -MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowCallSyntax) -{ - JSParseNode *pn, *pn2, *pn3; - JSTokenType tt; - - CHECK_RECURSION(); - - /* Check for new expression first. */ - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_NEW) { - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn2 = MemberExpr(cx, ts, tc, JS_FALSE); - if (!pn2) - return NULL; - pn->pn_op = JSOP_NEW; - PN_INIT_LIST_1(pn, pn2); - pn->pn_pos.begin = pn2->pn_pos.begin; - - if (js_MatchToken(cx, ts, TOK_LP) && !ArgumentList(cx, ts, tc, pn)) - return NULL; - if (pn->pn_count > ARGC_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_CON_ARGS); - return NULL; - } - pn->pn_pos.end = PN_LAST(pn)->pn_pos.end; - } else { - pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE); - if (!pn) - return NULL; - - if (pn->pn_type == TOK_ANYNAME || - pn->pn_type == TOK_AT || - pn->pn_type == TOK_DBLCOLON) { - pn2 = NewOrRecycledNode(cx, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_UNARYOP; - pn2->pn_pos = pn->pn_pos; - pn2->pn_op = JSOP_XMLNAME; - pn2->pn_arity = PN_UNARY; - pn2->pn_kid = pn; - pn2->pn_next = NULL; - pn2->pn_ts = ts; - pn2->pn_source = NULL; - pn = pn2; - } - } - - while ((tt = js_GetToken(cx, ts)) > TOK_EOF) { - if (tt == TOK_DOT) { - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; -#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); - pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); - if (!pn3) - return NULL; - tt = pn3->pn_type; - if (tt == TOK_NAME || - (tt == TOK_DBLCOLON && - pn3->pn_arity == PN_NAME && - pn3->pn_expr->pn_type == TOK_FUNCTION)) { - pn2->pn_op = (tt == TOK_NAME) ? JSOP_GETPROP : JSOP_GETMETHOD; - pn2->pn_expr = pn; - pn2->pn_atom = pn3->pn_atom; - RecycleTree(pn3, tc); - } else { - if (TOKEN_TYPE_IS_XML(tt)) { - pn2->pn_type = TOK_LB; - pn2->pn_op = JSOP_GETELEM; - } else if (tt == TOK_RP) { - JSParseNode *group = pn3; - - /* Recycle the useless TOK_RP/JSOP_GROUP node. */ - pn3 = group->pn_kid; - group->pn_kid = NULL; - RecycleTree(group, tc); - pn2->pn_type = TOK_FILTER; - pn2->pn_op = JSOP_FILTER; - - /* A filtering predicate is like a with statement. */ - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NAME_AFTER_DOT); - return NULL; - } - pn2->pn_arity = PN_BINARY; - pn2->pn_left = pn; - pn2->pn_right = pn3; - } -#else - ts->flags |= TSF_KEYWORD_IS_NAME; - MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - pn2->pn_op = JSOP_GETPROP; - pn2->pn_expr = pn; - pn2->pn_atom = CURRENT_TOKEN(ts).t_atom; -#endif - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; -#if JS_HAS_XML_SUPPORT - } else if (tt == TOK_DBLDOT) { - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME); - pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE); - if (!pn3) - return NULL; - tt = pn3->pn_type; - if (tt == TOK_NAME) { - pn3->pn_type = TOK_STRING; - pn3->pn_arity = PN_NULLARY; - pn3->pn_op = JSOP_QNAMEPART; - } else if (!TOKEN_TYPE_IS_XML(tt)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_NAME_AFTER_DOT); - return NULL; - } - pn2->pn_op = JSOP_DESCENDANTS; - pn2->pn_left = pn; - pn2->pn_right = pn3; - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; -#endif - } else if (tt == TOK_LB) { - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - pn3 = Expr(cx, ts, tc); - if (!pn3) - return NULL; - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX); - pn2->pn_pos.begin = pn->pn_pos.begin; - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - - /* Optimize o['p'] to o.p by rewriting pn2. */ - if (pn3->pn_type == TOK_STRING) { - pn2->pn_type = TOK_DOT; - pn2->pn_op = JSOP_GETPROP; - pn2->pn_arity = PN_NAME; - pn2->pn_expr = pn; - pn2->pn_atom = pn3->pn_atom; - } else { - pn2->pn_op = JSOP_GETELEM; - pn2->pn_left = pn; - pn2->pn_right = pn3; - } - } else if (allowCallSyntax && tt == TOK_LP) { - pn2 = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn2) - return NULL; - - /* Pick JSOP_EVAL and flag tc as heavyweight if eval(...). */ - pn2->pn_op = JSOP_CALL; - if (pn->pn_op == JSOP_NAME && - pn->pn_atom == cx->runtime->atomState.evalAtom) { - pn2->pn_op = JSOP_EVAL; - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } - - PN_INIT_LIST_1(pn2, pn); - pn2->pn_pos.begin = pn->pn_pos.begin; - - if (!ArgumentList(cx, ts, tc, pn2)) - return NULL; - if (pn2->pn_count > ARGC_LIMIT) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_TOO_MANY_FUN_ARGS); - return NULL; - } - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - } else { - js_UngetToken(ts); - return pn; - } - - pn = pn2; - } - if (tt == TOK_ERROR) - return NULL; - return pn; -} - -static JSParseNode * -BracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - uintN oldflags; - JSParseNode *pn; - - /* - * Always accept the 'in' operator in a parenthesized expression, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; - pn = Expr(cx, ts, tc); - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); - return pn; -} - -#if JS_HAS_XML_SUPPORT - -static JSParseNode * -EndBracketedExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = BracketedExpr(cx, ts, tc); - if (!pn) - return NULL; - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_ATTR_EXPR); - return pn; -} - -/* - * From the ECMA-357 grammar in 11.1.1 and 11.1.2: - * - * AttributeIdentifier: - * @ PropertySelector - * @ QualifiedIdentifier - * @ [ Expression ] - * - * PropertySelector: - * Identifier - * * - * - * QualifiedIdentifier: - * PropertySelector :: PropertySelector - * PropertySelector :: [ Expression ] - * - * We adapt AttributeIdentifier and QualifiedIdentier to be LL(1), like so: - * - * AttributeIdentifier: - * @ QualifiedIdentifier - * @ [ Expression ] - * - * PropertySelector: - * Identifier - * * - * - * QualifiedIdentifier: - * PropertySelector :: PropertySelector - * PropertySelector :: [ Expression ] - * PropertySelector - * - * As PrimaryExpression: Identifier is in ECMA-262 and we want the semantics - * for that rule to result in a name node, but ECMA-357 extends the grammar - * to include PrimaryExpression: QualifiedIdentifier, we must factor further: - * - * QualifiedIdentifier: - * PropertySelector QualifiedSuffix - * - * QualifiedSuffix: - * :: PropertySelector - * :: [ Expression ] - * /nothing/ - * - * And use this production instead of PrimaryExpression: QualifiedIdentifier: - * - * PrimaryExpression: - * Identifier QualifiedSuffix - * - * We hoist the :: match into callers of QualifiedSuffix, in order to tweak - * PropertySelector vs. Identifier pn_arity, pn_op, and other members. - */ -static JSParseNode * -PropertySelector(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - if (pn->pn_type == TOK_STAR) { - pn->pn_type = TOK_ANYNAME; - pn->pn_op = JSOP_ANYNAME; - pn->pn_atom = cx->runtime->atomState.starAtom; - } else { - JS_ASSERT(pn->pn_type == TOK_NAME); - pn->pn_op = JSOP_QNAMEPART; - pn->pn_arity = PN_NAME; - pn->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - } - return pn; -} - -static JSParseNode * -QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, - JSTreeContext *tc) -{ - JSParseNode *pn2, *pn3; - JSTokenType tt; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_DBLCOLON); - pn2 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn2) - return NULL; - - /* Left operand of :: must be evaluated if it is an identifier. */ - if (pn->pn_op == JSOP_QNAMEPART) - pn->pn_op = JSOP_NAME; - - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_STAR || tt == TOK_NAME) { - /* Inline and specialize PropertySelector for JSOP_QNAMECONST. */ - pn2->pn_op = JSOP_QNAMECONST; - pn2->pn_atom = (tt == TOK_STAR) - ? cx->runtime->atomState.starAtom - : CURRENT_TOKEN(ts).t_atom; - pn2->pn_expr = pn; - pn2->pn_slot = -1; - pn2->pn_attrs = 0; - return pn2; - } - - if (tt != TOK_LB) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - pn3 = EndBracketedExpr(cx, ts, tc); - if (!pn3) - return NULL; - - pn2->pn_op = JSOP_QNAME; - pn2->pn_arity = PN_BINARY; - pn2->pn_left = pn; - pn2->pn_right = pn3; - return pn2; -} - -static JSParseNode * -QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - - pn = PropertySelector(cx, ts, tc); - if (!pn) - return NULL; - if (js_MatchToken(cx, ts, TOK_DBLCOLON)) - pn = QualifiedSuffix(cx, ts, pn, tc); - return pn; -} - -static JSParseNode * -AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - JSTokenType tt; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_AT); - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn->pn_op = JSOP_TOATTRNAME; - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - if (tt == TOK_STAR || tt == TOK_NAME) { - pn2 = QualifiedIdentifier(cx, ts, tc); - } else if (tt == TOK_LB) { - pn2 = EndBracketedExpr(cx, ts, tc); - } else { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - if (!pn2) - return NULL; - pn->pn_kid = pn2; - return pn; -} - -/* - * Make a TOK_LC unary node whose pn_kid is an expression. - */ -static JSParseNode * -XMLExpr(JSContext *cx, JSTokenStream *ts, JSBool inTag, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2; - uintN oldflags; - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_LC); - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - - /* - * Turn off XML tag mode, but don't restore it after parsing this braced - * expression. Instead, simply restore ts's old flags. This is required - * because XMLExpr is called both from within a tag, and from within text - * contained in an element, but outside of any start, end, or point tag. - */ - oldflags = ts->flags; - ts->flags = oldflags & ~TSF_XMLTAGMODE; - pn2 = Expr(cx, ts, tc); - if (!pn2) - return NULL; - - MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR); - ts->flags = oldflags; - pn->pn_kid = pn2; - pn->pn_op = inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR; - return pn; -} - -/* - * Make a terminal node for one of TOK_XMLNAME, TOK_XMLATTR, TOK_XMLSPACE, - * TOK_XMLTEXT, TOK_XMLCDATA, TOK_XMLCOMMENT, or TOK_XMLPI. When converting - * parse tree to XML, we preserve a TOK_XMLSPACE node only if it's the sole - * child of a container tag. - */ -static JSParseNode * -XMLAtomNode(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn; - JSToken *tp; - - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - tp = &CURRENT_TOKEN(ts); - pn->pn_op = tp->t_op; - pn->pn_atom = tp->t_atom; - if (tp->type == TOK_XMLPI) - pn->pn_atom2 = tp->t_atom2; - return pn; -} - -/* - * Parse the productions: - * - * XMLNameExpr: - * XMLName XMLNameExpr? - * { Expr } XMLNameExpr? - * - * Return a PN_LIST, PN_UNARY, or PN_NULLARY according as XMLNameExpr produces - * a list of names and/or expressions, a single expression, or a single name. - * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME; if PN_UNARY, pn_type - * will be TOK_LC. - */ -static JSParseNode * -XMLNameExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) -{ - JSParseNode *pn, *pn2, *list; - JSTokenType tt; - - pn = list = NULL; - do { - tt = CURRENT_TOKEN(ts).type; - if (tt == TOK_LC) { - pn2 = XMLExpr(cx, ts, JS_TRUE, tc); - if (!pn2) - return NULL; - } else { - JS_ASSERT(tt == TOK_XMLNAME); - pn2 = XMLAtomNode(cx, ts, tc); - if (!pn2) - return NULL; - } - - if (!pn) { - pn = pn2; - } else { - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_XMLNAME; - list->pn_pos.begin = pn->pn_pos.begin; - PN_INIT_LIST_1(list, pn); - list->pn_extra = PNX_CANTFOLD; - pn = list; - } - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - } while ((tt = js_GetToken(cx, ts)) == TOK_XMLNAME || tt == TOK_LC); - - js_UngetToken(ts); - return pn; -} - -/* - * Macro to test whether an XMLNameExpr or XMLTagContent node can be folded - * at compile time into a JSXML tree. - */ -#define XML_FOLDABLE(pn) ((pn)->pn_arity == PN_LIST \ - ? ((pn)->pn_extra & PNX_CANTFOLD) == 0 \ - : (pn)->pn_type != TOK_LC) - -/* - * Parse the productions: - * - * XMLTagContent: - * XMLNameExpr - * XMLTagContent S XMLNameExpr S? = S? XMLAttr - * XMLTagContent S XMLNameExpr S? = S? { Expr } - * - * Return a PN_LIST, PN_UNARY, or PN_NULLARY according to how XMLTagContent - * produces a list of name and attribute values and/or braced expressions, a - * single expression, or a single name. - * - * If PN_LIST or PN_NULLARY, pn_type will be TOK_XMLNAME for the case where - * XMLTagContent: XMLNameExpr. If pn_type is not TOK_XMLNAME but pn_arity is - * PN_LIST, pn_type will be tagtype. If PN_UNARY, pn_type will be TOK_LC and - * we parsed exactly one expression. - */ -static JSParseNode * -XMLTagContent(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tagtype, JSAtom **namep) -{ - JSParseNode *pn, *pn2, *list; - JSTokenType tt; - - pn = XMLNameExpr(cx, ts, tc); - if (!pn) - return NULL; - *namep = (pn->pn_arity == PN_NULLARY) ? pn->pn_atom : NULL; - list = NULL; - - while (js_MatchToken(cx, ts, TOK_XMLSPACE)) { - tt = js_GetToken(cx, ts); - if (tt != TOK_XMLNAME && tt != TOK_LC) { - js_UngetToken(ts); - break; - } - - pn2 = XMLNameExpr(cx, ts, tc); - if (!pn2) - return NULL; - if (!list) { - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = tagtype; - list->pn_pos.begin = pn->pn_pos.begin; - PN_INIT_LIST_1(list, pn); - pn = list; - } - PN_APPEND(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - - js_MatchToken(cx, ts, TOK_XMLSPACE); - MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_NO_ASSIGN_IN_XML_ATTR); - js_MatchToken(cx, ts, TOK_XMLSPACE); - - tt = js_GetToken(cx, ts); - if (tt == TOK_XMLATTR) { - pn2 = XMLAtomNode(cx, ts, tc); - } else if (tt == TOK_LC) { - pn2 = XMLExpr(cx, ts, JS_TRUE, tc); - pn->pn_extra |= PNX_CANTFOLD; - } else { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_ATTR_VALUE); - return NULL; - } - if (!pn2) - return NULL; - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - - return pn; -} - -#define XML_CHECK_FOR_ERROR_AND_EOF(tt,result) \ - JS_BEGIN_MACRO \ - if ((tt) <= TOK_EOF) { \ - if ((tt) == TOK_EOF) { \ - js_ReportCompileErrorNumber(cx, ts, \ - JSREPORT_TS | JSREPORT_ERROR, \ - JSMSG_END_OF_XML_SOURCE); \ - } \ - return result; \ - } \ - JS_END_MACRO - -static JSParseNode * -XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowList); - -/* - * Consume XML element tag content, including the TOK_XMLETAGO (flags &= ~TSF_XMLTAGMODE; - for (;;) { - ts->flags |= TSF_XMLTEXTMODE; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_XMLTEXTMODE; - XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); - - JS_ASSERT(tt == TOK_XMLSPACE || tt == TOK_XMLTEXT); - textAtom = CURRENT_TOKEN(ts).t_atom; - if (textAtom) { - /* Non-zero-length XML text scanned. */ - pn2 = XMLAtomNode(cx, ts, tc); - if (!pn2) - return JS_FALSE; - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - XML_CHECK_FOR_ERROR_AND_EOF(tt, JS_FALSE); - if (tt == TOK_XMLETAGO) - break; - - if (tt == TOK_LC) { - pn2 = XMLExpr(cx, ts, JS_FALSE, tc); - pn->pn_extra |= PNX_CANTFOLD; - } else if (tt == TOK_XMLSTAGO) { - pn2 = XMLElementOrList(cx, ts, tc, JS_FALSE); - if (pn2) { - pn2->pn_extra &= ~PNX_XMLROOT; - pn->pn_extra |= pn2->pn_extra; - } - } else { - JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT || - tt == TOK_XMLPI); - pn2 = XMLAtomNode(cx, ts, tc); - } - if (!pn2) - return JS_FALSE; - pn->pn_pos.end = pn2->pn_pos.end; - PN_APPEND(pn, pn2); - } - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLETAGO); - ts->flags |= TSF_XMLTAGMODE; - return JS_TRUE; -} - -/* - * Return a PN_LIST node containing an XML or XMLList Initialiser. - */ -static JSParseNode * -XMLElementOrList(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowList) -{ - JSParseNode *pn, *pn2, *list; - JSBool hadSpace; - JSTokenType tt; - JSAtom *startAtom, *endAtom; - - CHECK_RECURSION(); - - JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_XMLSTAGO); - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - - ts->flags |= TSF_XMLTAGMODE; - hadSpace = js_MatchToken(cx, ts, TOK_XMLSPACE); - tt = js_GetToken(cx, ts); - if (tt == TOK_ERROR) - return NULL; - - if (tt == TOK_XMLNAME || tt == TOK_LC) { - /* - * XMLElement. Append the tag and its contents, if any, to pn. - */ - pn2 = XMLTagContent(cx, ts, tc, TOK_XMLSTAGO, &startAtom); - if (!pn2) - return NULL; - js_MatchToken(cx, ts, TOK_XMLSPACE); - - tt = js_GetToken(cx, ts); - if (tt == TOK_XMLPTAGC) { - /* Point tag (/>): recycle pn if pn2 is a list of tag contents. */ - if (pn2->pn_type == TOK_XMLSTAGO) { - PN_INIT_LIST(pn); - RecycleTree(pn, tc); - pn = pn2; - } else { - JS_ASSERT(pn2->pn_type == TOK_XMLNAME || - pn2->pn_type == TOK_LC); - PN_INIT_LIST_1(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - } - pn->pn_type = TOK_XMLPTAGC; - pn->pn_extra |= PNX_XMLROOT; - } else { - /* We had better have a tag-close (>) at this point. */ - if (tt != TOK_XMLTAGC) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_TAG_SYNTAX); - return NULL; - } - pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - - /* Make sure pn2 is a TOK_XMLSTAGO list containing tag contents. */ - if (pn2->pn_type != TOK_XMLSTAGO) { - PN_INIT_LIST_1(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - pn2 = pn; - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - } - - /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */ - pn->pn_type = TOK_XMLELEM; - PN_INIT_LIST_1(pn, pn2); - if (!XML_FOLDABLE(pn2)) - pn->pn_extra |= PNX_CANTFOLD; - pn->pn_extra |= PNX_XMLROOT; - - /* Get element contents and delimiting end-tag-open sequence. */ - if (!XMLElementContent(cx, ts, pn, tc)) - return NULL; - - js_MatchToken(cx, ts, TOK_XMLSPACE); - tt = js_GetToken(cx, ts); - XML_CHECK_FOR_ERROR_AND_EOF(tt, NULL); - if (tt != TOK_XMLNAME && tt != TOK_LC) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_TAG_SYNTAX); - return NULL; - } - - /* Parse end tag; check mismatch at compile-time if we can. */ - pn2 = XMLTagContent(cx, ts, tc, TOK_XMLETAGO, &endAtom); - if (!pn2) - return NULL; - if (pn2->pn_type == TOK_XMLETAGO) { - /* Oops, end tag has attributes! */ - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_TAG_SYNTAX); - return NULL; - } - if (endAtom && startAtom && endAtom != startAtom) { - JSString *str = ATOM_TO_STRING(startAtom); - - /* End vs. start tag name mismatch: point to the tag name. */ - js_ReportCompileErrorNumberUC(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_XML_TAG_NAME_MISMATCH, - JSSTRING_CHARS(str)); - return NULL; - } - - /* Make a TOK_XMLETAGO list with pn2 as its single child. */ - JS_ASSERT(pn2->pn_type == TOK_XMLNAME || pn2->pn_type == TOK_LC); - list = NewParseNode(cx, ts, PN_LIST, tc); - if (!list) - return NULL; - list->pn_type = TOK_XMLETAGO; - PN_INIT_LIST_1(list, pn2); - PN_APPEND(pn, list); - if (!XML_FOLDABLE(pn2)) { - list->pn_extra |= PNX_CANTFOLD; - pn->pn_extra |= PNX_CANTFOLD; - } - - js_MatchToken(cx, ts, TOK_XMLSPACE); - MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_TAG_SYNTAX); - } - - /* Set pn_op now that pn has been updated to its final value. */ - pn->pn_op = JSOP_TOXML; - } else if (!hadSpace && allowList && tt == TOK_XMLTAGC) { - /* XMLList Initialiser. */ - pn->pn_type = TOK_XMLLIST; - pn->pn_op = JSOP_TOXMLLIST; - PN_INIT_LIST(pn); - pn->pn_extra |= PNX_XMLROOT; - if (!XMLElementContent(cx, ts, pn, tc)) - return NULL; - - MUST_MATCH_TOKEN(TOK_XMLTAGC, JSMSG_BAD_XML_LIST_SYNTAX); - } else { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_NAME_SYNTAX); - return NULL; - } - - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - ts->flags &= ~TSF_XMLTAGMODE; - return pn; -} - -static JSParseNode * -XMLElementOrListRoot(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSBool allowList) -{ - uint32 oldopts; - JSParseNode *pn; - - /* - * Force XML support to be enabled so that comments and CDATA literals - * are recognized, instead of ). - */ - oldopts = JS_SetOptions(cx, cx->options | JSOPTION_XML); - pn = XMLElementOrList(cx, ts, tc, allowList); - JS_SetOptions(cx, oldopts); - return pn; -} - -JS_FRIEND_API(JSParseNode *) -js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSBool allowList) -{ - JSStackFrame *fp, frame; - JSParseNode *pn; - JSTreeContext tc; - JSTokenType tt; - - /* - * Push a compiler frame if we have no frames, or if the top frame is a - * lightweight function activation, or if its scope chain doesn't match - * the one passed to us. - */ - fp = cx->fp; - MaybeSetupFrame(cx, chain, fp, &frame); - JS_KEEP_ATOMS(cx->runtime); - TREE_CONTEXT_INIT(&tc); - - /* Set XML-only mode to turn off special treatment of {expr} in XML. */ - ts->flags |= TSF_OPERAND | TSF_XMLONLYMODE; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - - if (tt != TOK_XMLSTAGO) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_MARKUP); - pn = NULL; - } else { - pn = XMLElementOrListRoot(cx, ts, &tc, allowList); - } - - ts->flags &= ~TSF_XMLONLYMODE; - TREE_CONTEXT_FINISH(&tc); - JS_UNKEEP_ATOMS(cx->runtime); - cx->fp = fp; - return pn; -} - -#endif /* JS_HAS_XMLSUPPORT */ - -static JSParseNode * -PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, - JSTokenType tt, JSBool afterDot) -{ - JSParseNode *pn, *pn2, *pn3; - JSOp op; - -#if JS_HAS_SHARP_VARS - JSParseNode *defsharp; - JSBool notsharp; - - defsharp = NULL; - notsharp = JS_FALSE; - again: - /* - * Control flows here after #n= is scanned. If the following primary is - * not valid after such a "sharp variable" definition, the tt switch case - * should set notsharp. - */ -#endif - - CHECK_RECURSION(); - -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION); - if (tt == TOK_ERROR) - return NULL; - } -#endif - - switch (tt) { - case TOK_FUNCTION: -#if JS_HAS_XML_SUPPORT - ts->flags |= TSF_KEYWORD_IS_NAME; - if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { - ts->flags &= ~TSF_KEYWORD_IS_NAME; - pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_FUNCTION; - pn = QualifiedSuffix(cx, ts, pn2, tc); - if (!pn) - return NULL; - break; - } - ts->flags &= ~TSF_KEYWORD_IS_NAME; -#endif - pn = FunctionExpr(cx, ts, tc); - if (!pn) - return NULL; - break; - - case TOK_LB: - { - JSBool matched; - jsuint index; - - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_RB; - -#if JS_HAS_SHARP_VARS - if (defsharp) { - PN_INIT_LIST_1(pn, defsharp); - defsharp = NULL; - } else -#endif - PN_INIT_LIST(pn); - - ts->flags |= TSF_OPERAND; - matched = js_MatchToken(cx, ts, TOK_RB); - ts->flags &= ~TSF_OPERAND; - if (!matched) { - for (index = 0; ; index++) { - if (index == ARRAY_INIT_LIMIT) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_ARRAY_INIT_TOO_BIG); - return NULL; - } - - ts->flags |= TSF_OPERAND; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - if (tt == TOK_RB) { - pn->pn_extra |= PNX_ENDCOMMA; - break; - } - - if (tt == TOK_COMMA) { - /* So CURRENT_TOKEN gets TOK_COMMA and not TOK_LB. */ - js_MatchToken(cx, ts, TOK_COMMA); - pn2 = NewParseNode(cx, ts, PN_NULLARY, tc); - } else { - pn2 = AssignExpr(cx, ts, tc); - } - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - - if (tt != TOK_COMMA) { - /* If we didn't already match TOK_COMMA in above case. */ - if (!js_MatchToken(cx, ts, TOK_COMMA)) - break; - } - } - -#if JS_HAS_GENERATORS - /* - * At this point, (index == 0 && pn->pn_count != 0) implies one - * element initialiser was parsed (possibly with a defsharp before - * the left bracket). - * - * An array comprehension of the form: - * - * [i * j for (i in o) for (j in p) if (i != j)] - * - * translates to roughly the following let expression: - * - * let (array = new Array, i, j) { - * for (i in o) let { - * for (j in p) - * if (i != j) - * array.push(i * j) - * } - * array - * } - * - * where array is a nameless block-local variable. The "roughly" - * means that an implementation may optimize away the array.push. - * An array comprehension opens exactly one block scope, no matter - * how many for heads it contains. - * - * Each let () {...} or for (let ...) ... compiles to: - * - * JSOP_ENTERBLOCK ... JSOP_LEAVEBLOCK - * - * where is a literal object representing the block scope, - * with properties, naming each var declared in the block. - * - * Each var declaration in a let-block binds a name in at - * compile time, and allocates a slot on the operand stack at - * runtime via JSOP_ENTERBLOCK. A block-local var is accessed - * by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with - * JSOP_FORLOCAL. These ops all have an immediate operand, the - * local slot's stack index from fp->spbase. - * - * The array comprehension iteration step, array.push(i * j) in - * the example above, is done by ; JSOP_ARRAYCOMP , - * where is the index of array's stack slot. - */ - if (index == 0 && - pn->pn_count != 0 && - js_MatchToken(cx, ts, TOK_FOR)) { - JSParseNode **pnp, *pnexp, *pntop, *pnlet; - BindData data; - JSRuntime *rt; - JSStmtInfo stmtInfo; - JSAtom *atom; - - /* Relabel pn as an array comprehension node. */ - pn->pn_type = TOK_ARRAYCOMP; - - /* - * Remove the comprehension expression from pn's linked list - * and save it via pnexp. We'll re-install it underneath the - * ARRAYPUSH node after we parse the rest of the comprehension. - */ - pnexp = PN_LAST(pn); - JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2); - pn->pn_tail = (--pn->pn_count == 1) - ? &pn->pn_head->pn_next - : &pn->pn_head; - *pn->pn_tail = NULL; - - /* - * Make a parse-node and literal object representing the array - * comprehension's block scope. - */ - pntop = PushLexicalScope(cx, ts, tc, &stmtInfo); - if (!pntop) - return NULL; - pnp = &pntop->pn_expr; - - data.pn = NULL; - data.ts = ts; - data.obj = tc->blockChain; - data.op = JSOP_NOP; - data.binder = BindLet; - data.u.let.index = 0; - data.u.let.overflow = JSMSG_ARRAY_INIT_TOO_BIG; - - rt = cx->runtime; - do { - /* - * FOR node is binary, left is control and right is body. - * Use index to count each block-local let-variable on the - * left-hand side of IN. - */ - pn2 = NewParseNode(cx, ts, PN_BINARY, tc); - if (!pn2) - return NULL; - - pn2->pn_op = JSOP_FORIN; - if (js_MatchToken(cx, ts, TOK_NAME)) { - if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom) - pn2->pn_op = JSOP_FOREACH; - else - js_UngetToken(ts); - } - MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - - tt = js_GetToken(cx, ts); - switch (tt) { -#if JS_HAS_DESTRUCTURING - case TOK_LB: - case TOK_LC: - pnlet = DestructuringExpr(cx, &data, tc, tt); - if (!pnlet) - return NULL; - - if (pnlet->pn_type != TOK_RB || pnlet->pn_count != 2) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_FOR_LEFTSIDE); - return NULL; - } - - /* Destructuring requires [key, value] enumeration. */ - if (pn2->pn_op != JSOP_FOREACH) - pn2->pn_op = JSOP_FOREACHKEYVAL; - break; -#endif - - case TOK_NAME: - atom = CURRENT_TOKEN(ts).t_atom; - if (!data.binder(cx, &data, atom, tc)) - return NULL; - - /* - * Create a name node with op JSOP_NAME. We can't set - * op to JSOP_GETLOCAL here, because we don't yet know - * the block's depth in the operand stack frame. The - * code generator computes that, and it tries to bind - * all names to slots, so we must let it do the deed. - */ - pnlet = NewParseNode(cx, ts, PN_NAME, tc); - if (!pnlet) - return NULL; - pnlet->pn_op = JSOP_NAME; - pnlet->pn_atom = atom; - pnlet->pn_expr = NULL; - pnlet->pn_slot = -1; - pnlet->pn_attrs = 0; - break; - - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS|JSREPORT_ERROR, - JSMSG_NO_VARIABLE_NAME); - return NULL; - } - - MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME); - pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet, - Expr(cx, ts, tc), tc); - if (!pn3) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL); - pn2->pn_left = pn3; - *pnp = pn2; - pnp = &pn2->pn_right; - } while (js_MatchToken(cx, ts, TOK_FOR)); - - if (js_MatchToken(cx, ts, TOK_IF)) { - pn2 = NewParseNode(cx, ts, PN_TERNARY, tc); - if (!pn2) - return NULL; - pn2->pn_kid1 = Condition(cx, ts, tc); - if (!pn2->pn_kid1) - return NULL; - pn2->pn_kid2 = NULL; - pn2->pn_kid3 = NULL; - *pnp = pn2; - pnp = &pn2->pn_kid2; - } - - pn2 = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn2) - return NULL; - pn2->pn_type = TOK_ARRAYPUSH; - pn2->pn_op = JSOP_ARRAYPUSH; - pn2->pn_kid = pnexp; - *pnp = pn2; - PN_APPEND(pn, pntop); - - js_PopStatement(tc); - } -#endif /* JS_HAS_GENERATORS */ - - MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST); - } - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - return pn; - } - -#if JS_HAS_BLOCK_SCOPE - case TOK_LET: - pn = LetBlock(cx, ts, tc, JS_FALSE); - if (!pn) - return NULL; - break; -#endif - - case TOK_LC: - { - JSBool afterComma; - - pn = NewParseNode(cx, ts, PN_LIST, tc); - if (!pn) - return NULL; - pn->pn_type = TOK_RC; - -#if JS_HAS_SHARP_VARS - if (defsharp) { - PN_INIT_LIST_1(pn, defsharp); - defsharp = NULL; - } else -#endif - PN_INIT_LIST(pn); - - afterComma = JS_FALSE; - for (;;) { - ts->flags |= TSF_KEYWORD_IS_NAME; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_KEYWORD_IS_NAME; - switch (tt) { - case TOK_NUMBER: - pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (pn3) - pn3->pn_dval = CURRENT_TOKEN(ts).t_dval; - break; - case TOK_NAME: -#if JS_HAS_GETTER_SETTER - { - JSAtom *atom; - JSRuntime *rt; - - atom = CURRENT_TOKEN(ts).t_atom; - rt = cx->runtime; - if (atom == rt->atomState.getAtom || - atom == rt->atomState.setAtom) { - op = (atom == rt->atomState.getAtom) - ? JSOP_GETTER - : JSOP_SETTER; - if (js_MatchToken(cx, ts, TOK_NAME)) { - pn3 = NewParseNode(cx, ts, PN_NAME, tc); - if (!pn3) - return NULL; - pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; - pn3->pn_expr = NULL; - pn3->pn_slot = -1; - pn3->pn_attrs = 0; - - /* We have to fake a 'function' token here. */ - CURRENT_TOKEN(ts).t_op = JSOP_NOP; - CURRENT_TOKEN(ts).type = TOK_FUNCTION; - pn2 = FunctionExpr(cx, ts, tc); - pn2 = NewBinary(cx, TOK_COLON, op, pn3, pn2, tc); - goto skip; - } - } - /* else fall thru ... */ - } -#endif - case TOK_STRING: - pn3 = NewParseNode(cx, ts, PN_NULLARY, tc); - if (pn3) - pn3->pn_atom = CURRENT_TOKEN(ts).t_atom; - break; - case TOK_RC: - if (afterComma && - !js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_TRAILING_COMMA)) { - return NULL; - } - goto end_obj_init; - default: - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_PROP_ID); - return NULL; - } - - tt = js_GetToken(cx, ts); -#if JS_HAS_GETTER_SETTER - if (tt == TOK_NAME) { - tt = CheckGetterOrSetter(cx, ts, TOK_COLON); - if (tt == TOK_ERROR) - return NULL; - } -#endif - if (tt != TOK_COLON) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_COLON_AFTER_ID); - return NULL; - } - op = CURRENT_TOKEN(ts).t_op; - pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc), tc); -#if JS_HAS_GETTER_SETTER - skip: -#endif - if (!pn2) - return NULL; - PN_APPEND(pn, pn2); - - tt = js_GetToken(cx, ts); - if (tt == TOK_RC) - goto end_obj_init; - if (tt != TOK_COMMA) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_CURLY_AFTER_LIST); - return NULL; - } - afterComma = JS_TRUE; - } - end_obj_init: - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - return pn; - } - -#if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - if (defsharp) - goto badsharp; - defsharp = NewParseNode(cx, ts, PN_UNARY, tc); - if (!defsharp) - return NULL; - defsharp->pn_kid = NULL; - defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; - ts->flags |= TSF_OPERAND; - tt = js_GetToken(cx, ts); - ts->flags &= ~TSF_OPERAND; - goto again; - - case TOK_USESHARP: - /* Check for forward/dangling references at runtime, to allow eval. */ - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval; - notsharp = JS_TRUE; - break; -#endif /* JS_HAS_SHARP_VARS */ - - case TOK_LP: - pn = NewParseNode(cx, ts, PN_UNARY, tc); - if (!pn) - return NULL; - pn2 = BracketedExpr(cx, ts, tc); - if (!pn2) - return NULL; - - MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN); - if (pn2->pn_type == TOK_RP || - (js_CodeSpec[pn2->pn_op].prec >= js_CodeSpec[JSOP_GETPROP].prec && - !afterDot)) { - /* - * Avoid redundant JSOP_GROUP opcodes, for efficiency and mainly - * to help the decompiler look ahead from a JSOP_ENDINIT to see a - * JSOP_GROUP followed by a POP or POPV. That sequence means the - * parentheses are mandatory, to disambiguate object initialisers - * as expression statements from block statements. - * - * Also drop pn if pn2 is a member or a primary expression of any - * kind. This is required to avoid generating a JSOP_GROUP that - * will null the |obj| interpreter register, causing |this| in any - * call of that member expression to bind to the global object. - */ - pn->pn_kid = NULL; - RecycleTree(pn, tc); - pn = pn2; - } else { - pn->pn_type = TOK_RP; - pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end; - pn->pn_kid = pn2; - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_STAR: - pn = QualifiedIdentifier(cx, ts, tc); - if (!pn) - return NULL; - notsharp = JS_TRUE; - break; - - case TOK_AT: - pn = AttributeIdentifier(cx, ts, tc); - if (!pn) - return NULL; - notsharp = JS_TRUE; - break; - - case TOK_XMLSTAGO: - pn = XMLElementOrListRoot(cx, ts, tc, JS_TRUE); - if (!pn) - return NULL; - notsharp = JS_TRUE; /* XXXbe could be sharp? */ - break; -#endif /* JS_HAS_XML_SUPPORT */ - - case TOK_STRING: -#if JS_HAS_SHARP_VARS - notsharp = JS_TRUE; - /* FALL THROUGH */ -#endif - -#if JS_HAS_XML_SUPPORT - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: - case TOK_XMLPI: -#endif - case TOK_NAME: - case TOK_OBJECT: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_atom = CURRENT_TOKEN(ts).t_atom; -#if JS_HAS_XML_SUPPORT - if (tt == TOK_XMLPI) - pn->pn_atom2 = CURRENT_TOKEN(ts).t_atom2; - else -#endif - pn->pn_op = CURRENT_TOKEN(ts).t_op; - if (tt == TOK_NAME) { - pn->pn_arity = PN_NAME; - pn->pn_expr = NULL; - pn->pn_slot = -1; - pn->pn_attrs = 0; - -#if JS_HAS_XML_SUPPORT - if (js_MatchToken(cx, ts, TOK_DBLCOLON)) { - if (afterDot) { - JSString *str; - - /* - * Here PrimaryExpr is called after '.' or '..' and we - * just scanned .name:: or ..name:: . This is the only - * case where a keyword after '.' or '..' is not - * treated as a property name. - */ - str = ATOM_TO_STRING(pn->pn_atom); - tt = js_CheckKeyword(JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); - if (tt == TOK_FUNCTION) { - pn->pn_arity = PN_NULLARY; - pn->pn_type = TOK_FUNCTION; - } else if (tt != TOK_EOF) { - js_ReportCompileErrorNumber( - cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_KEYWORD_NOT_NS); - return NULL; - } - } - pn = QualifiedSuffix(cx, ts, pn, tc); - if (!pn) - return NULL; - break; - } -#endif - - /* Unqualified __parent__ and __proto__ uses require activations. */ - if (pn->pn_atom == cx->runtime->atomState.parentAtom || - pn->pn_atom == cx->runtime->atomState.protoAtom) { - tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { - JSAtomListElement *ale; - JSStackFrame *fp; - JSBool loopy; - - /* Measure optimizable global variable uses. */ - ATOM_LIST_SEARCH(ale, &tc->decls, pn->pn_atom); - if (ale && - !(fp = cx->fp)->fun && - fp->scopeChain == fp->varobj && - js_IsGlobalReference(tc, pn->pn_atom, &loopy)) { - tc->globalUses++; - if (loopy) - tc->loopyGlobalUses++; - } - } - } - break; - - case TOK_NUMBER: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_dval = CURRENT_TOKEN(ts).t_dval; -#if JS_HAS_SHARP_VARS - notsharp = JS_TRUE; -#endif - break; - - case TOK_PRIMARY: - pn = NewParseNode(cx, ts, PN_NULLARY, tc); - if (!pn) - return NULL; - pn->pn_op = CURRENT_TOKEN(ts).t_op; -#if JS_HAS_SHARP_VARS - notsharp = JS_TRUE; -#endif - break; - -#if !JS_HAS_EXPORT_IMPORT - case TOK_EXPORT: - case TOK_IMPORT: -#endif - case TOK_ERROR: - /* The scanner or one of its subroutines reported the error. */ - return NULL; - - default: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); - return NULL; - } - -#if JS_HAS_SHARP_VARS - if (defsharp) { - if (notsharp) { - badsharp: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_SHARP_VAR_DEF); - return NULL; - } - defsharp->pn_kid = pn; - return defsharp; - } -#endif - return pn; -} - -/* - * Fold from one constant type to another. - * XXX handles only strings and numbers for now - */ -static JSBool -FoldType(JSContext *cx, JSParseNode *pn, JSTokenType type) -{ - if (pn->pn_type != type) { - switch (type) { - case TOK_NUMBER: - if (pn->pn_type == TOK_STRING) { - jsdouble d; - if (!js_ValueToNumber(cx, ATOM_KEY(pn->pn_atom), &d)) - return JS_FALSE; - pn->pn_dval = d; - pn->pn_type = TOK_NUMBER; - pn->pn_op = JSOP_NUMBER; - } - break; - - case TOK_STRING: - if (pn->pn_type == TOK_NUMBER) { - JSString *str = js_NumberToString(cx, pn->pn_dval); - if (!str) - return JS_FALSE; - pn->pn_atom = js_AtomizeString(cx, str, 0); - if (!pn->pn_atom) - return JS_FALSE; - pn->pn_type = TOK_STRING; - pn->pn_op = JSOP_STRING; - } - break; - - default:; - } - } - return JS_TRUE; -} - -/* - * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless - * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after - * a successful call to this function. - */ -static JSBool -FoldBinaryNumeric(JSContext *cx, JSOp op, JSParseNode *pn1, JSParseNode *pn2, - JSParseNode *pn, JSTreeContext *tc) -{ - jsdouble d, d2; - int32 i, j; - uint32 u; - - JS_ASSERT(pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER); - d = pn1->pn_dval; - d2 = pn2->pn_dval; - switch (op) { - case JSOP_LSH: - case JSOP_RSH: - if (!js_DoubleToECMAInt32(cx, d, &i)) - return JS_FALSE; - if (!js_DoubleToECMAInt32(cx, d2, &j)) - return JS_FALSE; - j &= 31; - d = (op == JSOP_LSH) ? i << j : i >> j; - break; - - case JSOP_URSH: - if (!js_DoubleToECMAUint32(cx, d, &u)) - return JS_FALSE; - if (!js_DoubleToECMAInt32(cx, d2, &j)) - return JS_FALSE; - j &= 31; - d = u >> j; - break; - - case JSOP_ADD: - d += d2; - break; - - case JSOP_SUB: - d -= d2; - break; - - case JSOP_MUL: - d *= d2; - break; - - case JSOP_DIV: - if (d2 == 0) { -#if defined(XP_WIN) - /* XXX MSVC miscompiles such that (NaN == 0) */ - if (JSDOUBLE_IS_NaN(d2)) - d = *cx->runtime->jsNaN; - else -#endif - if (d == 0 || JSDOUBLE_IS_NaN(d)) - d = *cx->runtime->jsNaN; - else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) - d = *cx->runtime->jsNegativeInfinity; - else - d = *cx->runtime->jsPositiveInfinity; - } else { - d /= d2; - } - break; - - case JSOP_MOD: - if (d2 == 0) { - d = *cx->runtime->jsNaN; - } else { -#if defined(XP_WIN) - /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ - if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) -#endif - d = fmod(d, d2); - } - break; - - default:; - } - - /* Take care to allow pn1 or pn2 to alias pn. */ - if (pn1 != pn) - RecycleTree(pn1, tc); - if (pn2 != pn) - RecycleTree(pn2, tc); - pn->pn_type = TOK_NUMBER; - pn->pn_op = JSOP_NUMBER; - pn->pn_arity = PN_NULLARY; - pn->pn_dval = d; - return JS_TRUE; -} - -#if JS_HAS_XML_SUPPORT - -static JSBool -FoldXMLConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - JSTokenType tt; - JSParseNode **pnp, *pn1, *pn2; - JSString *accum, *str; - uint32 i, j; - - JS_ASSERT(pn->pn_arity == PN_LIST); - tt = pn->pn_type; - pnp = &pn->pn_head; - pn1 = *pnp; - accum = NULL; - if ((pn->pn_extra & PNX_CANTFOLD) == 0) { - if (tt == TOK_XMLETAGO) - accum = ATOM_TO_STRING(cx->runtime->atomState.etagoAtom); - else if (tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) - accum = ATOM_TO_STRING(cx->runtime->atomState.stagoAtom); - } - - for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) { - /* The parser already rejected end-tags with attributes. */ - JS_ASSERT(tt != TOK_XMLETAGO || i == 0); - switch (pn2->pn_type) { - case TOK_XMLATTR: - if (!accum) - goto cantfold; - /* FALL THROUGH */ - case TOK_XMLNAME: - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_STRING: - if (pn2->pn_arity == PN_LIST) - goto cantfold; - str = ATOM_TO_STRING(pn2->pn_atom); - break; - - case TOK_XMLCDATA: - str = js_MakeXMLCDATAString(cx, ATOM_TO_STRING(pn2->pn_atom)); - if (!str) - return JS_FALSE; - break; - - case TOK_XMLCOMMENT: - str = js_MakeXMLCommentString(cx, ATOM_TO_STRING(pn2->pn_atom)); - if (!str) - return JS_FALSE; - break; - - case TOK_XMLPI: - str = js_MakeXMLPIString(cx, ATOM_TO_STRING(pn2->pn_atom), - ATOM_TO_STRING(pn2->pn_atom2)); - if (!str) - return JS_FALSE; - break; - - cantfold: - default: - JS_ASSERT(*pnp == pn1); - if ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && - (i & 1) ^ (j & 1)) { -#ifdef DEBUG_brendanXXX - printf("1: %d, %d => %s\n", - i, j, accum ? JS_GetStringBytes(accum) : "NULL"); -#endif - } else if (accum && pn1 != pn2) { - while (pn1->pn_next != pn2) { - pn1 = RecycleTree(pn1, tc); - --pn->pn_count; - } - pn1->pn_type = TOK_XMLTEXT; - pn1->pn_op = JSOP_STRING; - pn1->pn_arity = PN_NULLARY; - pn1->pn_atom = js_AtomizeString(cx, accum, 0); - if (!pn1->pn_atom) - return JS_FALSE; - JS_ASSERT(pnp != &pn1->pn_next); - *pnp = pn1; - } - pnp = &pn2->pn_next; - pn1 = *pnp; - accum = NULL; - continue; - } - - if (accum) { - str = ((tt == TOK_XMLSTAGO || tt == TOK_XMLPTAGC) && i != 0) - ? js_AddAttributePart(cx, i & 1, accum, str) - : js_ConcatStrings(cx, accum, str); - if (!str) - return JS_FALSE; -#ifdef DEBUG_brendanXXX - printf("2: %d, %d => %s (%u)\n", - i, j, JS_GetStringBytes(str), JSSTRING_LENGTH(str)); -#endif - ++j; - } - accum = str; - } - - if (accum) { - str = NULL; - if ((pn->pn_extra & PNX_CANTFOLD) == 0) { - if (tt == TOK_XMLPTAGC) - str = ATOM_TO_STRING(cx->runtime->atomState.ptagcAtom); - else if (tt == TOK_XMLSTAGO || tt == TOK_XMLETAGO) - str = ATOM_TO_STRING(cx->runtime->atomState.tagcAtom); - } - if (str) { - accum = js_ConcatStrings(cx, accum, str); - if (!accum) - return JS_FALSE; - } - - JS_ASSERT(*pnp == pn1); - while (pn1->pn_next) { - pn1 = RecycleTree(pn1, tc); - --pn->pn_count; - } - pn1->pn_type = TOK_XMLTEXT; - pn1->pn_op = JSOP_STRING; - pn1->pn_arity = PN_NULLARY; - pn1->pn_atom = js_AtomizeString(cx, accum, 0); - if (!pn1->pn_atom) - return JS_FALSE; - JS_ASSERT(pnp != &pn1->pn_next); - *pnp = pn1; - } - - if (pn1 && pn->pn_count == 1) { - /* - * Only one node under pn, and it has been folded: move pn1 onto pn - * unless pn is an XML root (in which case we need it to tell the code - * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op). If pn is an - * XML root *and* it's a point-tag, rewrite it to TOK_XMLELEM to avoid - * extra "<" and "/>" bracketing at runtime. - */ - if (!(pn->pn_extra & PNX_XMLROOT)) { - PN_MOVE_NODE(pn, pn1); - } else if (tt == TOK_XMLPTAGC) { - pn->pn_type = TOK_XMLELEM; - pn->pn_op = JSOP_TOXML; - } - } - return JS_TRUE; -} - -#endif /* JS_HAS_XML_SUPPORT */ - -static JSBool -StartsWith(JSParseNode *pn, JSTokenType tt) -{ -#define TAIL_RECURSE(pn2) JS_BEGIN_MACRO pn = (pn2); goto recur; JS_END_MACRO - -recur: - if (pn->pn_type == tt) - return JS_TRUE; - switch (pn->pn_arity) { - case PN_FUNC: - return tt == TOK_FUNCTION; - case PN_LIST: - if (pn->pn_head) - TAIL_RECURSE(pn->pn_head); - break; - case PN_TERNARY: - if (pn->pn_kid1) - TAIL_RECURSE(pn->pn_kid1); - break; - case PN_BINARY: - if (pn->pn_left) - TAIL_RECURSE(pn->pn_left); - break; - case PN_UNARY: - /* A parenthesized expression starts with a left parenthesis. */ - if (pn->pn_type == TOK_RP) - return tt == TOK_LP; - if (pn->pn_kid) - TAIL_RECURSE(pn->pn_kid); - break; - case PN_NAME: - if (pn->pn_type == TOK_DOT || pn->pn_type == TOK_DBLDOT) - TAIL_RECURSE(pn->pn_expr); - /* FALL THROUGH */ - } - return JS_FALSE; -#undef TAIL_RECURSE -} - -JSBool -js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc) -{ - JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); - return JS_FALSE; - } - - switch (pn->pn_arity) { - case PN_FUNC: - { - uint16 oldflags = tc->flags; - - tc->flags = (uint16) pn->pn_flags; - if (!js_FoldConstants(cx, pn->pn_body, tc)) - return JS_FALSE; - tc->flags = oldflags; - break; - } - - case PN_LIST: -#if 0 /* JS_HAS_XML_SUPPORT */ - switch (pn->pn_type) { - case TOK_XMLELEM: - case TOK_XMLLIST: - case TOK_XMLPTAGC: - /* - * Try to fold this XML parse tree once, from the top down, into - * a JSXML tree with just one object wrapping the tree root. - * - * Certain subtrees could be folded similarly, but we'd have to - * ensure that none used namespace prefixes declared elsewhere in - * its super-tree, and we would have to convert each XML object - * created at runtime for such sub-trees back into a string, and - * concatenate and re-parse anyway. - */ - if ((pn->pn_extra & (PNX_XMLROOT | PNX_CANTFOLD)) == PNX_XMLROOT && - !(tc->flags & TCF_HAS_DEFXMLNS)) { - JSObject *obj; - JSAtom *atom; - - obj = js_ParseNodeToXMLObject(cx, pn); - if (!obj) - return JS_FALSE; - atom = js_AtomizeObject(cx, obj, 0); - if (!atom) - return JS_FALSE; - pn->pn_op = JSOP_XMLOBJECT; - pn->pn_arity = PN_NULLARY; - pn->pn_atom = atom; - return JS_TRUE; - } - - /* - * Can't fold from parse node to XML tree -- try folding strings - * as much as possible, and folding XML sub-trees bottom up to - * minimize string concatenation and ToXML/ToXMLList operations - * at runtime. - */ - break; - - default:; - } -#endif - - /* Save the list head in pn1 for later use. */ - for (pn1 = pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - if (!js_FoldConstants(cx, pn2, tc)) - return JS_FALSE; - } - break; - - case PN_TERNARY: - /* Any kid may be null (e.g. for (;;)). */ - pn1 = pn->pn_kid1; - pn2 = pn->pn_kid2; - pn3 = pn->pn_kid3; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - if (pn2 && !js_FoldConstants(cx, pn2, tc)) - return JS_FALSE; - if (pn3 && !js_FoldConstants(cx, pn3, tc)) - return JS_FALSE; - break; - - case PN_BINARY: - /* First kid may be null (for default case in switch). */ - pn1 = pn->pn_left; - pn2 = pn->pn_right; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - if (!js_FoldConstants(cx, pn2, tc)) - return JS_FALSE; - break; - - case PN_UNARY: - /* Our kid may be null (e.g. return; vs. return e;). */ - pn1 = pn->pn_kid; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - break; - - case PN_NAME: - /* - * Skip pn1 down along a chain of dotted member expressions to avoid - * excessive recursion. Our only goal here is to fold constants (if - * any) in the primary expression operand to the left of the first - * dot in the chain. - */ - pn1 = pn->pn_expr; - while (pn1 && pn1->pn_arity == PN_NAME) - pn1 = pn1->pn_expr; - if (pn1 && !js_FoldConstants(cx, pn1, tc)) - return JS_FALSE; - break; - - case PN_NULLARY: - break; - } - - switch (pn->pn_type) { - case TOK_IF: - if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR)) - break; - /* FALL THROUGH */ - - case TOK_HOOK: - /* Reduce 'if (C) T; else E' into T for true C, E for false. */ - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - switch (pn1->pn_type) { - case TOK_NUMBER: - if (pn1->pn_dval == 0) - pn2 = pn3; - break; - case TOK_STRING: - if (JSSTRING_LENGTH(ATOM_TO_STRING(pn1->pn_atom)) == 0) - pn2 = pn3; - break; - case TOK_PRIMARY: - if (pn1->pn_op == JSOP_TRUE) - break; - if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) { - pn2 = pn3; - break; - } - /* FALL THROUGH */ - default: - /* Early return to dodge common code that copies pn2 to pn. */ - return JS_TRUE; - } - - if (pn2) { - /* - * pn2 is the then- or else-statement subtree to compile. Take - * care not to expose an object initialiser, which would be parsed - * as a block, to the Statement parser via eval(uneval(e)) where e - * is '1 ? {p:2, q:3}[i] : r;' or the like. - */ - if (pn->pn_type == TOK_HOOK && StartsWith(pn2, TOK_RC)) { - pn->pn_type = TOK_RP; - pn->pn_arity = PN_UNARY; - pn->pn_kid = pn2; - } else { - PN_MOVE_NODE(pn, pn2); - } - } - if (!pn2 || (pn->pn_type == TOK_SEMI && !pn->pn_kid)) { - /* - * False condition and no else, or an empty then-statement was - * moved up over pn. Either way, make pn an empty block (not an - * empty statement, which does not decompile, even when labeled). - * NB: pn must be a TOK_IF as TOK_HOOK can never have a null kid - * or an empty statement for a child. - */ - pn->pn_type = TOK_LC; - pn->pn_arity = PN_LIST; - PN_INIT_LIST(pn); - } - RecycleTree(pn2, tc); - if (pn3 && pn3 != pn2) - RecycleTree(pn3, tc); - break; - - case TOK_ASSIGN: - /* - * Compound operators such as *= should be subject to folding, in case - * the left-hand side is constant, and so that the decompiler produces - * the same string that you get from decompiling a script or function - * compiled from that same string. As with +, += is special. - */ - if (pn->pn_op == JSOP_NOP) - break; - if (pn->pn_op != JSOP_ADD) - goto do_binary_op; - /* FALL THROUGH */ - - case TOK_PLUS: - if (pn->pn_arity == PN_LIST) { - size_t length, length2; - jschar *chars; - JSString *str, *str2; - - /* - * Any string literal term with all others number or string means - * this is a concatenation. If any term is not a string or number - * literal, we can't fold. - */ - JS_ASSERT(pn->pn_count > 2); - if (pn->pn_extra & PNX_CANTFOLD) - return JS_TRUE; - if (pn->pn_extra != PNX_STRCAT) - goto do_binary_op; - - /* Ok, we're concatenating: convert non-string constant operands. */ - length = 0; - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - if (!FoldType(cx, pn2, TOK_STRING)) - return JS_FALSE; - /* XXX fold only if all operands convert to string */ - if (pn2->pn_type != TOK_STRING) - return JS_TRUE; - length += ATOM_TO_STRING(pn2->pn_atom)->length; - } - - /* Allocate a new buffer and string descriptor for the result. */ - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - - /* Fill the buffer, advancing chars and recycling kids as we go. */ - for (pn2 = pn1; pn2; pn2 = RecycleTree(pn2, tc)) { - str2 = ATOM_TO_STRING(pn2->pn_atom); - length2 = str2->length; - js_strncpy(chars, str2->chars, length2); - chars += length2; - } - *chars = 0; - - /* Atomize the result string and mutate pn to refer to it. */ - pn->pn_atom = js_AtomizeString(cx, str, 0); - if (!pn->pn_atom) - return JS_FALSE; - pn->pn_type = TOK_STRING; - pn->pn_op = JSOP_STRING; - pn->pn_arity = PN_NULLARY; - break; - } - - /* Handle a binary string concatenation. */ - JS_ASSERT(pn->pn_arity == PN_BINARY); - if (pn1->pn_type == TOK_STRING || pn2->pn_type == TOK_STRING) { - JSString *left, *right, *str; - - if (!FoldType(cx, (pn1->pn_type != TOK_STRING) ? pn1 : pn2, - TOK_STRING)) { - return JS_FALSE; - } - if (pn1->pn_type != TOK_STRING || pn2->pn_type != TOK_STRING) - return JS_TRUE; - left = ATOM_TO_STRING(pn1->pn_atom); - right = ATOM_TO_STRING(pn2->pn_atom); - str = js_ConcatStrings(cx, left, right); - if (!str) - return JS_FALSE; - pn->pn_atom = js_AtomizeString(cx, str, 0); - if (!pn->pn_atom) - return JS_FALSE; - pn->pn_type = TOK_STRING; - pn->pn_op = JSOP_STRING; - pn->pn_arity = PN_NULLARY; - RecycleTree(pn1, tc); - RecycleTree(pn2, tc); - break; - } - - /* Can't concatenate string literals, let's try numbers. */ - goto do_binary_op; - - case TOK_STAR: - /* The * in 'import *;' parses as a nullary star node. */ - if (pn->pn_arity == PN_NULLARY) - break; - /* FALL THROUGH */ - - case TOK_SHOP: - case TOK_MINUS: - case TOK_DIVOP: - do_binary_op: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_count > 2); - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - if (!FoldType(cx, pn2, TOK_NUMBER)) - return JS_FALSE; - } - for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { - /* XXX fold only if all operands convert to number */ - if (pn2->pn_type != TOK_NUMBER) - break; - } - if (!pn2) { - JSOp op = pn->pn_op; - - pn2 = pn1->pn_next; - pn3 = pn2->pn_next; - if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc)) - return JS_FALSE; - while ((pn2 = pn3) != NULL) { - pn3 = pn2->pn_next; - if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc)) - return JS_FALSE; - } - } - } else { - JS_ASSERT(pn->pn_arity == PN_BINARY); - if (!FoldType(cx, pn1, TOK_NUMBER) || - !FoldType(cx, pn2, TOK_NUMBER)) { - return JS_FALSE; - } - if (pn1->pn_type == TOK_NUMBER && pn2->pn_type == TOK_NUMBER) { - if (!FoldBinaryNumeric(cx, pn->pn_op, pn1, pn2, pn, tc)) - return JS_FALSE; - } - } - break; - - case TOK_UNARYOP: - while (pn1->pn_type == TOK_RP) - pn1 = pn1->pn_kid; - if (pn1->pn_type == TOK_NUMBER) { - jsdouble d; - int32 i; - - /* Operate on one numeric constant. */ - d = pn1->pn_dval; - switch (pn->pn_op) { - case JSOP_BITNOT: - if (!js_DoubleToECMAInt32(cx, d, &i)) - return JS_FALSE; - d = ~i; - break; - - case JSOP_NEG: -#ifdef HPUX - /* - * Negation of a zero doesn't produce a negative - * zero on HPUX. Perform the operation by bit - * twiddling. - */ - JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; -#else - d = -d; -#endif - break; - - case JSOP_POS: - break; - - case JSOP_NOT: - pn->pn_type = TOK_PRIMARY; - pn->pn_op = (d == 0) ? JSOP_TRUE : JSOP_FALSE; - pn->pn_arity = PN_NULLARY; - /* FALL THROUGH */ - - default: - /* Return early to dodge the common TOK_NUMBER code. */ - return JS_TRUE; - } - pn->pn_type = TOK_NUMBER; - pn->pn_op = JSOP_NUMBER; - pn->pn_arity = PN_NULLARY; - pn->pn_dval = d; - RecycleTree(pn1, tc); - } - break; - -#if JS_HAS_XML_SUPPORT - case TOK_XMLELEM: - case TOK_XMLLIST: - case TOK_XMLPTAGC: - case TOK_XMLSTAGO: - case TOK_XMLETAGO: - case TOK_XMLNAME: - if (pn->pn_arity == PN_LIST) { - JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0); - if (!FoldXMLConstants(cx, pn, tc)) - return JS_FALSE; - } - break; - - case TOK_AT: - if (pn1->pn_type == TOK_XMLNAME) { - jsval v; - JSAtom *atom; - - v = ATOM_KEY(pn1->pn_atom); - if (!js_ToAttributeName(cx, &v)) - return JS_FALSE; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - atom = js_AtomizeObject(cx, JSVAL_TO_OBJECT(v), 0); - if (!atom) - return JS_FALSE; - - pn->pn_type = TOK_XMLNAME; - pn->pn_op = JSOP_OBJECT; - pn->pn_arity = PN_NULLARY; - pn->pn_atom = atom; - RecycleTree(pn1, tc); - } - break; -#endif /* JS_HAS_XML_SUPPORT */ - - default:; - } - - return JS_TRUE; -} diff --git a/spidermonkey/libjs/jsparse.h b/spidermonkey/libjs/jsparse.h deleted file mode 100644 index 7c23927..0000000 --- a/spidermonkey/libjs/jsparse.h +++ /dev/null @@ -1,438 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsparse_h___ -#define jsparse_h___ -/* - * JS parser definitions. - */ -#include "jsconfig.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jsscan.h" - -JS_BEGIN_EXTERN_C - -/* - * Parsing builds a tree of nodes that directs code generation. This tree is - * not a concrete syntax tree in all respects (for example, || and && are left - * associative, but (A && B && C) translates into the right-associated tree - * > so that code generation can emit a left-associative branch - * around when A is false). Nodes are labeled by token type, with a - * JSOp secondary label when needed: - * - * Label Variant Members - * ----- ------- ------- - * - * TOK_FUNCTION func pn_funAtom: atom holding function object containing - * arg and var properties. We create the function - * object at parse (not emit) time to specialize arg - * and var bytecodes early. - * pn_body: TOK_LC node for function body statements - * pn_flags: TCF_FUN_* flags (see jsemit.h) collected - * while parsing the function's body - * pn_tryCount: of try statements in function - * - * - * TOK_LC list pn_head: list of pn_count statements - * TOK_EXPORT list pn_head: list of pn_count TOK_NAMEs or one TOK_STAR - * (which is not a multiply node) - * TOK_IMPORT list pn_head: list of pn_count sub-trees of the form - * a.b.*, a[b].*, a.*, a.b, or a[b] -- but never a. - * Each member is expressed with TOK_DOT or TOK_LB. - * Each sub-tree's root node has a pn_op in the set - * JSOP_IMPORT{ALL,PROP,ELEM} - * TOK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null - * TOK_SWITCH binary pn_left: discriminant - * pn_right: list of TOK_CASE nodes, with at most one - * TOK_DEFAULT node, or if there are let bindings - * in the top level of the switch body's cases, a - * TOK_LEXICALSCOPE node that contains the list of - * TOK_CASE nodes. - * TOK_CASE, binary pn_left: case expr or null if TOK_DEFAULT - * TOK_DEFAULT pn_right: TOK_LC node for this case's statements - * pn_val: constant value if lookup or table switch - * TOK_WHILE binary pn_left: cond, pn_right: body - * TOK_DO binary pn_left: body, pn_right: cond - * TOK_FOR binary pn_left: either - * for/in loop: a binary TOK_IN node with - * pn_left: TOK_VAR or TOK_NAME to left of 'in' - * if TOK_VAR, its pn_extra may have PNX_POPVAR - * and PNX_FORINVAR bits set - * pn_right: object expr to right of 'in' - * for(;;) loop: a ternary TOK_RESERVED node with - * pn_kid1: init expr before first ';' - * pn_kid2: cond expr before second ';' - * pn_kid3: update expr after second ';' - * any kid may be null - * pn_right: body - * TOK_THROW unary pn_op: JSOP_THROW, pn_kid: exception - * TOK_TRY ternary pn_kid1: try block - * pn_kid2: null or TOK_RESERVED list of - * TOK_LEXICALSCOPE nodes, each with pn_expr pointing - * to a TOK_CATCH node - * pn_kid3: null or finally block - * TOK_CATCH ternary pn_kid1: TOK_NAME, TOK_RB, or TOK_RC catch var node - * (TOK_RB or TOK_RC if destructuring) - * pn_kid2: null or the catch guard expression - * pn_kid3: catch block statements - * TOK_BREAK name pn_atom: label or null - * TOK_CONTINUE name pn_atom: label or null - * TOK_WITH binary pn_left: head expr, pn_right: body - * TOK_VAR list pn_head: list of pn_count TOK_NAME nodes - * each name node has - * pn_atom: variable name - * pn_expr: initializer or null - * TOK_RETURN unary pn_kid: return expr or null - * TOK_SEMI unary pn_kid: expr or null statement - * TOK_COLON name pn_atom: label, pn_expr: labeled statement - * - * - * All left-associated binary trees of the same type are optimized into lists - * to avoid recursion when processing expression chains. - * TOK_COMMA list pn_head: list of pn_count comma-separated exprs - * TOK_ASSIGN binary pn_left: lvalue, pn_right: rvalue - * pn_op: JSOP_ADD for +=, etc. - * TOK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else - * TOK_OR binary pn_left: first in || chain, pn_right: rest of chain - * TOK_AND binary pn_left: first in && chain, pn_right: rest of chain - * TOK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr - * TOK_BITXOR binary pn_left: left-assoc ^ expr, pn_right: & expr - * TOK_BITAND binary pn_left: left-assoc & expr, pn_right: EQ expr - * TOK_EQOP binary pn_left: left-assoc EQ expr, pn_right: REL expr - * pn_op: JSOP_EQ, JSOP_NE, JSOP_NEW_EQ, JSOP_NEW_NE - * TOK_RELOP binary pn_left: left-assoc REL expr, pn_right: SH expr - * pn_op: JSOP_LT, JSOP_LE, JSOP_GT, JSOP_GE - * TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr - * pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH - * TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr - * pn_extra: if a left-associated binary TOK_PLUS - * tree has been flattened into a list (see above - * under ), pn_extra will contain - * PNX_STRCAT if at least one list element is a - * string literal (TOK_STRING); if such a list has - * any non-string, non-number term, pn_extra will - * contain PNX_CANTFOLD. - * pn_ - * TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB - * TOK_STAR, binary pn_left: left-assoc MUL expr, pn_right: UNARY expr - * TOK_DIVOP pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD - * TOK_UNARYOP unary pn_kid: UNARY expr, pn_op: JSOP_NEG, JSOP_POS, - * JSOP_NOT, JSOP_BITNOT, JSOP_TYPEOF, JSOP_VOID - * TOK_INC, unary pn_kid: MEMBER expr - * TOK_DEC - * TOK_NEW list pn_head: list of ctor, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * ctor is a MEMBER expr - * TOK_DELETE unary pn_kid: MEMBER expr - * TOK_DOT, name pn_expr: MEMBER expr to left of . - * TOK_DBLDOT pn_atom: name to right of . - * TOK_LB binary pn_left: MEMBER expr to left of [ - * pn_right: expr between [ and ] - * TOK_LP list pn_head: list of call, arg1, arg2, ... argN - * pn_count: 1 + N (where N is number of args) - * call is a MEMBER expr naming a callable object - * TOK_RB list pn_head: list of pn_count array element exprs - * [,,] holes are represented by TOK_COMMA nodes - * #n=[...] produces TOK_DEFSHARP at head of list - * pn_extra: PN_ENDCOMMA if extra comma at end - * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where - * each has pn_left: property id, pn_right: value - * #n={...} produces TOK_DEFSHARP at head of list - * TOK_DEFSHARP unary pn_num: jsint value of n in #n= - * pn_kid: null for #n=[...] and #n={...}, primary - * if #n=primary for function, paren, name, object - * literal expressions - * TOK_USESHARP nullary pn_num: jsint value of n in #n# - * TOK_RP unary pn_kid: parenthesized expression - * TOK_NAME, name pn_atom: name, string, or object atom - * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or - * JSOP_REGEXP - * TOK_OBJECT If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR - * with pn_slot >= 0 and pn_attrs telling const-ness - * TOK_NUMBER dval pn_dval: double value of numeric literal - * TOK_PRIMARY nullary pn_op: JSOp bytecode - * - * - * TOK_ANYNAME nullary pn_op: JSOP_ANYNAME - * pn_atom: cx->runtime->atomState.starAtom - * TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr - * TOK_DBLCOLON binary pn_op: JSOP_QNAME - * pn_left: TOK_ANYNAME or TOK_NAME node - * pn_right: TOK_STRING "*" node, or expr within [] - * name pn_op: JSOP_QNAMECONST - * pn_expr: TOK_ANYNAME or TOK_NAME left operand - * pn_atom: name on right of :: - * TOK_XMLELEM list XML element node - * pn_head: start tag, content1, ... contentN, end tag - * pn_count: 2 + N where N is number of content nodes - * N may be > x.length() if {expr} embedded - * TOK_XMLLIST list XML list node - * pn_head: content1, ... contentN - * TOK_XMLSTAGO, list XML start, end, and point tag contents - * TOK_XMLETAGC, pn_head: tag name or {expr}, ... XML attrs ... - * TOK_XMLPTAGO - * TOK_XMLNAME nullary pn_atom: XML name, with no {expr} embedded - * TOK_XMLNAME list pn_head: tag name or {expr}, ... name or {expr} - * TOK_XMLATTR, nullary pn_atom: attribute value string; pn_op: JSOP_STRING - * TOK_XMLCDATA, - * TOK_XMLCOMMENT - * TOK_XMLPI nullary pn_atom: XML processing instruction target - * pn_atom2: XML PI content, or null if no content - * TOK_XMLTEXT nullary pn_atom: marked-up text, or null if empty string - * TOK_LC unary {expr} in XML tag or content; pn_kid is expr - * - * So an XML tag with no {expr} and three attributes is a list with the form: - * - * (tagname attrname1 attrvalue1 attrname2 attrvalue2 attrname2 attrvalue3) - * - * An XML tag with embedded expressions like so: - * - * - * - * would have the form: - * - * ((name1 {expr1}) (name2 {expr2} name3) {expr3}) - * - * where () bracket a list with elements separated by spaces, and {expr} is a - * TOK_LC unary node with expr as its kid. - * - * Thus, the attribute name/value pairs occupy successive odd and even list - * locations, where pn_head is the TOK_XMLNAME node at list location 0. The - * parser builds the same sort of structures for elements: - * - * Hi there!How are you?{x + y} - * - * translates to: - * - * ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y})) - * - * - * - * Label Variant Members - * ----- ------- ------- - * TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR - * pn_atom: block object - * pn_expr: block body - * TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements - * if pn_count is 2, first element is #n=[...] - * last element is block enclosing for loop(s) - * and optionally if-guarded TOK_ARRAYPUSH - * pn_extra: stack slot, used during code gen - * TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP - * pn_kid: array comprehension expression - */ -typedef enum JSParseNodeArity { - PN_FUNC = -3, - PN_LIST = -2, - PN_TERNARY = 3, - PN_BINARY = 2, - PN_UNARY = 1, - PN_NAME = -1, - PN_NULLARY = 0 -} JSParseNodeArity; - -struct JSParseNode { - uint16 pn_type; - uint8 pn_op; - int8 pn_arity; - JSTokenPos pn_pos; - ptrdiff_t pn_offset; /* first generated bytecode offset */ - union { - struct { /* TOK_FUNCTION node */ - JSAtom *funAtom; /* atomized function object */ - JSParseNode *body; /* TOK_LC list of statements */ - uint32 flags; /* accumulated tree context flags */ - uint32 tryCount; /* count of try statements in body */ - } func; - struct { /* list of next-linked nodes */ - JSParseNode *head; /* first node in list */ - JSParseNode **tail; /* ptr to ptr to last node in list */ - uint32 count; /* number of nodes in list */ - uint32 extra; /* extra flags, see below */ - } list; - struct { /* ternary: if, for(;;), ?: */ - JSParseNode *kid1; /* condition, discriminant, etc. */ - JSParseNode *kid2; /* then-part, case list, etc. */ - JSParseNode *kid3; /* else-part, default case, etc. */ - } ternary; - struct { /* two kids if binary */ - JSParseNode *left; - JSParseNode *right; - jsval val; /* switch case value */ - } binary; - struct { /* one kid if unary */ - JSParseNode *kid; - jsint num; /* -1 or sharp variable number */ - } unary; - struct { /* name, labeled statement, etc. */ - JSAtom *atom; /* name or label atom, null if slot */ - JSParseNode *expr; /* object or initializer */ - jsint slot; /* -1 or arg or local var slot */ - uintN attrs; /* attributes if local var or const */ - } name; - struct { - JSAtom *atom; /* first atom in pair */ - JSAtom *atom2; /* second atom in pair or null */ - } apair; - jsdouble dval; /* aligned numeric literal value */ - } pn_u; - JSParseNode *pn_next; /* to align dval and pn_u on RISCs */ - JSTokenStream *pn_ts; /* token stream for error reports */ - JSAtom *pn_source; /* saved source for decompilation */ -}; - -#define pn_funAtom pn_u.func.funAtom -#define pn_body pn_u.func.body -#define pn_flags pn_u.func.flags -#define pn_tryCount pn_u.func.tryCount -#define pn_head pn_u.list.head -#define pn_tail pn_u.list.tail -#define pn_count pn_u.list.count -#define pn_extra pn_u.list.extra -#define pn_kid1 pn_u.ternary.kid1 -#define pn_kid2 pn_u.ternary.kid2 -#define pn_kid3 pn_u.ternary.kid3 -#define pn_left pn_u.binary.left -#define pn_right pn_u.binary.right -#define pn_val pn_u.binary.val -#define pn_kid pn_u.unary.kid -#define pn_num pn_u.unary.num -#define pn_atom pn_u.name.atom -#define pn_expr pn_u.name.expr -#define pn_slot pn_u.name.slot -#define pn_attrs pn_u.name.attrs -#define pn_dval pn_u.dval -#define pn_atom2 pn_u.apair.atom2 - -/* PN_LIST pn_extra flags. */ -#define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */ -#define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */ -#define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */ -#define PNX_FORINVAR 0x08 /* TOK_VAR is left kid of TOK_IN node, - which is left kid of TOK_FOR */ -#define PNX_ENDCOMMA 0x10 /* array literal has comma at end */ -#define PNX_XMLROOT 0x20 /* top-most node in XML literal tree */ -#define PNX_GROUPINIT 0x40 /* var [a, b] = [c, d]; unit list */ -#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */ - -/* - * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off - * any kids in pn2->pn_u, by clearing pn2. - */ -#define PN_MOVE_NODE(pn, pn2) \ - JS_BEGIN_MACRO \ - (pn)->pn_type = (pn2)->pn_type; \ - (pn)->pn_op = (pn2)->pn_op; \ - (pn)->pn_arity = (pn2)->pn_arity; \ - (pn)->pn_u = (pn2)->pn_u; \ - PN_CLEAR_NODE(pn2); \ - JS_END_MACRO - -#define PN_CLEAR_NODE(pn) \ - JS_BEGIN_MACRO \ - (pn)->pn_type = TOK_EOF; \ - (pn)->pn_op = JSOP_NOP; \ - (pn)->pn_arity = PN_NULLARY; \ - JS_END_MACRO - -/* True if pn is a parsenode representing a literal constant. */ -#define PN_IS_CONSTANT(pn) \ - ((pn)->pn_type == TOK_NUMBER || \ - (pn)->pn_type == TOK_STRING || \ - ((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS)) - -/* - * Compute a pointer to the last JSParseNode element in a singly-linked list. - * NB: list must be non-empty for correct PN_LAST usage! - */ -#define PN_LAST(list) \ - ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next))) - -#define PN_INIT_LIST(list) \ - JS_BEGIN_MACRO \ - (list)->pn_head = NULL; \ - (list)->pn_tail = &(list)->pn_head; \ - (list)->pn_count = (list)->pn_extra = 0; \ - JS_END_MACRO - -#define PN_INIT_LIST_1(list, pn) \ - JS_BEGIN_MACRO \ - (list)->pn_head = (pn); \ - (list)->pn_tail = &(pn)->pn_next; \ - (list)->pn_count = 1; \ - (list)->pn_extra = 0; \ - JS_END_MACRO - -#define PN_APPEND(list, pn) \ - JS_BEGIN_MACRO \ - *(list)->pn_tail = (pn); \ - (list)->pn_tail = &(pn)->pn_next; \ - (list)->pn_count++; \ - JS_END_MACRO - -/* - * Parse a top-level JS script. - * - * The caller must prevent the GC from running while this function is active, - * because atoms and function newborns are not rooted yet. - */ -extern JS_FRIEND_API(JSParseNode *) -js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts); - -extern JS_FRIEND_API(JSBool) -js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSCodeGenerator *cg); - -extern JSBool -js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun); - -extern JSBool -js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); - -#if JS_HAS_XML_SUPPORT -JS_FRIEND_API(JSParseNode *) -js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, - JSBool allowList); -#endif - -JS_END_EXTERN_C - -#endif /* jsparse_h___ */ diff --git a/spidermonkey/libjs/jsprf.c b/spidermonkey/libjs/jsprf.c deleted file mode 100644 index 6735520..0000000 --- a/spidermonkey/libjs/jsprf.c +++ /dev/null @@ -1,1266 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -** Portable safe sprintf code. -** -** Author: Kipp E.B. Hickman -*/ -#include "jsstddef.h" -#include -#include -#include -#include -#include "jsprf.h" -#include "jslong.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jspubtd.h" -#include "jsstr.h" - -/* -** Note: on some platforms va_list is defined as an array, -** and requires array notation. -*/ -#ifdef HAVE_VA_COPY -#define VARARGS_ASSIGN(foo, bar) VA_COPY(foo,bar) -#elif defined(va_copy) -#define VARARGS_ASSIGN(foo, bar) va_copy(foo,bar) -#elif defined(HAVE_VA_LIST_AS_ARRAY) -#define VARARGS_ASSIGN(foo, bar) foo[0] = bar[0] -#else -#define VARARGS_ASSIGN(foo, bar) (foo) = (bar) -#endif - -/* -** WARNING: This code may *NOT* call JS_LOG (because JS_LOG calls it) -*/ - -/* -** XXX This needs to be internationalized! -*/ - -typedef struct SprintfStateStr SprintfState; - -struct SprintfStateStr { - int (*stuff)(SprintfState *ss, const char *sp, JSUint32 len); - - char *base; - char *cur; - JSUint32 maxlen; - - int (*func)(void *arg, const char *sp, JSUint32 len); - void *arg; -}; - -/* -** Numbered Arguement State -*/ -struct NumArgState{ - int type; /* type of the current ap */ - va_list ap; /* point to the corresponding position on ap */ -}; - -#define NAS_DEFAULT_NUM 20 /* default number of NumberedArgumentState array */ - - -#define TYPE_INT16 0 -#define TYPE_UINT16 1 -#define TYPE_INTN 2 -#define TYPE_UINTN 3 -#define TYPE_INT32 4 -#define TYPE_UINT32 5 -#define TYPE_INT64 6 -#define TYPE_UINT64 7 -#define TYPE_STRING 8 -#define TYPE_DOUBLE 9 -#define TYPE_INTSTR 10 -#define TYPE_WSTRING 11 -#define TYPE_UNKNOWN 20 - -#define FLAG_LEFT 0x1 -#define FLAG_SIGNED 0x2 -#define FLAG_SPACED 0x4 -#define FLAG_ZEROS 0x8 -#define FLAG_NEG 0x10 - -/* -** Fill into the buffer using the data in src -*/ -static int fill2(SprintfState *ss, const char *src, int srclen, int width, - int flags) -{ - char space = ' '; - int rv; - - width -= srclen; - if ((width > 0) && ((flags & FLAG_LEFT) == 0)) { /* Right adjusting */ - if (flags & FLAG_ZEROS) { - space = '0'; - } - while (--width >= 0) { - rv = (*ss->stuff)(ss, &space, 1); - if (rv < 0) { - return rv; - } - } - } - - /* Copy out the source data */ - rv = (*ss->stuff)(ss, src, (JSUint32)srclen); - if (rv < 0) { - return rv; - } - - if ((width > 0) && ((flags & FLAG_LEFT) != 0)) { /* Left adjusting */ - while (--width >= 0) { - rv = (*ss->stuff)(ss, &space, 1); - if (rv < 0) { - return rv; - } - } - } - return 0; -} - -/* -** Fill a number. The order is: optional-sign zero-filling conversion-digits -*/ -static int fill_n(SprintfState *ss, const char *src, int srclen, int width, - int prec, int type, int flags) -{ - int zerowidth = 0; - int precwidth = 0; - int signwidth = 0; - int leftspaces = 0; - int rightspaces = 0; - int cvtwidth; - int rv; - char sign; - - if ((type & 1) == 0) { - if (flags & FLAG_NEG) { - sign = '-'; - signwidth = 1; - } else if (flags & FLAG_SIGNED) { - sign = '+'; - signwidth = 1; - } else if (flags & FLAG_SPACED) { - sign = ' '; - signwidth = 1; - } - } - cvtwidth = signwidth + srclen; - - if (prec > 0) { - if (prec > srclen) { - precwidth = prec - srclen; /* Need zero filling */ - cvtwidth += precwidth; - } - } - - if ((flags & FLAG_ZEROS) && (prec < 0)) { - if (width > cvtwidth) { - zerowidth = width - cvtwidth; /* Zero filling */ - cvtwidth += zerowidth; - } - } - - if (flags & FLAG_LEFT) { - if (width > cvtwidth) { - /* Space filling on the right (i.e. left adjusting) */ - rightspaces = width - cvtwidth; - } - } else { - if (width > cvtwidth) { - /* Space filling on the left (i.e. right adjusting) */ - leftspaces = width - cvtwidth; - } - } - while (--leftspaces >= 0) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - if (signwidth) { - rv = (*ss->stuff)(ss, &sign, 1); - if (rv < 0) { - return rv; - } - } - while (--precwidth >= 0) { - rv = (*ss->stuff)(ss, "0", 1); - if (rv < 0) { - return rv; - } - } - while (--zerowidth >= 0) { - rv = (*ss->stuff)(ss, "0", 1); - if (rv < 0) { - return rv; - } - } - rv = (*ss->stuff)(ss, src, (JSUint32)srclen); - if (rv < 0) { - return rv; - } - while (--rightspaces >= 0) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - return 0; -} - -/* -** Convert a long into its printable form -*/ -static int cvt_l(SprintfState *ss, long num, int width, int prec, int radix, - int type, int flags, const char *hexp) -{ - char cvtbuf[100]; - char *cvt; - int digits; - - /* according to the man page this needs to happen */ - if ((prec == 0) && (num == 0)) { - return 0; - } - - /* - ** Converting decimal is a little tricky. In the unsigned case we - ** need to stop when we hit 10 digits. In the signed case, we can - ** stop when the number is zero. - */ - cvt = cvtbuf + sizeof(cvtbuf); - digits = 0; - while (num) { - int digit = (((unsigned long)num) % radix) & 0xF; - *--cvt = hexp[digit]; - digits++; - num = (long)(((unsigned long)num) / radix); - } - if (digits == 0) { - *--cvt = '0'; - digits++; - } - - /* - ** Now that we have the number converted without its sign, deal with - ** the sign and zero padding. - */ - return fill_n(ss, cvt, digits, width, prec, type, flags); -} - -/* -** Convert a 64-bit integer into its printable form -*/ -static int cvt_ll(SprintfState *ss, JSInt64 num, int width, int prec, int radix, - int type, int flags, const char *hexp) -{ - char cvtbuf[100]; - char *cvt; - int digits; - JSInt64 rad; - - /* according to the man page this needs to happen */ - if ((prec == 0) && (JSLL_IS_ZERO(num))) { - return 0; - } - - /* - ** Converting decimal is a little tricky. In the unsigned case we - ** need to stop when we hit 10 digits. In the signed case, we can - ** stop when the number is zero. - */ - JSLL_I2L(rad, radix); - cvt = cvtbuf + sizeof(cvtbuf); - digits = 0; - while (!JSLL_IS_ZERO(num)) { - JSInt32 digit; - JSInt64 quot, rem; - JSLL_UDIVMOD(", &rem, num, rad); - JSLL_L2I(digit, rem); - *--cvt = hexp[digit & 0xf]; - digits++; - num = quot; - } - if (digits == 0) { - *--cvt = '0'; - digits++; - } - - /* - ** Now that we have the number converted without its sign, deal with - ** the sign and zero padding. - */ - return fill_n(ss, cvt, digits, width, prec, type, flags); -} - -/* -** Convert a double precision floating point number into its printable -** form. -** -** XXX stop using sprintf to convert floating point -*/ -static int cvt_f(SprintfState *ss, double d, const char *fmt0, const char *fmt1) -{ - char fin[20]; - char fout[300]; - int amount = fmt1 - fmt0; - - JS_ASSERT((amount > 0) && (amount < (int)sizeof(fin))); - if (amount >= (int)sizeof(fin)) { - /* Totally bogus % command to sprintf. Just ignore it */ - return 0; - } - memcpy(fin, fmt0, (size_t)amount); - fin[amount] = 0; - - /* Convert floating point using the native sprintf code */ -#ifdef DEBUG - { - const char *p = fin; - while (*p) { - JS_ASSERT(*p != 'L'); - p++; - } - } -#endif - sprintf(fout, fin, d); - - /* - ** This assert will catch overflow's of fout, when building with - ** debugging on. At least this way we can track down the evil piece - ** of calling code and fix it! - */ - JS_ASSERT(strlen(fout) < sizeof(fout)); - - return (*ss->stuff)(ss, fout, strlen(fout)); -} - -/* -** Convert a string into its printable form. "width" is the output -** width. "prec" is the maximum number of characters of "s" to output, -** where -1 means until NUL. -*/ -static int cvt_s(SprintfState *ss, const char *s, int width, int prec, - int flags) -{ - int slen; - - if (prec == 0) - return 0; - - /* Limit string length by precision value */ - slen = s ? strlen(s) : 6; - if (prec > 0) { - if (prec < slen) { - slen = prec; - } - } - - /* and away we go */ - return fill2(ss, s ? s : "(null)", slen, width, flags); -} - -static int cvt_ws(SprintfState *ss, const jschar *ws, int width, int prec, - int flags) -{ - int result; - /* - * Supply NULL as the JSContext; errors are not reported, - * and malloc() is used to allocate the buffer buffer. - */ - if (ws) { - int slen = js_strlen(ws); - char *s = js_DeflateString(NULL, ws, slen); - if (!s) - return -1; /* JSStuffFunc error indicator. */ - result = cvt_s(ss, s, width, prec, flags); - free(s); - } else { - result = cvt_s(ss, NULL, width, prec, flags); - } - return result; -} - -/* -** BuildArgArray stands for Numbered Argument list Sprintf -** for example, -** fmp = "%4$i, %2$d, %3s, %1d"; -** the number must start from 1, and no gap among them -*/ - -static struct NumArgState* BuildArgArray( const char *fmt, va_list ap, int* rv, struct NumArgState* nasArray ) -{ - int number = 0, cn = 0, i; - const char *p; - char c; - struct NumArgState *nas; - - - /* - ** first pass: - ** detemine how many legal % I have got, then allocate space - */ - - p = fmt; - *rv = 0; - i = 0; - while( ( c = *p++ ) != 0 ){ - if( c != '%' ) - continue; - if( ( c = *p++ ) == '%' ) /* skip %% case */ - continue; - - while( c != 0 ){ - if( c > '9' || c < '0' ){ - if( c == '$' ){ /* numbered argument csae */ - if( i > 0 ){ - *rv = -1; - return NULL; - } - number++; - } else { /* non-numbered argument case */ - if( number > 0 ){ - *rv = -1; - return NULL; - } - i = 1; - } - break; - } - - c = *p++; - } - } - - if( number == 0 ){ - return NULL; - } - - - if( number > NAS_DEFAULT_NUM ){ - nas = (struct NumArgState*)malloc( number * sizeof( struct NumArgState ) ); - if( !nas ){ - *rv = -1; - return NULL; - } - } else { - nas = nasArray; - } - - for( i = 0; i < number; i++ ){ - nas[i].type = TYPE_UNKNOWN; - } - - - /* - ** second pass: - ** set nas[].type - */ - - p = fmt; - while( ( c = *p++ ) != 0 ){ - if( c != '%' ) continue; - c = *p++; - if( c == '%' ) continue; - - cn = 0; - while( c && c != '$' ){ /* should improve error check later */ - cn = cn*10 + c - '0'; - c = *p++; - } - - if( !c || cn < 1 || cn > number ){ - *rv = -1; - break; - } - - /* nas[cn] starts from 0, and make sure nas[cn].type is not assigned */ - cn--; - if( nas[cn].type != TYPE_UNKNOWN ) - continue; - - c = *p++; - - /* width */ - if (c == '*') { - /* not supported feature, for the argument is not numbered */ - *rv = -1; - break; - } - - while ((c >= '0') && (c <= '9')) { - c = *p++; - } - - /* precision */ - if (c == '.') { - c = *p++; - if (c == '*') { - /* not supported feature, for the argument is not numbered */ - *rv = -1; - break; - } - - while ((c >= '0') && (c <= '9')) { - c = *p++; - } - } - - /* size */ - nas[cn].type = TYPE_INTN; - if (c == 'h') { - nas[cn].type = TYPE_INT16; - c = *p++; - } else if (c == 'L') { - /* XXX not quite sure here */ - nas[cn].type = TYPE_INT64; - c = *p++; - } else if (c == 'l') { - nas[cn].type = TYPE_INT32; - c = *p++; - if (c == 'l') { - nas[cn].type = TYPE_INT64; - c = *p++; - } - } - - /* format */ - switch (c) { - case 'd': - case 'c': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - break; - - case 'e': - case 'f': - case 'g': - nas[ cn ].type = TYPE_DOUBLE; - break; - - case 'p': - /* XXX should use cpp */ - if (sizeof(void *) == sizeof(JSInt32)) { - nas[ cn ].type = TYPE_UINT32; - } else if (sizeof(void *) == sizeof(JSInt64)) { - nas[ cn ].type = TYPE_UINT64; - } else if (sizeof(void *) == sizeof(JSIntn)) { - nas[ cn ].type = TYPE_UINTN; - } else { - nas[ cn ].type = TYPE_UNKNOWN; - } - break; - - case 'C': - case 'S': - case 'E': - case 'G': - /* XXX not supported I suppose */ - JS_ASSERT(0); - nas[ cn ].type = TYPE_UNKNOWN; - break; - - case 's': - nas[ cn ].type = (nas[ cn ].type == TYPE_UINT16) ? TYPE_WSTRING : TYPE_STRING; - break; - - case 'n': - nas[ cn ].type = TYPE_INTSTR; - break; - - default: - JS_ASSERT(0); - nas[ cn ].type = TYPE_UNKNOWN; - break; - } - - /* get a legal para. */ - if( nas[ cn ].type == TYPE_UNKNOWN ){ - *rv = -1; - break; - } - } - - - /* - ** third pass - ** fill the nas[cn].ap - */ - - if( *rv < 0 ){ - if( nas != nasArray ) - free( nas ); - return NULL; - } - - cn = 0; - while( cn < number ){ - if( nas[cn].type == TYPE_UNKNOWN ){ - cn++; - continue; - } - - VARARGS_ASSIGN(nas[cn].ap, ap); - - switch( nas[cn].type ){ - case TYPE_INT16: - case TYPE_UINT16: - case TYPE_INTN: - case TYPE_UINTN: (void)va_arg( ap, JSIntn ); break; - - case TYPE_INT32: (void)va_arg( ap, JSInt32 ); break; - - case TYPE_UINT32: (void)va_arg( ap, JSUint32 ); break; - - case TYPE_INT64: (void)va_arg( ap, JSInt64 ); break; - - case TYPE_UINT64: (void)va_arg( ap, JSUint64 ); break; - - case TYPE_STRING: (void)va_arg( ap, char* ); break; - - case TYPE_WSTRING: (void)va_arg( ap, jschar* ); break; - - case TYPE_INTSTR: (void)va_arg( ap, JSIntn* ); break; - - case TYPE_DOUBLE: (void)va_arg( ap, double ); break; - - default: - if( nas != nasArray ) - free( nas ); - *rv = -1; - return NULL; - } - - cn++; - } - - - return nas; -} - -/* -** The workhorse sprintf code. -*/ -static int dosprintf(SprintfState *ss, const char *fmt, va_list ap) -{ - char c; - int flags, width, prec, radix, type; - union { - char ch; - jschar wch; - int i; - long l; - JSInt64 ll; - double d; - const char *s; - const jschar* ws; - int *ip; - } u; - const char *fmt0; - static char *hex = "0123456789abcdef"; - static char *HEX = "0123456789ABCDEF"; - char *hexp; - int rv, i; - struct NumArgState *nas = NULL; - struct NumArgState nasArray[ NAS_DEFAULT_NUM ]; - char pattern[20]; - const char *dolPt = NULL; /* in "%4$.2f", dolPt will poiont to . */ -#ifdef JS_C_STRINGS_ARE_UTF8 - char utf8buf[6]; - int utf8len; -#endif - - /* - ** build an argument array, IF the fmt is numbered argument - ** list style, to contain the Numbered Argument list pointers - */ - - nas = BuildArgArray( fmt, ap, &rv, nasArray ); - if( rv < 0 ){ - /* the fmt contains error Numbered Argument format, jliu@netscape.com */ - JS_ASSERT(0); - return rv; - } - - while ((c = *fmt++) != 0) { - if (c != '%') { - rv = (*ss->stuff)(ss, fmt - 1, 1); - if (rv < 0) { - return rv; - } - continue; - } - fmt0 = fmt - 1; - - /* - ** Gobble up the % format string. Hopefully we have handled all - ** of the strange cases! - */ - flags = 0; - c = *fmt++; - if (c == '%') { - /* quoting a % with %% */ - rv = (*ss->stuff)(ss, fmt - 1, 1); - if (rv < 0) { - return rv; - } - continue; - } - - if( nas != NULL ){ - /* the fmt contains the Numbered Arguments feature */ - i = 0; - while( c && c != '$' ){ /* should imporve error check later */ - i = ( i * 10 ) + ( c - '0' ); - c = *fmt++; - } - - if( nas[i-1].type == TYPE_UNKNOWN ){ - if( nas && ( nas != nasArray ) ) - free( nas ); - return -1; - } - - ap = nas[i-1].ap; - dolPt = fmt; - c = *fmt++; - } - - /* - * Examine optional flags. Note that we do not implement the - * '#' flag of sprintf(). The ANSI C spec. of the '#' flag is - * somewhat ambiguous and not ideal, which is perhaps why - * the various sprintf() implementations are inconsistent - * on this feature. - */ - while ((c == '-') || (c == '+') || (c == ' ') || (c == '0')) { - if (c == '-') flags |= FLAG_LEFT; - if (c == '+') flags |= FLAG_SIGNED; - if (c == ' ') flags |= FLAG_SPACED; - if (c == '0') flags |= FLAG_ZEROS; - c = *fmt++; - } - if (flags & FLAG_SIGNED) flags &= ~FLAG_SPACED; - if (flags & FLAG_LEFT) flags &= ~FLAG_ZEROS; - - /* width */ - if (c == '*') { - c = *fmt++; - width = va_arg(ap, int); - } else { - width = 0; - while ((c >= '0') && (c <= '9')) { - width = (width * 10) + (c - '0'); - c = *fmt++; - } - } - - /* precision */ - prec = -1; - if (c == '.') { - c = *fmt++; - if (c == '*') { - c = *fmt++; - prec = va_arg(ap, int); - } else { - prec = 0; - while ((c >= '0') && (c <= '9')) { - prec = (prec * 10) + (c - '0'); - c = *fmt++; - } - } - } - - /* size */ - type = TYPE_INTN; - if (c == 'h') { - type = TYPE_INT16; - c = *fmt++; - } else if (c == 'L') { - /* XXX not quite sure here */ - type = TYPE_INT64; - c = *fmt++; - } else if (c == 'l') { - type = TYPE_INT32; - c = *fmt++; - if (c == 'l') { - type = TYPE_INT64; - c = *fmt++; - } - } - - /* format */ - hexp = hex; - switch (c) { - case 'd': case 'i': /* decimal/integer */ - radix = 10; - goto fetch_and_convert; - - case 'o': /* octal */ - radix = 8; - type |= 1; - goto fetch_and_convert; - - case 'u': /* unsigned decimal */ - radix = 10; - type |= 1; - goto fetch_and_convert; - - case 'x': /* unsigned hex */ - radix = 16; - type |= 1; - goto fetch_and_convert; - - case 'X': /* unsigned HEX */ - radix = 16; - hexp = HEX; - type |= 1; - goto fetch_and_convert; - - fetch_and_convert: - switch (type) { - case TYPE_INT16: - u.l = va_arg(ap, int); - if (u.l < 0) { - u.l = -u.l; - flags |= FLAG_NEG; - } - goto do_long; - case TYPE_UINT16: - u.l = va_arg(ap, int) & 0xffff; - goto do_long; - case TYPE_INTN: - u.l = va_arg(ap, int); - if (u.l < 0) { - u.l = -u.l; - flags |= FLAG_NEG; - } - goto do_long; - case TYPE_UINTN: - u.l = (long)va_arg(ap, unsigned int); - goto do_long; - - case TYPE_INT32: - u.l = va_arg(ap, JSInt32); - if (u.l < 0) { - u.l = -u.l; - flags |= FLAG_NEG; - } - goto do_long; - case TYPE_UINT32: - u.l = (long)va_arg(ap, JSUint32); - do_long: - rv = cvt_l(ss, u.l, width, prec, radix, type, flags, hexp); - if (rv < 0) { - return rv; - } - break; - - case TYPE_INT64: - u.ll = va_arg(ap, JSInt64); - if (!JSLL_GE_ZERO(u.ll)) { - JSLL_NEG(u.ll, u.ll); - flags |= FLAG_NEG; - } - goto do_longlong; - case TYPE_UINT64: - u.ll = va_arg(ap, JSUint64); - do_longlong: - rv = cvt_ll(ss, u.ll, width, prec, radix, type, flags, hexp); - if (rv < 0) { - return rv; - } - break; - } - break; - - case 'e': - case 'E': - case 'f': - case 'g': - u.d = va_arg(ap, double); - if( nas != NULL ){ - i = fmt - dolPt; - if( i < (int)sizeof( pattern ) ){ - pattern[0] = '%'; - memcpy( &pattern[1], dolPt, (size_t)i ); - rv = cvt_f(ss, u.d, pattern, &pattern[i+1] ); - } - } else - rv = cvt_f(ss, u.d, fmt0, fmt); - - if (rv < 0) { - return rv; - } - break; - - case 'c': - if ((flags & FLAG_LEFT) == 0) { - while (width-- > 1) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - } - switch (type) { - case TYPE_INT16: - /* Treat %hc as %c if JS_C_STRINGS_ARE_UTF8 is undefined. */ -#ifdef JS_C_STRINGS_ARE_UTF8 - u.wch = va_arg(ap, int); - utf8len = js_OneUcs4ToUtf8Char (utf8buf, u.wch); - rv = (*ss->stuff)(ss, utf8buf, utf8len); - break; -#endif - case TYPE_INTN: - u.ch = va_arg(ap, int); - rv = (*ss->stuff)(ss, &u.ch, 1); - break; - } - if (rv < 0) { - return rv; - } - if (flags & FLAG_LEFT) { - while (width-- > 1) { - rv = (*ss->stuff)(ss, " ", 1); - if (rv < 0) { - return rv; - } - } - } - break; - - case 'p': - if (sizeof(void *) == sizeof(JSInt32)) { - type = TYPE_UINT32; - } else if (sizeof(void *) == sizeof(JSInt64)) { - type = TYPE_UINT64; - } else if (sizeof(void *) == sizeof(int)) { - type = TYPE_UINTN; - } else { - JS_ASSERT(0); - break; - } - radix = 16; - goto fetch_and_convert; - -#if 0 - case 'C': - case 'S': - case 'E': - case 'G': - /* XXX not supported I suppose */ - JS_ASSERT(0); - break; -#endif - - case 's': - if(type == TYPE_INT16) { - /* - * This would do a simple string/byte conversion - * if JS_C_STRINGS_ARE_UTF8 is not defined. - */ - u.ws = va_arg(ap, const jschar*); - rv = cvt_ws(ss, u.ws, width, prec, flags); - } else { - u.s = va_arg(ap, const char*); - rv = cvt_s(ss, u.s, width, prec, flags); - } - if (rv < 0) { - return rv; - } - break; - - case 'n': - u.ip = va_arg(ap, int*); - if (u.ip) { - *u.ip = ss->cur - ss->base; - } - break; - - default: - /* Not a % token after all... skip it */ -#if 0 - JS_ASSERT(0); -#endif - rv = (*ss->stuff)(ss, "%", 1); - if (rv < 0) { - return rv; - } - rv = (*ss->stuff)(ss, fmt - 1, 1); - if (rv < 0) { - return rv; - } - } - } - - /* Stuff trailing NUL */ - rv = (*ss->stuff)(ss, "\0", 1); - - if( nas && ( nas != nasArray ) ){ - free( nas ); - } - - return rv; -} - -/************************************************************************/ - -static int FuncStuff(SprintfState *ss, const char *sp, JSUint32 len) -{ - int rv; - - rv = (*ss->func)(ss->arg, sp, len); - if (rv < 0) { - return rv; - } - ss->maxlen += len; - return 0; -} - -JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc func, void *arg, - const char *fmt, ...) -{ - va_list ap; - int rv; - - va_start(ap, fmt); - rv = JS_vsxprintf(func, arg, fmt, ap); - va_end(ap); - return rv; -} - -JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc func, void *arg, - const char *fmt, va_list ap) -{ - SprintfState ss; - int rv; - - ss.stuff = FuncStuff; - ss.func = func; - ss.arg = arg; - ss.maxlen = 0; - rv = dosprintf(&ss, fmt, ap); - return (rv < 0) ? (JSUint32)-1 : ss.maxlen; -} - -/* -** Stuff routine that automatically grows the malloc'd output buffer -** before it overflows. -*/ -static int GrowStuff(SprintfState *ss, const char *sp, JSUint32 len) -{ - ptrdiff_t off; - char *newbase; - JSUint32 newlen; - - off = ss->cur - ss->base; - if (off + len >= ss->maxlen) { - /* Grow the buffer */ - newlen = ss->maxlen + ((len > 32) ? len : 32); - if (ss->base) { - newbase = (char*) realloc(ss->base, newlen); - } else { - newbase = (char*) malloc(newlen); - } - if (!newbase) { - /* Ran out of memory */ - return -1; - } - ss->base = newbase; - ss->maxlen = newlen; - ss->cur = ss->base + off; - } - - /* Copy data */ - while (len) { - --len; - *ss->cur++ = *sp++; - } - JS_ASSERT((JSUint32)(ss->cur - ss->base) <= ss->maxlen); - return 0; -} - -/* -** sprintf into a malloc'd buffer -*/ -JS_PUBLIC_API(char *) JS_smprintf(const char *fmt, ...) -{ - va_list ap; - char *rv; - - va_start(ap, fmt); - rv = JS_vsmprintf(fmt, ap); - va_end(ap); - return rv; -} - -/* -** Free memory allocated, for the caller, by JS_smprintf -*/ -JS_PUBLIC_API(void) JS_smprintf_free(char *mem) -{ - free(mem); -} - -JS_PUBLIC_API(char *) JS_vsmprintf(const char *fmt, va_list ap) -{ - SprintfState ss; - int rv; - - ss.stuff = GrowStuff; - ss.base = 0; - ss.cur = 0; - ss.maxlen = 0; - rv = dosprintf(&ss, fmt, ap); - if (rv < 0) { - if (ss.base) { - free(ss.base); - } - return 0; - } - return ss.base; -} - -/* -** Stuff routine that discards overflow data -*/ -static int LimitStuff(SprintfState *ss, const char *sp, JSUint32 len) -{ - JSUint32 limit = ss->maxlen - (ss->cur - ss->base); - - if (len > limit) { - len = limit; - } - while (len) { - --len; - *ss->cur++ = *sp++; - } - return 0; -} - -/* -** sprintf into a fixed size buffer. Make sure there is a NUL at the end -** when finished. -*/ -JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...) -{ - va_list ap; - int rv; - - JS_ASSERT((JSInt32)outlen > 0); - if ((JSInt32)outlen <= 0) { - return 0; - } - - va_start(ap, fmt); - rv = JS_vsnprintf(out, outlen, fmt, ap); - va_end(ap); - return rv; -} - -JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen,const char *fmt, - va_list ap) -{ - SprintfState ss; - JSUint32 n; - - JS_ASSERT((JSInt32)outlen > 0); - if ((JSInt32)outlen <= 0) { - return 0; - } - - ss.stuff = LimitStuff; - ss.base = out; - ss.cur = out; - ss.maxlen = outlen; - (void) dosprintf(&ss, fmt, ap); - - /* If we added chars, and we didn't append a null, do it now. */ - if( (ss.cur != ss.base) && (ss.cur[-1] != '\0') ) - ss.cur[-1] = '\0'; - - n = ss.cur - ss.base; - return n ? n - 1 : n; -} - -JS_PUBLIC_API(char *) JS_sprintf_append(char *last, const char *fmt, ...) -{ - va_list ap; - char *rv; - - va_start(ap, fmt); - rv = JS_vsprintf_append(last, fmt, ap); - va_end(ap); - return rv; -} - -JS_PUBLIC_API(char *) JS_vsprintf_append(char *last, const char *fmt, va_list ap) -{ - SprintfState ss; - int rv; - - ss.stuff = GrowStuff; - if (last) { - int lastlen = strlen(last); - ss.base = last; - ss.cur = last + lastlen; - ss.maxlen = lastlen; - } else { - ss.base = 0; - ss.cur = 0; - ss.maxlen = 0; - } - rv = dosprintf(&ss, fmt, ap); - if (rv < 0) { - if (ss.base) { - free(ss.base); - } - return 0; - } - return ss.base; -} - diff --git a/spidermonkey/libjs/jsprf.h b/spidermonkey/libjs/jsprf.h deleted file mode 100644 index 0eb910f..0000000 --- a/spidermonkey/libjs/jsprf.h +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsprf_h___ -#define jsprf_h___ - -/* -** API for PR printf like routines. Supports the following formats -** %d - decimal -** %u - unsigned decimal -** %x - unsigned hex -** %X - unsigned uppercase hex -** %o - unsigned octal -** %hd, %hu, %hx, %hX, %ho - 16-bit versions of above -** %ld, %lu, %lx, %lX, %lo - 32-bit versions of above -** %lld, %llu, %llx, %llX, %llo - 64 bit versions of above -** %s - string -** %hs - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) -** %c - character -** %hc - 16-bit version of above (only available if compiled with JS_C_STRINGS_ARE_UTF8) -** %p - pointer (deals with machine dependent pointer size) -** %f - float -** %g - float -*/ -#include "jstypes.h" -#include -#include - -JS_BEGIN_EXTERN_C - -/* -** sprintf into a fixed size buffer. Guarantees that a NUL is at the end -** of the buffer. Returns the length of the written output, NOT including -** the NUL, or (JSUint32)-1 if an error occurs. -*/ -extern JS_PUBLIC_API(JSUint32) JS_snprintf(char *out, JSUint32 outlen, const char *fmt, ...); - -/* -** sprintf into a malloc'd buffer. Return a pointer to the malloc'd -** buffer on success, NULL on failure. Call "JS_smprintf_free" to release -** the memory returned. -*/ -extern JS_PUBLIC_API(char*) JS_smprintf(const char *fmt, ...); - -/* -** Free the memory allocated, for the caller, by JS_smprintf -*/ -extern JS_PUBLIC_API(void) JS_smprintf_free(char *mem); - -/* -** "append" sprintf into a malloc'd buffer. "last" is the last value of -** the malloc'd buffer. sprintf will append data to the end of last, -** growing it as necessary using realloc. If last is NULL, JS_sprintf_append -** will allocate the initial string. The return value is the new value of -** last for subsequent calls, or NULL if there is a malloc failure. -*/ -extern JS_PUBLIC_API(char*) JS_sprintf_append(char *last, const char *fmt, ...); - -/* -** sprintf into a function. The function "f" is called with a string to -** place into the output. "arg" is an opaque pointer used by the stuff -** function to hold any state needed to do the storage of the output -** data. The return value is a count of the number of characters fed to -** the stuff function, or (JSUint32)-1 if an error occurs. -*/ -typedef JSIntn (*JSStuffFunc)(void *arg, const char *s, JSUint32 slen); - -extern JS_PUBLIC_API(JSUint32) JS_sxprintf(JSStuffFunc f, void *arg, const char *fmt, ...); - -/* -** va_list forms of the above. -*/ -extern JS_PUBLIC_API(JSUint32) JS_vsnprintf(char *out, JSUint32 outlen, const char *fmt, va_list ap); -extern JS_PUBLIC_API(char*) JS_vsmprintf(const char *fmt, va_list ap); -extern JS_PUBLIC_API(char*) JS_vsprintf_append(char *last, const char *fmt, va_list ap); -extern JS_PUBLIC_API(JSUint32) JS_vsxprintf(JSStuffFunc f, void *arg, const char *fmt, va_list ap); - -/* -*************************************************************************** -** FUNCTION: JS_sscanf -** DESCRIPTION: -** JS_sscanf() scans the input character string, performs data -** conversions, and stores the converted values in the data objects -** pointed to by its arguments according to the format control -** string. -** -** JS_sscanf() behaves the same way as the sscanf() function in the -** Standard C Library (stdio.h), with the following exceptions: -** - JS_sscanf() handles the NSPR integer and floating point types, -** such as JSInt16, JSInt32, JSInt64, and JSFloat64, whereas -** sscanf() handles the standard C types like short, int, long, -** and double. -** - JS_sscanf() has no multibyte character support, while sscanf() -** does. -** INPUTS: -** const char *buf -** a character string holding the input to scan -** const char *fmt -** the format control string for the conversions -** ... -** variable number of arguments, each of them is a pointer to -** a data object in which the converted value will be stored -** OUTPUTS: none -** RETURNS: JSInt32 -** The number of values converted and stored. -** RESTRICTIONS: -** Multibyte characters in 'buf' or 'fmt' are not allowed. -*************************************************************************** -*/ - -extern JS_PUBLIC_API(JSInt32) JS_sscanf(const char *buf, const char *fmt, ...); - -JS_END_EXTERN_C - -#endif /* jsprf_h___ */ diff --git a/spidermonkey/libjs/jsproto.tbl b/spidermonkey/libjs/jsproto.tbl deleted file mode 100644 index 18f2355..0000000 --- a/spidermonkey/libjs/jsproto.tbl +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=80 ft=c: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey 1.7 work in progress, released - * February 14, 2006. - * - * The Initial Developer of the Original Code is - * Brendan Eich - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsconfig.h" - -#if JS_HAS_SCRIPT_OBJECT -# define SCRIPT_INIT js_InitScriptClass -#else -# define SCRIPT_INIT js_InitNullClass -#endif - -#if JS_HAS_XML_SUPPORT -# define XML_INIT js_InitXMLClass -# define NAMESPACE_INIT js_InitNamespaceClass -# define QNAME_INIT js_InitQNameClass -# define ANYNAME_INIT js_InitAnyNameClass -# define ATTRIBUTE_INIT js_InitAttributeNameClass -#else -# define XML_INIT js_InitNullClass -# define NAMESPACE_INIT js_InitNullClass -# define QNAME_INIT js_InitNullClass -# define ANYNAME_INIT js_InitNullClass -# define ATTRIBUTE_INIT js_InitNullClass -#endif - -#if JS_HAS_GENERATORS -# define GENERATOR_INIT js_InitIteratorClasses -#else -# define GENERATOR_INIT js_InitNullClass -#endif - -#if JS_HAS_FILE_OBJECT -# define FILE_INIT js_InitFileClass -#else -# define FILE_INIT js_InitNullClass -#endif - -/* - * Enumerator codes in the second column must not change -- they are part of - * the JS XDR API. - */ -JS_PROTO(Null, 0, js_InitNullClass) -JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses) -JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses) -JS_PROTO(Array, 3, js_InitArrayClass) -JS_PROTO(Boolean, 4, js_InitBooleanClass) -JS_PROTO(Call, 5, js_InitCallClass) -JS_PROTO(Date, 6, js_InitDateClass) -JS_PROTO(Math, 7, js_InitMathClass) -JS_PROTO(Number, 8, js_InitNumberClass) -JS_PROTO(String, 9, js_InitStringClass) -JS_PROTO(RegExp, 10, js_InitRegExpClass) -JS_PROTO(Script, 11, SCRIPT_INIT) -JS_PROTO(XML, 12, XML_INIT) -JS_PROTO(Namespace, 13, NAMESPACE_INIT) -JS_PROTO(QName, 14, QNAME_INIT) -JS_PROTO(AnyName, 15, ANYNAME_INIT) -JS_PROTO(AttributeName, 16, ATTRIBUTE_INIT) -JS_PROTO(Error, 17, js_InitExceptionClasses) -JS_PROTO(InternalError, 18, js_InitExceptionClasses) -JS_PROTO(EvalError, 19, js_InitExceptionClasses) -JS_PROTO(RangeError, 20, js_InitExceptionClasses) -JS_PROTO(ReferenceError, 21, js_InitExceptionClasses) -JS_PROTO(SyntaxError, 22, js_InitExceptionClasses) -JS_PROTO(TypeError, 23, js_InitExceptionClasses) -JS_PROTO(URIError, 24, js_InitExceptionClasses) -JS_PROTO(Generator, 25, GENERATOR_INIT) -JS_PROTO(Iterator, 26, js_InitIteratorClasses) -JS_PROTO(StopIteration, 27, js_InitIteratorClasses) -JS_PROTO(UnusedProto28, 28, js_InitNullClass) -JS_PROTO(File, 29, FILE_INIT) -JS_PROTO(Block, 30, js_InitBlockClass) - -#undef SCRIPT_INIT -#undef XML_INIT -#undef NAMESPACE_INIT -#undef QNAME_INIT -#undef ANYNAME_INIT -#undef ATTRIBUTE_INIT -#undef GENERATOR_INIT -#undef FILE_INIT diff --git a/spidermonkey/libjs/jsprvtd.h b/spidermonkey/libjs/jsprvtd.h deleted file mode 100644 index f71b9a5..0000000 --- a/spidermonkey/libjs/jsprvtd.h +++ /dev/null @@ -1,202 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsprvtd_h___ -#define jsprvtd_h___ -/* - * JS private typename definitions. - * - * This header is included only in other .h files, for convenience and for - * simplicity of type naming. The alternative for structures is to use tags, - * which are named the same as their typedef names (legal in C/C++, and less - * noisy than suffixing the typedef name with "Struct" or "Str"). Instead, - * all .h files that include this file may use the same typedef name, whether - * declaring a pointer to struct type, or defining a member of struct type. - * - * A few fundamental scalar types are defined here too. Neither the scalar - * nor the struct typedefs should change much, therefore the nearly-global - * make dependency induced by this file should not prove painful. - */ - -#include "jspubtd.h" - -/* Internal identifier (jsid) macros. */ -#define JSID_ATOM 0x0 -#define JSID_INT 0x1 -#define JSID_OBJECT 0x2 -#define JSID_TAGMASK 0x3 -#define JSID_TAG(id) ((id) & JSID_TAGMASK) -#define JSID_SETTAG(id,t) ((id) | (t)) -#define JSID_CLRTAG(id) ((id) & ~(jsid)JSID_TAGMASK) - -#define JSID_IS_ATOM(id) (JSID_TAG(id) == JSID_ATOM) -#define JSID_TO_ATOM(id) ((JSAtom *)(id)) -#define ATOM_TO_JSID(atom) ((jsid)(atom)) -#define ATOM_JSID_TO_JSVAL(id) ATOM_KEY(JSID_TO_ATOM(id)) - -#define JSID_IS_INT(id) ((id) & JSID_INT) -#define JSID_TO_INT(id) ((jsint)(id) >> 1) -#define INT_TO_JSID(i) (((jsint)(i) << 1) | JSID_INT) -#define INT_JSID_TO_JSVAL(id) (id) -#define INT_JSVAL_TO_JSID(v) (v) - -#define JSID_IS_OBJECT(id) (JSID_TAG(id) == JSID_OBJECT) -#define JSID_TO_OBJECT(id) ((JSObject *) JSID_CLRTAG(id)) -#define OBJECT_TO_JSID(obj) ((jsid)(obj) | JSID_OBJECT) -#define OBJECT_JSID_TO_JSVAL(id) OBJECT_TO_JSVAL(JSID_CLRTAG(id)) -#define OBJECT_JSVAL_TO_JSID(v) OBJECT_TO_JSID(JSVAL_TO_OBJECT(v)) - -/* Scalar typedefs. */ -typedef uint8 jsbytecode; -typedef uint8 jssrcnote; -typedef uint32 jsatomid; - -/* Struct typedefs. */ -typedef struct JSArgumentFormatMap JSArgumentFormatMap; -typedef struct JSCodeGenerator JSCodeGenerator; -typedef struct JSDependentString JSDependentString; -typedef struct JSGCThing JSGCThing; -typedef struct JSGenerator JSGenerator; -typedef struct JSParseNode JSParseNode; -typedef struct JSSharpObjectMap JSSharpObjectMap; -typedef struct JSThread JSThread; -typedef struct JSToken JSToken; -typedef struct JSTokenPos JSTokenPos; -typedef struct JSTokenPtr JSTokenPtr; -typedef struct JSTokenStream JSTokenStream; -typedef struct JSTreeContext JSTreeContext; -typedef struct JSTryNote JSTryNote; - -/* Friend "Advanced API" typedefs. */ -typedef struct JSAtom JSAtom; -typedef struct JSAtomList JSAtomList; -typedef struct JSAtomListElement JSAtomListElement; -typedef struct JSAtomMap JSAtomMap; -typedef struct JSAtomState JSAtomState; -typedef struct JSCodeSpec JSCodeSpec; -typedef struct JSPrinter JSPrinter; -typedef struct JSRegExp JSRegExp; -typedef struct JSRegExpStatics JSRegExpStatics; -typedef struct JSScope JSScope; -typedef struct JSScopeOps JSScopeOps; -typedef struct JSScopeProperty JSScopeProperty; -typedef struct JSStackHeader JSStackHeader; -typedef struct JSStringBuffer JSStringBuffer; -typedef struct JSSubString JSSubString; -typedef struct JSXML JSXML; -typedef struct JSXMLNamespace JSXMLNamespace; -typedef struct JSXMLQName JSXMLQName; -typedef struct JSXMLArray JSXMLArray; -typedef struct JSXMLArrayCursor JSXMLArrayCursor; - -/* "Friend" types used by jscntxt.h and jsdbgapi.h. */ -typedef enum JSTrapStatus { - JSTRAP_ERROR, - JSTRAP_CONTINUE, - JSTRAP_RETURN, - JSTRAP_THROW, - JSTRAP_LIMIT -} JSTrapStatus; - -typedef JSTrapStatus -(* JS_DLL_CALLBACK JSTrapHandler)(JSContext *cx, JSScript *script, - jsbytecode *pc, jsval *rval, void *closure); - -typedef JSBool -(* JS_DLL_CALLBACK JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, - jsval old, jsval *newp, void *closure); - -/* called just after script creation */ -typedef void -(* JS_DLL_CALLBACK JSNewScriptHook)(JSContext *cx, - const char *filename, /* URL of script */ - uintN lineno, /* first line */ - JSScript *script, - JSFunction *fun, - void *callerdata); - -/* called just before script destruction */ -typedef void -(* JS_DLL_CALLBACK JSDestroyScriptHook)(JSContext *cx, - JSScript *script, - void *callerdata); - -typedef void -(* JS_DLL_CALLBACK JSSourceHandler)(const char *filename, uintN lineno, - jschar *str, size_t length, - void **listenerTSData, void *closure); - -/* - * This hook captures high level script execution and function calls (JS or - * native). It is used by JS_SetExecuteHook to hook top level scripts and by - * JS_SetCallHook to hook function calls. It will get called twice per script - * or function call: just before execution begins and just after it finishes. - * In both cases the 'current' frame is that of the executing code. - * - * The 'before' param is JS_TRUE for the hook invocation before the execution - * and JS_FALSE for the invocation after the code has run. - * - * The 'ok' param is significant only on the post execution invocation to - * signify whether or not the code completed 'normally'. - * - * The 'closure' param is as passed to JS_SetExecuteHook or JS_SetCallHook - * for the 'before'invocation, but is whatever value is returned from that - * invocation for the 'after' invocation. Thus, the hook implementor *could* - * allocate a structure in the 'before' invocation and return a pointer to that - * structure. The pointer would then be handed to the hook for the 'after' - * invocation. Alternately, the 'before' could just return the same value as - * in 'closure' to cause the 'after' invocation to be called with the same - * 'closure' value as the 'before'. - * - * Returning NULL in the 'before' hook will cause the 'after' hook *not* to - * be called. - */ -typedef void * -(* JS_DLL_CALLBACK JSInterpreterHook)(JSContext *cx, JSStackFrame *fp, JSBool before, - JSBool *ok, void *closure); - -typedef void -(* JS_DLL_CALLBACK JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, - void *closure); - -typedef JSBool -(* JS_DLL_CALLBACK JSDebugErrorHook)(JSContext *cx, const char *message, - JSErrorReport *report, void *closure); - -#endif /* jsprvtd_h___ */ diff --git a/spidermonkey/libjs/jspubtd.h b/spidermonkey/libjs/jspubtd.h deleted file mode 100644 index 4e8c92a..0000000 --- a/spidermonkey/libjs/jspubtd.h +++ /dev/null @@ -1,667 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jspubtd_h___ -#define jspubtd_h___ -/* - * JS public API typedefs. - */ -#include "jstypes.h" -#include "jscompat.h" - -JS_BEGIN_EXTERN_C - -/* Scalar typedefs. */ -typedef uint16 jschar; -typedef int32 jsint; -typedef uint32 jsuint; -typedef float64 jsdouble; -typedef jsword jsval; -typedef jsword jsid; -typedef int32 jsrefcount; /* PRInt32 if JS_THREADSAFE, see jslock.h */ - -/* - * Run-time version enumeration. See jsconfig.h for compile-time counterparts - * to these values that may be selected by the JS_VERSION macro, and tested by - * #if expressions. - */ -typedef enum JSVersion { - JSVERSION_1_0 = 100, - JSVERSION_1_1 = 110, - JSVERSION_1_2 = 120, - JSVERSION_1_3 = 130, - JSVERSION_1_4 = 140, - JSVERSION_ECMA_3 = 148, - JSVERSION_1_5 = 150, - JSVERSION_1_6 = 160, - JSVERSION_1_7 = 170, - JSVERSION_DEFAULT = 0, - JSVERSION_UNKNOWN = -1 -} JSVersion; - -#define JSVERSION_IS_ECMA(version) \ - ((version) == JSVERSION_DEFAULT || (version) >= JSVERSION_1_3) - -/* Result of typeof operator enumeration. */ -typedef enum JSType { - JSTYPE_VOID, /* undefined */ - JSTYPE_OBJECT, /* object */ - JSTYPE_FUNCTION, /* function */ - JSTYPE_STRING, /* string */ - JSTYPE_NUMBER, /* number */ - JSTYPE_BOOLEAN, /* boolean */ - JSTYPE_NULL, /* null */ - JSTYPE_XML, /* xml object */ - JSTYPE_LIMIT -} JSType; - -/* Dense index into cached prototypes and class atoms for standard objects. */ -typedef enum JSProtoKey { -#define JS_PROTO(name,code,init) JSProto_##name = code, -#include "jsproto.tbl" -#undef JS_PROTO - JSProto_LIMIT -} JSProtoKey; - -/* JSObjectOps.checkAccess mode enumeration. */ -typedef enum JSAccessMode { - JSACC_PROTO = 0, /* XXXbe redundant w.r.t. id */ - JSACC_PARENT = 1, /* XXXbe redundant w.r.t. id */ - JSACC_IMPORT = 2, /* import foo.bar */ - JSACC_WATCH = 3, /* a watchpoint on object foo for id 'bar' */ - JSACC_READ = 4, /* a "get" of foo.bar */ - JSACC_WRITE = 8, /* a "set" of foo.bar = baz */ - JSACC_LIMIT -} JSAccessMode; - -#define JSACC_TYPEMASK (JSACC_WRITE - 1) - -/* - * This enum type is used to control the behavior of a JSObject property - * iterator function that has type JSNewEnumerate. - */ -typedef enum JSIterateOp { - JSENUMERATE_INIT, /* Create new iterator state */ - JSENUMERATE_NEXT, /* Iterate once */ - JSENUMERATE_DESTROY /* Destroy iterator state */ -} JSIterateOp; - -/* Struct typedefs. */ -typedef struct JSClass JSClass; -typedef struct JSExtendedClass JSExtendedClass; -typedef struct JSConstDoubleSpec JSConstDoubleSpec; -typedef struct JSContext JSContext; -typedef struct JSErrorReport JSErrorReport; -typedef struct JSFunction JSFunction; -typedef struct JSFunctionSpec JSFunctionSpec; -typedef struct JSIdArray JSIdArray; -typedef struct JSProperty JSProperty; -typedef struct JSPropertySpec JSPropertySpec; -typedef struct JSObject JSObject; -typedef struct JSObjectMap JSObjectMap; -typedef struct JSObjectOps JSObjectOps; -typedef struct JSXMLObjectOps JSXMLObjectOps; -typedef struct JSRuntime JSRuntime; -typedef struct JSRuntime JSTaskState; /* XXX deprecated name */ -typedef struct JSScript JSScript; -typedef struct JSStackFrame JSStackFrame; -typedef struct JSString JSString; -typedef struct JSXDRState JSXDRState; -typedef struct JSExceptionState JSExceptionState; -typedef struct JSLocaleCallbacks JSLocaleCallbacks; - -/* JSClass (and JSObjectOps where appropriate) function pointer typedefs. */ - -/* - * Add, delete, get or set a property named by id in obj. Note the jsval id - * type -- id may be a string (Unicode property identifier) or an int (element - * index). The *vp out parameter, on success, is the new property value after - * an add, get, or set. After a successful delete, *vp is JSVAL_FALSE iff - * obj[id] can't be deleted (because it's permanent). - */ -typedef JSBool -(* JS_DLL_CALLBACK JSPropertyOp)(JSContext *cx, JSObject *obj, jsval id, - jsval *vp); - -/* - * This function type is used for callbacks that enumerate the properties of - * a JSObject. The behavior depends on the value of enum_op: - * - * JSENUMERATE_INIT - * A new, opaque iterator state should be allocated and stored in *statep. - * (You can use PRIVATE_TO_JSVAL() to tag the pointer to be stored). - * - * The number of properties that will be enumerated should be returned as - * an integer jsval in *idp, if idp is non-null, and provided the number of - * enumerable properties is known. If idp is non-null and the number of - * enumerable properties can't be computed in advance, *idp should be set - * to JSVAL_ZERO. - * - * JSENUMERATE_NEXT - * A previously allocated opaque iterator state is passed in via statep. - * Return the next jsid in the iteration using *idp. The opaque iterator - * state pointed at by statep is destroyed and *statep is set to JSVAL_NULL - * if there are no properties left to enumerate. - * - * JSENUMERATE_DESTROY - * Destroy the opaque iterator state previously allocated in *statep by a - * call to this function when enum_op was JSENUMERATE_INIT. - * - * The return value is used to indicate success, with a value of JS_FALSE - * indicating failure. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSNewEnumerateOp)(JSContext *cx, JSObject *obj, - JSIterateOp enum_op, - jsval *statep, jsid *idp); - -/* - * The old-style JSClass.enumerate op should define all lazy properties not - * yet reflected in obj. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSEnumerateOp)(JSContext *cx, JSObject *obj); - -/* - * Resolve a lazy property named by id in obj by defining it directly in obj. - * Lazy properties are those reflected from some peer native property space - * (e.g., the DOM attributes for a given node reflected as obj) on demand. - * - * JS looks for a property in an object, and if not found, tries to resolve - * the given id. If resolve succeeds, the engine looks again in case resolve - * defined obj[id]. If no such property exists directly in obj, the process - * is repeated with obj's prototype, etc. - * - * NB: JSNewResolveOp provides a cheaper way to resolve lazy properties. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSResolveOp)(JSContext *cx, JSObject *obj, jsval id); - -/* - * Like JSResolveOp, but flags provide contextual information as follows: - * - * JSRESOLVE_QUALIFIED a qualified property id: obj.id or obj[id], not id - * JSRESOLVE_ASSIGNING obj[id] is on the left-hand side of an assignment - * JSRESOLVE_DETECTING 'if (o.p)...' or similar detection opcode sequence - * JSRESOLVE_DECLARING var, const, or function prolog declaration opcode - * JSRESOLVE_CLASSNAME class name used when constructing - * - * The *objp out parameter, on success, should be null to indicate that id - * was not resolved; and non-null, referring to obj or one of its prototypes, - * if id was resolved. - * - * This hook instead of JSResolveOp is called via the JSClass.resolve member - * if JSCLASS_NEW_RESOLVE is set in JSClass.flags. - * - * Setting JSCLASS_NEW_RESOLVE and JSCLASS_NEW_RESOLVE_GETS_START further - * extends this hook by passing in the starting object on the prototype chain - * via *objp. Thus a resolve hook implementation may define the property id - * being resolved in the object in which the id was first sought, rather than - * in a prototype object whose class led to the resolve hook being called. - * - * When using JSCLASS_NEW_RESOLVE_GETS_START, the resolve hook must therefore - * null *objp to signify "not resolved". With only JSCLASS_NEW_RESOLVE and no - * JSCLASS_NEW_RESOLVE_GETS_START, the hook can assume *objp is null on entry. - * This is not good practice, but enough existing hook implementations count - * on it that we can't break compatibility by passing the starting object in - * *objp without a new JSClass flag. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSNewResolveOp)(JSContext *cx, JSObject *obj, jsval id, - uintN flags, JSObject **objp); - -/* - * Convert obj to the given type, returning true with the resulting value in - * *vp on success, and returning false on error or exception. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, - jsval *vp); - -/* - * Finalize obj, which the garbage collector has determined to be unreachable - * from other live objects or from GC roots. Obviously, finalizers must never - * store a reference to obj. - */ -typedef void -(* JS_DLL_CALLBACK JSFinalizeOp)(JSContext *cx, JSObject *obj); - -/* - * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer - * to extend and reduce the set of string types finalized by the GC. - */ -typedef void -(* JS_DLL_CALLBACK JSStringFinalizeOp)(JSContext *cx, JSString *str); - -/* - * The signature for JSClass.getObjectOps, used by JS_NewObject's internals - * to discover the set of high-level object operations to use for new objects - * of the given class. All native objects have a JSClass, which is stored as - * a private (int-tagged) pointer in obj->slots[JSSLOT_CLASS]. In contrast, - * all native and host objects have a JSObjectMap at obj->map, which may be - * shared among a number of objects, and which contains the JSObjectOps *ops - * pointer used to dispatch object operations from API calls. - * - * Thus JSClass (which pre-dates JSObjectOps in the API) provides a low-level - * interface to class-specific code and data, while JSObjectOps allows for a - * higher level of operation, which does not use the object's class except to - * find the class's JSObjectOps struct, by calling clasp->getObjectOps, and to - * finalize the object. - * - * If this seems backwards, that's because it is! API compatibility requires - * a JSClass *clasp parameter to JS_NewObject, etc. Most host objects do not - * need to implement the larger JSObjectOps, and can share the common JSScope - * code and data used by the native (js_ObjectOps, see jsobj.c) ops. - * - * Further extension to preserve API compatibility: if this function returns - * a pointer to JSXMLObjectOps.base, not to JSObjectOps, then the engine calls - * extended hooks needed for E4X. - */ -typedef JSObjectOps * -(* JS_DLL_CALLBACK JSGetObjectOps)(JSContext *cx, JSClass *clasp); - -/* - * JSClass.checkAccess type: check whether obj[id] may be accessed per mode, - * returning false on error/exception, true on success with obj[id]'s last-got - * value in *vp, and its attributes in *attrsp. As for JSPropertyOp above, id - * is either a string or an int jsval. - * - * See JSCheckAccessIdOp, below, for the JSObjectOps counterpart, which takes - * a jsid (a tagged int or aligned, unique identifier pointer) rather than a - * jsval. The native js_ObjectOps.checkAccess simply forwards to the object's - * clasp->checkAccess, so that both JSClass and JSObjectOps implementors may - * specialize access checks. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsval id, - JSAccessMode mode, jsval *vp); - -/* - * Encode or decode an object, given an XDR state record representing external - * data. See jsxdrapi.h. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSXDRObjectOp)(JSXDRState *xdr, JSObject **objp); - -/* - * Check whether v is an instance of obj. Return false on error or exception, - * true on success with JS_TRUE in *bp if v is an instance of obj, JS_FALSE in - * *bp otherwise. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSHasInstanceOp)(JSContext *cx, JSObject *obj, jsval v, - JSBool *bp); - -/* - * Function type for JSClass.mark and JSObjectOps.mark, called from the GC to - * scan live GC-things reachable from obj's private data structure. For each - * such thing, a mark implementation must call - * - * JS_MarkGCThing(cx, thing, name, arg); - * - * The trailing name and arg parameters are used for GC_MARK_DEBUG-mode heap - * dumping and ref-path tracing. The mark function should pass a (typically - * literal) string naming the private data member for name, and it must pass - * the opaque arg parameter through from its caller. - * - * For the JSObjectOps.mark hook, the return value is the number of slots at - * obj->slots to scan. For JSClass.mark, the return value is ignored. - * - * NB: JSMarkOp implementations cannot allocate new GC-things (JS_NewObject - * called from a mark function will fail silently, e.g.). - */ -typedef uint32 -(* JS_DLL_CALLBACK JSMarkOp)(JSContext *cx, JSObject *obj, void *arg); - -/* - * The optional JSClass.reserveSlots hook allows a class to make computed - * per-instance object slots reservations, in addition to or instead of using - * JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve - * a constant-per-class number of slots. Implementations of this hook should - * return the number of slots to reserve, not including any reserved by using - * JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags. - * - * NB: called with obj locked by the JSObjectOps-specific mutual exclusion - * mechanism appropriate for obj, so don't nest other operations that might - * also lock obj. - */ -typedef uint32 -(* JS_DLL_CALLBACK JSReserveSlotsOp)(JSContext *cx, JSObject *obj); - -/* JSObjectOps function pointer typedefs. */ - -/* - * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops - * members initialized from the same-named parameters, and with the nslots and - * freeslot members initialized according to ops and clasp. Return null on - * error, non-null on success. - * - * JSObjectMaps are reference-counted by generic code in the engine. Usually, - * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref - * returned to the caller on success. After a successful construction, some - * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs - * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will - * be called to dispose of the map. - */ -typedef JSObjectMap * -(* JS_DLL_CALLBACK JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, - JSObjectOps *ops, JSClass *clasp, - JSObject *obj); - -/* - * Generic type for an infallible JSObjectMap operation, used currently by - * JSObjectOps.destroyObjectMap. - */ -typedef void -(* JS_DLL_CALLBACK JSObjectMapOp)(JSContext *cx, JSObjectMap *map); - -/* - * Look for id in obj and its prototype chain, returning false on error or - * exception, true on success. On success, return null in *propp if id was - * not found. If id was found, return the first object searching from obj - * along its prototype chain in which id names a direct property in *objp, and - * return a non-null, opaque property pointer in *propp. - * - * If JSLookupPropOp succeeds and returns with *propp non-null, that pointer - * may be passed as the prop parameter to a JSAttributesOp, as a short-cut - * that bypasses id re-lookup. In any case, a non-null *propp result after a - * successful lookup must be dropped via JSObjectOps.dropProperty. - * - * NB: successful return with non-null *propp means the implementation may - * have locked *objp and added a reference count associated with *propp, so - * callers should not risk deadlock by nesting or interleaving other lookups - * or any obj-bearing ops before dropping *propp. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSLookupPropOp)(JSContext *cx, JSObject *obj, jsid id, - JSObject **objp, JSProperty **propp); - -/* - * Define obj[id], a direct property of obj named id, having the given initial - * value, with the specified getter, setter, and attributes. If the propp out - * param is non-null, *propp on successful return contains an opaque property - * pointer usable as a speedup hint with JSAttributesOp. But note that propp - * may be null, indicating that the caller is not interested in recovering an - * opaque pointer to the newly-defined property. - * - * If propp is non-null and JSDefinePropOp succeeds, its caller must be sure - * to drop *propp using JSObjectOps.dropProperty in short order, just as with - * JSLookupPropOp. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSDefinePropOp)(JSContext *cx, JSObject *obj, - jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, - uintN attrs, JSProperty **propp); - -/* - * Get, set, or delete obj[id], returning false on error or exception, true - * on success. If getting or setting, the new value is returned in *vp on - * success. If deleting without error, *vp will be JSVAL_FALSE if obj[id] is - * permanent, and JSVAL_TRUE if id named a direct property of obj that was in - * fact deleted, or if id names no direct property of obj (id could name a - * prototype property, or no property in obj or its prototype chain). - */ -typedef JSBool -(* JS_DLL_CALLBACK JSPropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, - jsval *vp); - -/* - * Get or set attributes of the property obj[id]. Return false on error or - * exception, true with current attributes in *attrsp. If prop is non-null, - * it must come from the *propp out parameter of a prior JSDefinePropOp or - * JSLookupPropOp call. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSAttributesOp)(JSContext *cx, JSObject *obj, jsid id, - JSProperty *prop, uintN *attrsp); - -/* - * JSObjectOps.checkAccess type: check whether obj[id] may be accessed per - * mode, returning false on error/exception, true on success with obj[id]'s - * last-got value in *vp, and its attributes in *attrsp. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSCheckAccessIdOp)(JSContext *cx, JSObject *obj, jsid id, - JSAccessMode mode, jsval *vp, - uintN *attrsp); - -/* - * A generic type for functions mapping an object to another object, or null - * if an error or exception was thrown on cx. Used by JSObjectOps.thisObject - * at present. - */ -typedef JSObject * -(* JS_DLL_CALLBACK JSObjectOp)(JSContext *cx, JSObject *obj); - -/* - * A generic type for functions taking a context, object, and property, with - * no return value. Used by JSObjectOps.dropProperty currently (see above, - * JSDefinePropOp and JSLookupPropOp, for the object-locking protocol in which - * dropProperty participates). - */ -typedef void -(* JS_DLL_CALLBACK JSPropertyRefOp)(JSContext *cx, JSObject *obj, - JSProperty *prop); - -/* - * Function type for JSObjectOps.setProto and JSObjectOps.setParent. These - * hooks must check for cycles without deadlocking, and otherwise take special - * steps. See jsobj.c, js_SetProtoOrParent, for an example. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSSetObjectSlotOp)(JSContext *cx, JSObject *obj, - uint32 slot, JSObject *pobj); - -/* - * Get and set a required slot, one that should already have been allocated. - * These operations are infallible, so required slots must be pre-allocated, - * or implementations must suppress out-of-memory errors. The native ops - * (js_ObjectOps, see jsobj.c) access slots reserved by including a call to - * the JSCLASS_HAS_RESERVED_SLOTS(n) macro in the JSClass.flags initializer. - * - * NB: the slot parameter is a zero-based index into obj->slots[], unlike the - * index parameter to the JS_GetReservedSlot and JS_SetReservedSlot API entry - * points, which is a zero-based index into the JSCLASS_RESERVED_SLOTS(clasp) - * reserved slots that come after the initial well-known slots: proto, parent, - * class, and optionally, the private data slot. - */ -typedef jsval -(* JS_DLL_CALLBACK JSGetRequiredSlotOp)(JSContext *cx, JSObject *obj, - uint32 slot); - -typedef JSBool -(* JS_DLL_CALLBACK JSSetRequiredSlotOp)(JSContext *cx, JSObject *obj, - uint32 slot, jsval v); - -typedef JSObject * -(* JS_DLL_CALLBACK JSGetMethodOp)(JSContext *cx, JSObject *obj, jsid id, - jsval *vp); - -typedef JSBool -(* JS_DLL_CALLBACK JSSetMethodOp)(JSContext *cx, JSObject *obj, jsid id, - jsval *vp); - -typedef JSBool -(* JS_DLL_CALLBACK JSEnumerateValuesOp)(JSContext *cx, JSObject *obj, - JSIterateOp enum_op, - jsval *statep, jsid *idp, jsval *vp); - -typedef JSBool -(* JS_DLL_CALLBACK JSEqualityOp)(JSContext *cx, JSObject *obj, jsval v, - JSBool *bp); - -typedef JSBool -(* JS_DLL_CALLBACK JSConcatenateOp)(JSContext *cx, JSObject *obj, jsval v, - jsval *vp); - -/* Typedef for native functions called by the JS VM. */ - -typedef JSBool -(* JS_DLL_CALLBACK JSNative)(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval); - -/* Callbacks and their arguments. */ - -typedef enum JSContextOp { - JSCONTEXT_NEW, - JSCONTEXT_DESTROY -} JSContextOp; - -/* - * The possible values for contextOp when the runtime calls the callback are: - * JSCONTEXT_NEW JS_NewContext succesfully created a new JSContext - * instance. The callback can initialize the instance as - * required. If the callback returns false, the instance - * will be destroyed and JS_NewContext returns null. In - * this case the callback is not called again. - * JSCONTEXT_DESTROY One of JS_DestroyContext* methods is called. The - * callback may perform its own cleanup and must always - * return true. - * Any other value For future compatibility the callback must do nothing - * and return true in this case. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSContextCallback)(JSContext *cx, uintN contextOp); - -typedef enum JSGCStatus { - JSGC_BEGIN, - JSGC_END, - JSGC_MARK_END, - JSGC_FINALIZE_END -} JSGCStatus; - -typedef JSBool -(* JS_DLL_CALLBACK JSGCCallback)(JSContext *cx, JSGCStatus status); - -typedef JSBool -(* JS_DLL_CALLBACK JSBranchCallback)(JSContext *cx, JSScript *script); - -typedef void -(* JS_DLL_CALLBACK JSErrorReporter)(JSContext *cx, const char *message, - JSErrorReport *report); - -/* - * Possible exception types. These types are part of a JSErrorFormatString - * structure. They define which error to throw in case of a runtime error. - * JSEXN_NONE marks an unthrowable error. - */ -typedef enum JSExnType { - JSEXN_NONE = -1, - JSEXN_ERR, - JSEXN_INTERNALERR, - JSEXN_EVALERR, - JSEXN_RANGEERR, - JSEXN_REFERENCEERR, - JSEXN_SYNTAXERR, - JSEXN_TYPEERR, - JSEXN_URIERR, - JSEXN_LIMIT -} JSExnType; - -typedef struct JSErrorFormatString { - /* The error format string (UTF-8 if JS_C_STRINGS_ARE_UTF8 is defined). */ - const char *format; - - /* The number of arguments to expand in the formatted error message. */ - uint16 argCount; - - /* One of the JSExnType constants above. */ - int16 exnType; -} JSErrorFormatString; - -typedef const JSErrorFormatString * -(* JS_DLL_CALLBACK JSErrorCallback)(void *userRef, const char *locale, - const uintN errorNumber); - -#ifdef va_start -#define JS_ARGUMENT_FORMATTER_DEFINED 1 - -typedef JSBool -(* JS_DLL_CALLBACK JSArgumentFormatter)(JSContext *cx, const char *format, - JSBool fromJS, jsval **vpp, - va_list *app); -#endif - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleToUpperCase)(JSContext *cx, JSString *src, - jsval *rval); - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleToLowerCase)(JSContext *cx, JSString *src, - jsval *rval); - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleCompare)(JSContext *cx, - JSString *src1, JSString *src2, - jsval *rval); - -typedef JSBool -(* JS_DLL_CALLBACK JSLocaleToUnicode)(JSContext *cx, char *src, jsval *rval); - -/* - * Security protocol types. - */ -typedef struct JSPrincipals JSPrincipals; - -/* - * XDR-encode or -decode a principals instance, based on whether xdr->mode is - * JSXDR_ENCODE, in which case *principalsp should be encoded; or JSXDR_DECODE, - * in which case implementations must return a held (via JSPRINCIPALS_HOLD), - * non-null *principalsp out parameter. Return true on success, false on any - * error, which the implementation must have reported. - */ -typedef JSBool -(* JS_DLL_CALLBACK JSPrincipalsTranscoder)(JSXDRState *xdr, - JSPrincipals **principalsp); - -/* - * Return a weak reference to the principals associated with obj, possibly via - * the immutable parent chain leading from obj to a top-level container (e.g., - * a window object in the DOM level 0). If there are no principals associated - * with obj, return null. Therefore null does not mean an error was reported; - * in no event should an error be reported or an exception be thrown by this - * callback's implementation. - */ -typedef JSPrincipals * -(* JS_DLL_CALLBACK JSObjectPrincipalsFinder)(JSContext *cx, JSObject *obj); - -JS_END_EXTERN_C - -#endif /* jspubtd_h___ */ diff --git a/spidermonkey/libjs/jsregexp.c b/spidermonkey/libjs/jsregexp.c deleted file mode 100644 index 5d2fce4..0000000 --- a/spidermonkey/libjs/jsregexp.c +++ /dev/null @@ -1,4206 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS regular expressions, after Perl. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsstr.h" - -/* Note : contiguity of 'simple opcodes' is important for SimpleMatch() */ -typedef enum REOp { - REOP_EMPTY = 0, /* match rest of input against rest of r.e. */ - REOP_ALT = 1, /* alternative subexpressions in kid and next */ - REOP_SIMPLE_START = 2, /* start of 'simple opcodes' */ - REOP_BOL = 2, /* beginning of input (or line if multiline) */ - REOP_EOL = 3, /* end of input (or line if multiline) */ - REOP_WBDRY = 4, /* match "" at word boundary */ - REOP_WNONBDRY = 5, /* match "" at word non-boundary */ - REOP_DOT = 6, /* stands for any character */ - REOP_DIGIT = 7, /* match a digit char: [0-9] */ - REOP_NONDIGIT = 8, /* match a non-digit char: [^0-9] */ - REOP_ALNUM = 9, /* match an alphanumeric char: [0-9a-z_A-Z] */ - REOP_NONALNUM = 10, /* match a non-alphanumeric char: [^0-9a-z_A-Z] */ - REOP_SPACE = 11, /* match a whitespace char */ - REOP_NONSPACE = 12, /* match a non-whitespace char */ - REOP_BACKREF = 13, /* back-reference (e.g., \1) to a parenthetical */ - REOP_FLAT = 14, /* match a flat string */ - REOP_FLAT1 = 15, /* match a single char */ - REOP_FLATi = 16, /* case-independent REOP_FLAT */ - REOP_FLAT1i = 17, /* case-independent REOP_FLAT1 */ - REOP_UCFLAT1 = 18, /* single Unicode char */ - REOP_UCFLAT1i = 19, /* case-independent REOP_UCFLAT1 */ - REOP_UCFLAT = 20, /* flat Unicode string; len immediate counts chars */ - REOP_UCFLATi = 21, /* case-independent REOP_UCFLAT */ - REOP_CLASS = 22, /* character class with index */ - REOP_NCLASS = 23, /* negated character class with index */ - REOP_SIMPLE_END = 23, /* end of 'simple opcodes' */ - REOP_QUANT = 25, /* quantified atom: atom{1,2} */ - REOP_STAR = 26, /* zero or more occurrences of kid */ - REOP_PLUS = 27, /* one or more occurrences of kid */ - REOP_OPT = 28, /* optional subexpression in kid */ - REOP_LPAREN = 29, /* left paren bytecode: kid is u.num'th sub-regexp */ - REOP_RPAREN = 30, /* right paren bytecode */ - REOP_JUMP = 31, /* for deoptimized closure loops */ - REOP_DOTSTAR = 32, /* optimize .* to use a single opcode */ - REOP_ANCHOR = 33, /* like .* but skips left context to unanchored r.e. */ - REOP_EOLONLY = 34, /* $ not preceded by any pattern */ - REOP_BACKREFi = 37, /* case-independent REOP_BACKREF */ - REOP_LPARENNON = 41, /* non-capturing version of REOP_LPAREN */ - REOP_ASSERT = 43, /* zero width positive lookahead assertion */ - REOP_ASSERT_NOT = 44, /* zero width negative lookahead assertion */ - REOP_ASSERTTEST = 45, /* sentinel at end of assertion child */ - REOP_ASSERTNOTTEST = 46, /* sentinel at end of !assertion child */ - REOP_MINIMALSTAR = 47, /* non-greedy version of * */ - REOP_MINIMALPLUS = 48, /* non-greedy version of + */ - REOP_MINIMALOPT = 49, /* non-greedy version of ? */ - REOP_MINIMALQUANT = 50, /* non-greedy version of {} */ - REOP_ENDCHILD = 51, /* sentinel at end of quantifier child */ - REOP_REPEAT = 52, /* directs execution of greedy quantifier */ - REOP_MINIMALREPEAT = 53, /* directs execution of non-greedy quantifier */ - REOP_ALTPREREQ = 54, /* prerequisite for ALT, either of two chars */ - REOP_ALTPREREQ2 = 55, /* prerequisite for ALT, a char or a class */ - REOP_ENDALT = 56, /* end of final alternate */ - REOP_CONCAT = 57, /* concatenation of terms (parse time only) */ - - REOP_END -} REOp; - -#define REOP_IS_SIMPLE(op) ((unsigned)((op) - REOP_SIMPLE_START) < \ - (unsigned)REOP_SIMPLE_END) - -struct RENode { - REOp op; /* r.e. op bytecode */ - RENode *next; /* next in concatenation order */ - void *kid; /* first operand */ - union { - void *kid2; /* second operand */ - jsint num; /* could be a number */ - size_t parenIndex; /* or a parenthesis index */ - struct { /* or a quantifier range */ - uintN min; - uintN max; - JSPackedBool greedy; - } range; - struct { /* or a character class */ - size_t startIndex; - size_t kidlen; /* length of string at kid, in jschars */ - size_t index; /* index into class list */ - uint16 bmsize; /* bitmap size, based on max char code */ - JSPackedBool sense; - } ucclass; - struct { /* or a literal sequence */ - jschar chr; /* of one character */ - size_t length; /* or many (via the kid) */ - } flat; - struct { - RENode *kid2; /* second operand from ALT */ - jschar ch1; /* match char for ALTPREREQ */ - jschar ch2; /* ditto, or class index for ALTPREREQ2 */ - } altprereq; - } u; -}; - -#define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \ - ((c >= 'a') && (c <= 'z')) ) -#define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \ - (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) - -#define CLASS_CACHE_SIZE 4 - -typedef struct CompilerState { - JSContext *context; - JSTokenStream *tokenStream; /* For reporting errors */ - const jschar *cpbegin; - const jschar *cpend; - const jschar *cp; - size_t parenCount; - size_t classCount; /* number of [] encountered */ - size_t treeDepth; /* maximum depth of parse tree */ - size_t progLength; /* estimated bytecode length */ - RENode *result; - size_t classBitmapsMem; /* memory to hold all class bitmaps */ - struct { - const jschar *start; /* small cache of class strings */ - size_t length; /* since they're often the same */ - size_t index; - } classCache[CLASS_CACHE_SIZE]; - uint16 flags; -} CompilerState; - -typedef struct EmitStateStackEntry { - jsbytecode *altHead; /* start of REOP_ALT* opcode */ - jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ - jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ - jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ - RENode *continueNode; /* original REOP_ALT* node being stacked */ - jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ - JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to - avoid 16-bit unsigned offset overflow */ -} EmitStateStackEntry; - -/* - * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, - * the getters and setters take the pc of the offset, not of the opcode before - * the offset. - */ -#define ARG_LEN 2 -#define GET_ARG(pc) ((uint16)(((pc)[0] << 8) | (pc)[1])) -#define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ - (pc)[1] = (jsbytecode) (arg)) - -#define OFFSET_LEN ARG_LEN -#define OFFSET_MAX (JS_BIT(ARG_LEN * 8) - 1) -#define GET_OFFSET(pc) GET_ARG(pc) - -/* - * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. - * For sanity, we limit it to 2^24 bytes. - */ -#define TREE_DEPTH_MAX (JS_BIT(24) / sizeof(EmitStateStackEntry)) - -/* - * The maximum memory that can be allocated for class bitmaps. - * For sanity, we limit it to 2^24 bytes. - */ -#define CLASS_BITMAPS_MEM_LIMIT JS_BIT(24) - -/* - * Functions to get size and write/read bytecode that represent small indexes - * compactly. - * Each byte in the code represent 7-bit chunk of the index. 8th bit when set - * indicates that the following byte brings more bits to the index. Otherwise - * this is the last byte in the index bytecode representing highest index bits. - */ -static size_t -GetCompactIndexWidth(size_t index) -{ - size_t width; - - for (width = 1; (index >>= 7) != 0; ++width) { } - return width; -} - -static jsbytecode * -WriteCompactIndex(jsbytecode *pc, size_t index) -{ - size_t next; - - while ((next = index >> 7) != 0) { - *pc++ = (jsbytecode)(index | 0x80); - index = next; - } - *pc++ = (jsbytecode)index; - return pc; -} - -static jsbytecode * -ReadCompactIndex(jsbytecode *pc, size_t *result) -{ - size_t nextByte; - - nextByte = *pc++; - if ((nextByte & 0x80) == 0) { - /* - * Short-circuit the most common case when compact index <= 127. - */ - *result = nextByte; - } else { - size_t shift = 7; - *result = 0x7F & nextByte; - do { - nextByte = *pc++; - *result |= (nextByte & 0x7F) << shift; - shift += 7; - } while ((nextByte & 0x80) != 0); - } - return pc; -} - -typedef struct RECapture { - ptrdiff_t index; /* start of contents, -1 for empty */ - size_t length; /* length of capture */ -} RECapture; - -typedef struct REMatchState { - const jschar *cp; - RECapture parens[1]; /* first of 're->parenCount' captures, - allocated at end of this struct */ -} REMatchState; - -struct REBackTrackData; - -typedef struct REProgState { - jsbytecode *continue_pc; /* current continuation data */ - jsbytecode continue_op; - ptrdiff_t index; /* progress in text */ - size_t parenSoFar; /* highest indexed paren started */ - union { - struct { - uintN min; /* current quantifier limits */ - uintN max; - } quantifier; - struct { - size_t top; /* backtrack stack state */ - size_t sz; - } assertion; - } u; -} REProgState; - -typedef struct REBackTrackData { - size_t sz; /* size of previous stack entry */ - jsbytecode *backtrack_pc; /* where to backtrack to */ - jsbytecode backtrack_op; - const jschar *cp; /* index in text of match at backtrack */ - size_t parenIndex; /* start index of saved paren contents */ - size_t parenCount; /* # of saved paren contents */ - size_t saveStateStackTop; /* number of parent states */ - /* saved parent states follow */ - /* saved paren contents follow */ -} REBackTrackData; - -#define INITIAL_STATESTACK 100 -#define INITIAL_BACKTRACK 8000 - -typedef struct REGlobalData { - JSContext *cx; - JSRegExp *regexp; /* the RE in execution */ - JSBool ok; /* runtime error (out_of_memory only?) */ - size_t start; /* offset to start at */ - ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ - const jschar *cpbegin; /* text base address */ - const jschar *cpend; /* text limit address */ - - REProgState *stateStack; /* stack of state of current parents */ - size_t stateStackTop; - size_t stateStackLimit; - - REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ - REBackTrackData *backTrackSP; - size_t backTrackStackSize; - size_t cursz; /* size of current stack entry */ - - JSArenaPool pool; /* It's faster to use one malloc'd pool - than to malloc/free the three items - that are allocated from this pool */ -} REGlobalData; - -/* - * 1. If IgnoreCase is false, return ch. - * 2. Let u be ch converted to upper case as if by calling - * String.prototype.toUpperCase on the one-character string ch. - * 3. If u does not consist of a single character, return ch. - * 4. Let cu be u's character. - * 5. If ch's code point value is greater than or equal to decimal 128 and cu's - * code point value is less than decimal 128, then return ch. - * 6. Return cu. - */ -static jschar -upcase(jschar ch) -{ - jschar cu = JS_TOUPPER(ch); - if (ch >= 128 && cu < 128) - return ch; - return cu; -} - -static jschar -downcase(jschar ch) -{ - jschar cl = JS_TOLOWER(ch); - if (cl >= 128 && ch < 128) - return ch; - return cl; -} - -/* Construct and initialize an RENode, returning NULL for out-of-memory */ -static RENode * -NewRENode(CompilerState *state, REOp op) -{ - JSContext *cx; - RENode *ren; - - cx = state->context; - JS_ARENA_ALLOCATE_CAST(ren, RENode *, &cx->tempPool, sizeof *ren); - if (!ren) { - JS_ReportOutOfMemory(cx); - return NULL; - } - ren->op = op; - ren->next = NULL; - ren->kid = NULL; - return ren; -} - -/* - * Validates and converts hex ascii value. - */ -static JSBool -isASCIIHexDigit(jschar c, uintN *digit) -{ - uintN cv = c; - - if (cv < '0') - return JS_FALSE; - if (cv <= '9') { - *digit = cv - '0'; - return JS_TRUE; - } - cv |= 0x20; - if (cv >= 'a' && cv <= 'f') { - *digit = cv - 'a' + 10; - return JS_TRUE; - } - return JS_FALSE; -} - - -typedef struct { - REOp op; - const jschar *errPos; - size_t parenIndex; -} REOpData; - - -/* - * Process the op against the two top operands, reducing them to a single - * operand in the penultimate slot. Update progLength and treeDepth. - */ -static JSBool -ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, - intN operandSP) -{ - RENode *result; - - switch (opData->op) { - case REOP_ALT: - result = NewRENode(state, REOP_ALT); - if (!result) - return JS_FALSE; - result->kid = operandStack[operandSP - 2]; - result->u.kid2 = operandStack[operandSP - 1]; - operandStack[operandSP - 2] = result; - - if (state->treeDepth == TREE_DEPTH_MAX) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - ++state->treeDepth; - - /* - * Look at both alternates to see if there's a FLAT or a CLASS at - * the start of each. If so, use a prerequisite match. - */ - if (((RENode *) result->kid)->op == REOP_FLAT && - ((RENode *) result->u.kid2)->op == REOP_FLAT && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ; - result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; - result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr; - /* ALTPREREQ, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else - if (((RENode *) result->kid)->op == REOP_CLASS && - ((RENode *) result->kid)->u.ucclass.index < 256 && - ((RENode *) result->u.kid2)->op == REOP_FLAT && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ2; - result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; - result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index; - /* ALTPREREQ2, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else - if (((RENode *) result->kid)->op == REOP_FLAT && - ((RENode *) result->u.kid2)->op == REOP_CLASS && - ((RENode *) result->u.kid2)->u.ucclass.index < 256 && - (state->flags & JSREG_FOLD) == 0) { - result->op = REOP_ALTPREREQ2; - result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; - result->u.altprereq.ch2 = - ((RENode *) result->u.kid2)->u.ucclass.index; - /* ALTPREREQ2, , uch1, uch2, , ..., - JUMP, ... ENDALT */ - state->progLength += 13; - } - else { - /* ALT, , ..., JUMP, ... ENDALT */ - state->progLength += 7; - } - break; - - case REOP_CONCAT: - result = operandStack[operandSP - 2]; - while (result->next) - result = result->next; - result->next = operandStack[operandSP - 1]; - break; - - case REOP_ASSERT: - case REOP_ASSERT_NOT: - case REOP_LPARENNON: - case REOP_LPAREN: - /* These should have been processed by a close paren. */ - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_PAREN, opData->errPos); - return JS_FALSE; - - default:; - } - return JS_TRUE; -} - -/* - * Parser forward declarations. - */ -static JSBool ParseTerm(CompilerState *state); -static JSBool ParseQuantifier(CompilerState *state); -static intN ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues); - -/* - * Top-down regular expression grammar, based closely on Perl4. - * - * regexp: altern A regular expression is one or more - * altern '|' regexp alternatives separated by vertical bar. - */ -#define INITIAL_STACK_SIZE 128 - -static JSBool -ParseRegExp(CompilerState *state) -{ - size_t parenIndex; - RENode *operand; - REOpData *operatorStack; - RENode **operandStack; - REOp op; - intN i; - JSBool result = JS_FALSE; - - intN operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; - intN operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; - - /* Watch out for empty regexp */ - if (state->cp == state->cpend) { - state->result = NewRENode(state, REOP_EMPTY); - return (state->result != NULL); - } - - operatorStack = (REOpData *) - JS_malloc(state->context, sizeof(REOpData) * operatorStackSize); - if (!operatorStack) - return JS_FALSE; - - operandStack = (RENode **) - JS_malloc(state->context, sizeof(RENode *) * operandStackSize); - if (!operandStack) - goto out; - - for (;;) { - parenIndex = state->parenCount; - if (state->cp == state->cpend) { - /* - * If we are at the end of the regexp and we're short one or more - * operands, the regexp must have the form /x|/ or some such, with - * left parentheses making us short more than one operand. - */ - if (operatorSP >= operandSP) { - operand = NewRENode(state, REOP_EMPTY); - if (!operand) - goto out; - goto pushOperand; - } - } else { - switch (*state->cp) { - case '(': - ++state->cp; - if (state->cp + 1 < state->cpend && - *state->cp == '?' && - (state->cp[1] == '=' || - state->cp[1] == '!' || - state->cp[1] == ':')) { - switch (state->cp[1]) { - case '=': - op = REOP_ASSERT; - /* ASSERT, , ... ASSERTTEST */ - state->progLength += 4; - break; - case '!': - op = REOP_ASSERT_NOT; - /* ASSERTNOT, , ... ASSERTNOTTEST */ - state->progLength += 4; - break; - default: - op = REOP_LPARENNON; - break; - } - state->cp += 2; - } else { - op = REOP_LPAREN; - /* LPAREN, , ... RPAREN, */ - state->progLength - += 2 * (1 + GetCompactIndexWidth(parenIndex)); - state->parenCount++; - if (state->parenCount == 65535) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_TOO_MANY_PARENS); - goto out; - } - } - goto pushOperator; - - case ')': - /* - * If there's no stacked open parenthesis, throw syntax error. - */ - for (i = operatorSP - 1; ; i--) { - if (i < 0) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_UNMATCHED_RIGHT_PAREN); - goto out; - } - if (operatorStack[i].op == REOP_ASSERT || - operatorStack[i].op == REOP_ASSERT_NOT || - operatorStack[i].op == REOP_LPARENNON || - operatorStack[i].op == REOP_LPAREN) { - break; - } - } - /* FALL THROUGH */ - - case '|': - /* Expected an operand before these, so make an empty one */ - operand = NewRENode(state, REOP_EMPTY); - if (!operand) - goto out; - goto pushOperand; - - default: - if (!ParseTerm(state)) - goto out; - operand = state->result; -pushOperand: - if (operandSP == operandStackSize) { - operandStackSize += operandStackSize; - operandStack = (RENode **) - JS_realloc(state->context, operandStack, - sizeof(RENode *) * operandStackSize); - if (!operandStack) - goto out; - } - operandStack[operandSP++] = operand; - break; - } - } - - /* At the end; process remaining operators. */ -restartOperator: - if (state->cp == state->cpend) { - while (operatorSP) { - --operatorSP; - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) - goto out; - --operandSP; - } - JS_ASSERT(operandSP == 1); - state->result = operandStack[0]; - result = JS_TRUE; - goto out; - } - - switch (*state->cp) { - case '|': - /* Process any stacked 'concat' operators */ - ++state->cp; - while (operatorSP && - operatorStack[operatorSP - 1].op == REOP_CONCAT) { - --operatorSP; - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) { - goto out; - } - --operandSP; - } - op = REOP_ALT; - goto pushOperator; - - case ')': - /* - * If there's no stacked open parenthesis, throw syntax error. - */ - for (i = operatorSP - 1; ; i--) { - if (i < 0) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNMATCHED_RIGHT_PAREN); - goto out; - } - if (operatorStack[i].op == REOP_ASSERT || - operatorStack[i].op == REOP_ASSERT_NOT || - operatorStack[i].op == REOP_LPARENNON || - operatorStack[i].op == REOP_LPAREN) { - break; - } - } - ++state->cp; - - /* Process everything on the stack until the open parenthesis. */ - for (;;) { - JS_ASSERT(operatorSP); - --operatorSP; - switch (operatorStack[operatorSP].op) { - case REOP_ASSERT: - case REOP_ASSERT_NOT: - case REOP_LPAREN: - operand = NewRENode(state, operatorStack[operatorSP].op); - if (!operand) - goto out; - operand->u.parenIndex = - operatorStack[operatorSP].parenIndex; - JS_ASSERT(operandSP); - operand->kid = operandStack[operandSP - 1]; - operandStack[operandSP - 1] = operand; - if (state->treeDepth == TREE_DEPTH_MAX) { - js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - goto out; - } - ++state->treeDepth; - /* FALL THROUGH */ - - case REOP_LPARENNON: - state->result = operandStack[operandSP - 1]; - if (!ParseQuantifier(state)) - goto out; - operandStack[operandSP - 1] = state->result; - goto restartOperator; - default: - if (!ProcessOp(state, &operatorStack[operatorSP], - operandStack, operandSP)) - goto out; - --operandSP; - break; - } - } - break; - - case '{': - { - const jschar *errp = state->cp; - - if (ParseMinMaxQuantifier(state, JS_TRUE) < 0) { - /* - * This didn't even scan correctly as a quantifier, so we should - * treat it as flat. - */ - op = REOP_CONCAT; - goto pushOperator; - } - - state->cp = errp; - /* FALL THROUGH */ - } - - case '+': - case '*': - case '?': - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp); - result = JS_FALSE; - goto out; - - default: - /* Anything else is the start of the next term. */ - op = REOP_CONCAT; -pushOperator: - if (operatorSP == operatorStackSize) { - operatorStackSize += operatorStackSize; - operatorStack = (REOpData *) - JS_realloc(state->context, operatorStack, - sizeof(REOpData) * operatorStackSize); - if (!operatorStack) - goto out; - } - operatorStack[operatorSP].op = op; - operatorStack[operatorSP].errPos = state->cp; - operatorStack[operatorSP++].parenIndex = parenIndex; - break; - } - } -out: - if (operatorStack) - JS_free(state->context, operatorStack); - if (operandStack) - JS_free(state->context, operandStack); - return result; -} - -/* - * Hack two bits in CompilerState.flags, for use within FindParenCount to flag - * its being on the stack, and to propagate errors to its callers. - */ -#define JSREG_FIND_PAREN_COUNT 0x8000 -#define JSREG_FIND_PAREN_ERROR 0x4000 - -/* - * Magic return value from FindParenCount and GetDecimalValue, to indicate - * overflow beyond GetDecimalValue's max parameter, or a computed maximum if - * its findMax parameter is non-null. - */ -#define OVERFLOW_VALUE ((uintN)-1) - -static uintN -FindParenCount(CompilerState *state) -{ - CompilerState temp; - int i; - - if (state->flags & JSREG_FIND_PAREN_COUNT) - return OVERFLOW_VALUE; - - /* - * Copy state into temp, flag it so we never report an invalid backref, - * and reset its members to parse the entire regexp. This is obviously - * suboptimal, but GetDecimalValue calls us only if a backref appears to - * refer to a forward parenthetical, which is rare. - */ - temp = *state; - temp.flags |= JSREG_FIND_PAREN_COUNT; - temp.cp = temp.cpbegin; - temp.parenCount = 0; - temp.classCount = 0; - temp.progLength = 0; - temp.treeDepth = 0; - temp.classBitmapsMem = 0; - for (i = 0; i < CLASS_CACHE_SIZE; i++) - temp.classCache[i].start = NULL; - - if (!ParseRegExp(&temp)) { - state->flags |= JSREG_FIND_PAREN_ERROR; - return OVERFLOW_VALUE; - } - return temp.parenCount; -} - -/* - * Extract and return a decimal value at state->cp. The initial character c - * has already been read. Return OVERFLOW_VALUE if the result exceeds max. - * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in - * state->flags to discover whether an error occurred under findMax. - */ -static uintN -GetDecimalValue(jschar c, uintN max, uintN (*findMax)(CompilerState *state), - CompilerState *state) -{ - uintN value = JS7_UNDEC(c); - JSBool overflow = (value > max && (!findMax || value > findMax(state))); - - /* The following restriction allows simpler overflow checks. */ - JS_ASSERT(max <= ((uintN)-1 - 9) / 10); - while (state->cp < state->cpend) { - c = *state->cp; - if (!JS7_ISDEC(c)) - break; - value = 10 * value + JS7_UNDEC(c); - if (!overflow && value > max && (!findMax || value > findMax(state))) - overflow = JS_TRUE; - ++state->cp; - } - return overflow ? OVERFLOW_VALUE : value; -} - -/* - * Calculate the total size of the bitmap required for a class expression. - */ -static JSBool -CalculateBitmapSize(CompilerState *state, RENode *target, const jschar *src, - const jschar *end) -{ - uintN max = 0; - JSBool inRange = JS_FALSE; - jschar c, rangeStart = 0; - uintN n, digit, nDigits, i; - - target->u.ucclass.bmsize = 0; - target->u.ucclass.sense = JS_TRUE; - - if (src == end) - return JS_TRUE; - - if (*src == '^') { - ++src; - target->u.ucclass.sense = JS_FALSE; - } - - while (src != end) { - uintN localMax = 0; - switch (*src) { - case '\\': - ++src; - c = *src++; - switch (c) { - case 'b': - localMax = 0x8; - break; - case 'f': - localMax = 0xC; - break; - case 'n': - localMax = 0xA; - break; - case 'r': - localMax = 0xD; - break; - case 't': - localMax = 0x9; - break; - case 'v': - localMax = 0xB; - break; - case 'c': - if (src < end && RE_IS_LETTER(*src)) { - localMax = (jschar) (*src++ & 0x1F); - } else { - --src; - localMax = '\\'; - } - break; - case 'x': - nDigits = 2; - goto lexHex; - case 'u': - nDigits = 4; -lexHex: - n = 0; - for (i = 0; (i < nDigits) && (src < end); i++) { - c = *src++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original - *'\' as a literal. - */ - src -= i + 1; - n = '\\'; - break; - } - n = (n << 4) | digit; - } - localMax = n; - break; - case 'd': - if (inRange) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - localMax = '9'; - break; - case 'D': - case 's': - case 'S': - case 'w': - case 'W': - if (inRange) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - target->u.ucclass.bmsize = 65535; - return JS_TRUE; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* - * This is a non-ECMA extension - decimal escapes (in this - * case, octal!) are supposed to be an error inside class - * ranges, but supported here for backwards compatibility. - * - */ - n = JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - n = 8 * n + JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - i = 8 * n + JS7_UNDEC(c); - if (i <= 0377) - n = i; - else - src--; - } - } - localMax = n; - break; - - default: - localMax = c; - break; - } - break; - default: - localMax = *src++; - break; - } - if (state->flags & JSREG_FOLD) { - c = JS_MAX(upcase((jschar) localMax), downcase((jschar) localMax)); - if (c > localMax) - localMax = c; - } - if (inRange) { - if (rangeStart > localMax) { - JS_ReportErrorNumber(state->context, - js_GetErrorMessage, NULL, - JSMSG_BAD_CLASS_RANGE); - return JS_FALSE; - } - inRange = JS_FALSE; - } else { - if (src < end - 1) { - if (*src == '-') { - ++src; - inRange = JS_TRUE; - rangeStart = (jschar)localMax; - continue; - } - } - } - if (localMax > max) - max = localMax; - } - target->u.ucclass.bmsize = max; - return JS_TRUE; -} - -/* - * item: assertion An item is either an assertion or - * quantatom a quantified atom. - * - * assertion: '^' Assertions match beginning of string - * (or line if the class static property - * RegExp.multiline is true). - * '$' End of string (or line if the class - * static property RegExp.multiline is - * true). - * '\b' Word boundary (between \w and \W). - * '\B' Word non-boundary. - * - * quantatom: atom An unquantified atom. - * quantatom '{' n ',' m '}' - * Atom must occur between n and m times. - * quantatom '{' n ',' '}' Atom must occur at least n times. - * quantatom '{' n '}' Atom must occur exactly n times. - * quantatom '*' Zero or more times (same as {0,}). - * quantatom '+' One or more times (same as {1,}). - * quantatom '?' Zero or one time (same as {0,1}). - * - * any of which can be optionally followed by '?' for ungreedy - * - * atom: '(' regexp ')' A parenthesized regexp (what matched - * can be addressed using a backreference, - * see '\' n below). - * '.' Matches any char except '\n'. - * '[' classlist ']' A character class. - * '[' '^' classlist ']' A negated character class. - * '\f' Form Feed. - * '\n' Newline (Line Feed). - * '\r' Carriage Return. - * '\t' Horizontal Tab. - * '\v' Vertical Tab. - * '\d' A digit (same as [0-9]). - * '\D' A non-digit. - * '\w' A word character, [0-9a-z_A-Z]. - * '\W' A non-word character. - * '\s' A whitespace character, [ \b\f\n\r\t\v]. - * '\S' A non-whitespace character. - * '\' n A backreference to the nth (n decimal - * and positive) parenthesized expression. - * '\' octal An octal escape sequence (octal must be - * two or three digits long, unless it is - * 0 for the null character). - * '\x' hex A hex escape (hex must be two digits). - * '\u' unicode A unicode escape (must be four digits). - * '\c' ctrl A control character, ctrl is a letter. - * '\' literalatomchar Any character except one of the above - * that follow '\' in an atom. - * otheratomchar Any character not first among the other - * atom right-hand sides. - */ -static JSBool -ParseTerm(CompilerState *state) -{ - jschar c = *state->cp++; - uintN nDigits; - uintN num, tmp, n, i; - const jschar *termStart; - - switch (c) { - /* assertions and atoms */ - case '^': - state->result = NewRENode(state, REOP_BOL); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case '$': - state->result = NewRENode(state, REOP_EOL); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case '\\': - if (state->cp >= state->cpend) { - /* a trailing '\' is an error */ - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_TRAILING_SLASH); - return JS_FALSE; - } - c = *state->cp++; - switch (c) { - /* assertion escapes */ - case 'b' : - state->result = NewRENode(state, REOP_WBDRY); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - case 'B': - state->result = NewRENode(state, REOP_WNONBDRY); - if (!state->result) - return JS_FALSE; - state->progLength++; - return JS_TRUE; - /* Decimal escape */ - case '0': - /* Give a strict warning. See also the note below. */ - if (!js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_INVALID_BACKREF)) { - return JS_FALSE; - } - doOctal: - num = 0; - while (state->cp < state->cpend) { - c = *state->cp; - if (c < '0' || '7' < c) - break; - state->cp++; - tmp = 8 * num + (uintN)JS7_UNDEC(c); - if (tmp > 0377) - break; - num = tmp; - } - c = (jschar)num; - doFlat: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->progLength += 3; - break; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - termStart = state->cp - 1; - num = GetDecimalValue(c, state->parenCount, FindParenCount, state); - if (state->flags & JSREG_FIND_PAREN_ERROR) - return JS_FALSE; - if (num == OVERFLOW_VALUE) { - /* Give a strict mode warning. */ - if (!js_ReportCompileErrorNumber(state->context, - state->tokenStream, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - (c >= '8') - ? JSMSG_INVALID_BACKREF - : JSMSG_BAD_BACKREF)) { - return JS_FALSE; - } - - /* - * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax - * error here. However, for compatibility with IE, we treat the - * whole backref as flat if the first character in it is not a - * valid octal character, and as an octal escape otherwise. - */ - state->cp = termStart; - if (c >= '8') { - /* Treat this as flat. termStart - 1 is the \. */ - c = '\\'; - goto asFlat; - } - - /* Treat this as an octal escape. */ - goto doOctal; - } - JS_ASSERT(1 <= num && num <= 0x10000); - state->result = NewRENode(state, REOP_BACKREF); - if (!state->result) - return JS_FALSE; - state->result->u.parenIndex = num - 1; - state->progLength - += 1 + GetCompactIndexWidth(state->result->u.parenIndex); - break; - /* Control escape */ - case 'f': - c = 0xC; - goto doFlat; - case 'n': - c = 0xA; - goto doFlat; - case 'r': - c = 0xD; - goto doFlat; - case 't': - c = 0x9; - goto doFlat; - case 'v': - c = 0xB; - goto doFlat; - /* Control letter */ - case 'c': - if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { - c = (jschar) (*state->cp++ & 0x1F); - } else { - /* back off to accepting the original '\' as a literal */ - --state->cp; - c = '\\'; - } - goto doFlat; - /* HexEscapeSequence */ - case 'x': - nDigits = 2; - goto lexHex; - /* UnicodeEscapeSequence */ - case 'u': - nDigits = 4; -lexHex: - n = 0; - for (i = 0; i < nDigits && state->cp < state->cpend; i++) { - uintN digit; - c = *state->cp++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original 'u' or 'x' as a - * literal. - */ - state->cp -= i + 2; - n = *state->cp++; - break; - } - n = (n << 4) | digit; - } - c = (jschar) n; - goto doFlat; - /* Character class escapes */ - case 'd': - state->result = NewRENode(state, REOP_DIGIT); -doSimple: - if (!state->result) - return JS_FALSE; - state->progLength++; - break; - case 'D': - state->result = NewRENode(state, REOP_NONDIGIT); - goto doSimple; - case 's': - state->result = NewRENode(state, REOP_SPACE); - goto doSimple; - case 'S': - state->result = NewRENode(state, REOP_NONSPACE); - goto doSimple; - case 'w': - state->result = NewRENode(state, REOP_ALNUM); - goto doSimple; - case 'W': - state->result = NewRENode(state, REOP_NONALNUM); - goto doSimple; - /* IdentityEscape */ - default: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->result->kid = (void *) (state->cp - 1); - state->progLength += 3; - break; - } - break; - case '[': - state->result = NewRENode(state, REOP_CLASS); - if (!state->result) - return JS_FALSE; - termStart = state->cp; - state->result->u.ucclass.startIndex = termStart - state->cpbegin; - for (;;) { - if (state->cp == state->cpend) { - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNTERM_CLASS, termStart); - - return JS_FALSE; - } - if (*state->cp == '\\') { - state->cp++; - if (state->cp != state->cpend) - state->cp++; - continue; - } - if (*state->cp == ']') { - state->result->u.ucclass.kidlen = state->cp - termStart; - break; - } - state->cp++; - } - for (i = 0; i < CLASS_CACHE_SIZE; i++) { - if (!state->classCache[i].start) { - state->classCache[i].start = termStart; - state->classCache[i].length = state->result->u.ucclass.kidlen; - state->classCache[i].index = state->classCount; - break; - } - if (state->classCache[i].length == - state->result->u.ucclass.kidlen) { - for (n = 0; ; n++) { - if (n == state->classCache[i].length) { - state->result->u.ucclass.index - = state->classCache[i].index; - goto claim; - } - if (state->classCache[i].start[n] != termStart[n]) - break; - } - } - } - state->result->u.ucclass.index = state->classCount++; - - claim: - /* - * Call CalculateBitmapSize now as we want any errors it finds - * to be reported during the parse phase, not at execution. - */ - if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) - return JS_FALSE; - /* - * Update classBitmapsMem with number of bytes to hold bmsize bits, - * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 - * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. - */ - n = (state->result->u.ucclass.bmsize >> 3) + 1; - if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - state->classBitmapsMem += n; - /* CLASS, */ - state->progLength - += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); - break; - - case '.': - state->result = NewRENode(state, REOP_DOT); - goto doSimple; - - case '{': - { - const jschar *errp = state->cp--; - intN err; - - err = ParseMinMaxQuantifier(state, JS_TRUE); - state->cp = errp; - - if (err < 0) - goto asFlat; - - /* FALL THROUGH */ - } - case '*': - case '+': - case '?': - js_ReportCompileErrorNumberUC(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_QUANTIFIER, state->cp - 1); - return JS_FALSE; - default: -asFlat: - state->result = NewRENode(state, REOP_FLAT); - if (!state->result) - return JS_FALSE; - state->result->u.flat.chr = c; - state->result->u.flat.length = 1; - state->result->kid = (void *) (state->cp - 1); - state->progLength += 3; - break; - } - return ParseQuantifier(state); -} - -static JSBool -ParseQuantifier(CompilerState *state) -{ - RENode *term; - term = state->result; - if (state->cp < state->cpend) { - switch (*state->cp) { - case '+': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 1; - state->result->u.range.max = (uintN)-1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '*': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 0; - state->result->u.range.max = (uintN)-1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '?': - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = 0; - state->result->u.range.max = 1; - /* , ... */ - state->progLength += 4; - goto quantifier; - case '{': /* balance '}' */ - { - intN err; - const jschar *errp = state->cp; - - err = ParseMinMaxQuantifier(state, JS_FALSE); - if (err == 0) - goto quantifier; - if (err == -1) - return JS_TRUE; - - js_ReportCompileErrorNumberUC(state->context, - state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - err, errp); - return JS_FALSE; - } - default:; - } - } - return JS_TRUE; - -quantifier: - if (state->treeDepth == TREE_DEPTH_MAX) { - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - return JS_FALSE; - } - - ++state->treeDepth; - ++state->cp; - state->result->kid = term; - if (state->cp < state->cpend && *state->cp == '?') { - ++state->cp; - state->result->u.range.greedy = JS_FALSE; - } else { - state->result->u.range.greedy = JS_TRUE; - } - return JS_TRUE; -} - -static intN -ParseMinMaxQuantifier(CompilerState *state, JSBool ignoreValues) -{ - uintN min, max; - jschar c; - const jschar *errp = state->cp++; - - c = *state->cp; - if (JS7_ISDEC(c)) { - ++state->cp; - min = GetDecimalValue(c, 0xFFFF, NULL, state); - c = *state->cp; - - if (!ignoreValues && min == OVERFLOW_VALUE) - return JSMSG_MIN_TOO_BIG; - - if (c == ',') { - c = *++state->cp; - if (JS7_ISDEC(c)) { - ++state->cp; - max = GetDecimalValue(c, 0xFFFF, NULL, state); - c = *state->cp; - if (!ignoreValues && max == OVERFLOW_VALUE) - return JSMSG_MAX_TOO_BIG; - if (!ignoreValues && min > max) - return JSMSG_OUT_OF_ORDER; - } else { - max = (uintN)-1; - } - } else { - max = min; - } - if (c == '}') { - state->result = NewRENode(state, REOP_QUANT); - if (!state->result) - return JS_FALSE; - state->result->u.range.min = min; - state->result->u.range.max = max; - /* - * QUANT, , , ... - * where is written as compact(max+1) to make - * (uintN)-1 sentinel to occupy 1 byte, not width_of(max)+1. - */ - state->progLength += (1 + GetCompactIndexWidth(min) - + GetCompactIndexWidth(max + 1) - +3); - return 0; - } - } - - state->cp = errp; - return -1; -} - -static JSBool -SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) -{ - ptrdiff_t offset = target - jump; - - /* Check that target really points forward. */ - JS_ASSERT(offset >= 2); - if ((size_t)offset > OFFSET_MAX) - return JS_FALSE; - - jump[0] = JUMP_OFFSET_HI(offset); - jump[1] = JUMP_OFFSET_LO(offset); - return JS_TRUE; -} - -/* - * Generate bytecode for the tree rooted at t using an explicit stack instead - * of recursion. - */ -static jsbytecode * -EmitREBytecode(CompilerState *state, JSRegExp *re, size_t treeDepth, - jsbytecode *pc, RENode *t) -{ - EmitStateStackEntry *emitStateSP, *emitStateStack; - RECharSet *charSet; - REOp op; - - if (treeDepth == 0) { - emitStateStack = NULL; - } else { - emitStateStack = - (EmitStateStackEntry *)JS_malloc(state->context, - sizeof(EmitStateStackEntry) * - treeDepth); - if (!emitStateStack) - return NULL; - } - emitStateSP = emitStateStack; - op = t->op; - - for (;;) { - *pc++ = op; - switch (op) { - case REOP_EMPTY: - --pc; - break; - - case REOP_ALTPREREQ2: - case REOP_ALTPREREQ: - JS_ASSERT(emitStateSP); - emitStateSP->altHead = pc - 1; - emitStateSP->endTermFixup = pc; - pc += OFFSET_LEN; - SET_ARG(pc, t->u.altprereq.ch1); - pc += ARG_LEN; - SET_ARG(pc, t->u.altprereq.ch2); - pc += ARG_LEN; - - emitStateSP->nextAltFixup = pc; /* offset to next alternate */ - pc += OFFSET_LEN; - - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_JUMP; - emitStateSP->jumpToJumpFlag = JS_FALSE; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_JUMP: - emitStateSP->nextTermFixup = pc; /* offset to following term */ - pc += OFFSET_LEN; - if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) - goto jump_too_big; - emitStateSP->continueOp = REOP_ENDALT; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = t->u.kid2; - op = t->op; - continue; - - case REOP_ENDALT: - /* - * If we already patched emitStateSP->nextTermFixup to jump to - * a nearer jump, to avoid 16-bit immediate offset overflow, we - * are done here. - */ - if (emitStateSP->jumpToJumpFlag) - break; - - /* - * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. - * REOP_ENDALT is executed only on successful match of the last - * alternate in a group. - */ - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - if (t->op != REOP_ALT) { - if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) - goto jump_too_big; - } - - /* - * If the program is bigger than the REOP_JUMP offset range, then - * we must check for alternates before this one that are part of - * the same group, and fix up their jump offsets to target jumps - * close enough to fit in a 16-bit unsigned offset immediate. - */ - if ((size_t)(pc - re->program) > OFFSET_MAX && - emitStateSP > emitStateStack) { - EmitStateStackEntry *esp, *esp2; - jsbytecode *alt, *jump; - ptrdiff_t span, header; - - esp2 = emitStateSP; - alt = esp2->altHead; - for (esp = esp2 - 1; esp >= emitStateStack; --esp) { - if (esp->continueOp == REOP_ENDALT && - !esp->jumpToJumpFlag && - esp->nextTermFixup + OFFSET_LEN == alt && - (size_t)(pc - ((esp->continueNode->op != REOP_ALT) - ? esp->endTermFixup - : esp->nextTermFixup)) > OFFSET_MAX) { - alt = esp->altHead; - jump = esp->nextTermFixup; - - /* - * The span must be 1 less than the distance from - * jump offset to jump offset, so we actually jump - * to a REOP_JUMP bytecode, not to its offset! - */ - for (;;) { - JS_ASSERT(jump < esp2->nextTermFixup); - span = esp2->nextTermFixup - jump - 1; - if ((size_t)span <= OFFSET_MAX) - break; - do { - if (--esp2 == esp) - goto jump_too_big; - } while (esp2->continueOp != REOP_ENDALT); - } - - jump[0] = JUMP_OFFSET_HI(span); - jump[1] = JUMP_OFFSET_LO(span); - - if (esp->continueNode->op != REOP_ALT) { - /* - * We must patch the offset at esp->endTermFixup - * as well, for the REOP_ALTPREREQ{,2} opcodes. - * If we're unlucky and endTermFixup is more than - * OFFSET_MAX bytes from its target, we cheat by - * jumping 6 bytes to the jump whose offset is at - * esp->nextTermFixup, which has the same target. - */ - jump = esp->endTermFixup; - header = esp->nextTermFixup - jump; - span += header; - if ((size_t)span > OFFSET_MAX) - span = header; - - jump[0] = JUMP_OFFSET_HI(span); - jump[1] = JUMP_OFFSET_LO(span); - } - - esp->jumpToJumpFlag = JS_TRUE; - } - } - } - break; - - case REOP_ALT: - JS_ASSERT(emitStateSP); - emitStateSP->altHead = pc - 1; - emitStateSP->nextAltFixup = pc; /* offset to next alternate */ - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_JUMP; - emitStateSP->jumpToJumpFlag = JS_FALSE; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = t->kid; - op = t->op; - continue; - - case REOP_FLAT: - /* - * Coalesce FLATs if possible and if it would not increase bytecode - * beyond preallocated limit. The latter happens only when bytecode - * size for coalesced string with offset p and length 2 exceeds 6 - * bytes preallocated for 2 single char nodes, i.e. when - * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or - * GetCompactIndexWidth(p) > 4. - * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more - * nodes strictly decreases bytecode size, the check has to be - * done only for the first coalescing. - */ - if (t->kid && - GetCompactIndexWidth((jschar *)t->kid - state->cpbegin) <= 4) - { - while (t->next && - t->next->op == REOP_FLAT && - (jschar*)t->kid + t->u.flat.length == - (jschar*)t->next->kid) { - t->u.flat.length += t->next->u.flat.length; - t->next = t->next->next; - } - } - if (t->kid && t->u.flat.length > 1) { - pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLATi : REOP_FLAT; - pc = WriteCompactIndex(pc, (jschar *)t->kid - state->cpbegin); - pc = WriteCompactIndex(pc, t->u.flat.length); - } else if (t->u.flat.chr < 256) { - pc[-1] = (state->flags & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; - *pc++ = (jsbytecode) t->u.flat.chr; - } else { - pc[-1] = (state->flags & JSREG_FOLD) - ? REOP_UCFLAT1i - : REOP_UCFLAT1; - SET_ARG(pc, t->u.flat.chr); - pc += ARG_LEN; - } - break; - - case REOP_LPAREN: - JS_ASSERT(emitStateSP); - pc = WriteCompactIndex(pc, t->u.parenIndex); - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_RPAREN; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_RPAREN: - pc = WriteCompactIndex(pc, t->u.parenIndex); - break; - - case REOP_BACKREF: - pc = WriteCompactIndex(pc, t->u.parenIndex); - break; - - case REOP_ASSERT: - JS_ASSERT(emitStateSP); - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ASSERTTEST; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_ASSERTTEST: - case REOP_ASSERTNOTTEST: - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - break; - - case REOP_ASSERT_NOT: - JS_ASSERT(emitStateSP); - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ASSERTNOTTEST; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_QUANT: - JS_ASSERT(emitStateSP); - if (t->u.range.min == 0 && t->u.range.max == (uintN)-1) { - pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; - } else if (t->u.range.min == 0 && t->u.range.max == 1) { - pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; - } else if (t->u.range.min == 1 && t->u.range.max == (uintN) -1) { - pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; - } else { - if (!t->u.range.greedy) - pc[-1] = REOP_MINIMALQUANT; - pc = WriteCompactIndex(pc, t->u.range.min); - /* - * Write max + 1 to avoid using size_t(max) + 1 bytes - * for (uintN)-1 sentinel. - */ - pc = WriteCompactIndex(pc, t->u.range.max + 1); - } - emitStateSP->nextTermFixup = pc; - pc += OFFSET_LEN; - emitStateSP->continueNode = t; - emitStateSP->continueOp = REOP_ENDCHILD; - ++emitStateSP; - JS_ASSERT((size_t)(emitStateSP - emitStateStack) <= treeDepth); - t = (RENode *) t->kid; - op = t->op; - continue; - - case REOP_ENDCHILD: - if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) - goto jump_too_big; - break; - - case REOP_CLASS: - if (!t->u.ucclass.sense) - pc[-1] = REOP_NCLASS; - pc = WriteCompactIndex(pc, t->u.ucclass.index); - charSet = &re->classList[t->u.ucclass.index]; - charSet->converted = JS_FALSE; - charSet->length = t->u.ucclass.bmsize; - charSet->u.src.startIndex = t->u.ucclass.startIndex; - charSet->u.src.length = t->u.ucclass.kidlen; - charSet->sense = t->u.ucclass.sense; - break; - - default: - break; - } - - t = t->next; - if (t) { - op = t->op; - } else { - if (emitStateSP == emitStateStack) - break; - --emitStateSP; - t = emitStateSP->continueNode; - op = emitStateSP->continueOp; - } - } - - cleanup: - if (emitStateStack) - JS_free(state->context, emitStateStack); - return pc; - - jump_too_big: - js_ReportCompileErrorNumber(state->context, state->tokenStream, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_REGEXP_TOO_COMPLEX); - pc = NULL; - goto cleanup; -} - - -JSRegExp * -js_NewRegExp(JSContext *cx, JSTokenStream *ts, - JSString *str, uintN flags, JSBool flat) -{ - JSRegExp *re; - void *mark; - CompilerState state; - size_t resize; - jsbytecode *endPC; - uintN i; - size_t len; - - re = NULL; - mark = JS_ARENA_MARK(&cx->tempPool); - len = JSSTRING_LENGTH(str); - - state.context = cx; - state.tokenStream = ts; - state.cp = js_UndependString(cx, str); - if (!state.cp) - goto out; - state.cpbegin = state.cp; - state.cpend = state.cp + len; - state.flags = flags; - state.parenCount = 0; - state.classCount = 0; - state.progLength = 0; - state.treeDepth = 0; - state.classBitmapsMem = 0; - for (i = 0; i < CLASS_CACHE_SIZE; i++) - state.classCache[i].start = NULL; - - if (len != 0 && flat) { - state.result = NewRENode(&state, REOP_FLAT); - state.result->u.flat.chr = *state.cpbegin; - state.result->u.flat.length = len; - state.result->kid = (void *) state.cpbegin; - /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ - state.progLength += 1 + GetCompactIndexWidth(0) - + GetCompactIndexWidth(len); - } else { - if (!ParseRegExp(&state)) - goto out; - } - resize = offsetof(JSRegExp, program) + state.progLength + 1; - re = (JSRegExp *) JS_malloc(cx, resize); - if (!re) - goto out; - - re->nrefs = 1; - JS_ASSERT(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); - re->classCount = state.classCount; - if (re->classCount) { - re->classList = (RECharSet *) - JS_malloc(cx, re->classCount * sizeof(RECharSet)); - if (!re->classList) { - js_DestroyRegExp(cx, re); - re = NULL; - goto out; - } - for (i = 0; i < re->classCount; i++) - re->classList[i].converted = JS_FALSE; - } else { - re->classList = NULL; - } - endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); - if (!endPC) { - js_DestroyRegExp(cx, re); - re = NULL; - goto out; - } - *endPC++ = REOP_END; - /* - * Check whether size was overestimated and shrink using realloc. - * This is safe since no pointers to newly parsed regexp or its parts - * besides re exist here. - */ - if ((size_t)(endPC - re->program) != state.progLength + 1) { - JSRegExp *tmp; - JS_ASSERT((size_t)(endPC - re->program) < state.progLength + 1); - resize = offsetof(JSRegExp, program) + (endPC - re->program); - tmp = (JSRegExp *) JS_realloc(cx, re, resize); - if (tmp) - re = tmp; - } - - re->flags = flags; - re->cloneIndex = 0; - re->parenCount = state.parenCount; - re->source = str; - -out: - JS_ARENA_RELEASE(&cx->tempPool, mark); - return re; -} - -JSRegExp * -js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, - JSString *str, JSString *opt, JSBool flat) -{ - uintN flags; - jschar *s; - size_t i, n; - char charBuf[2]; - - flags = 0; - if (opt) { - s = JSSTRING_CHARS(opt); - for (i = 0, n = JSSTRING_LENGTH(opt); i < n; i++) { - switch (s[i]) { - case 'g': - flags |= JSREG_GLOB; - break; - case 'i': - flags |= JSREG_FOLD; - break; - case 'm': - flags |= JSREG_MULTILINE; - break; - default: - charBuf[0] = (char)s[i]; - charBuf[1] = '\0'; - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_FLAG, charBuf); - return NULL; - } - } - } - return js_NewRegExp(cx, ts, str, flags, flat); -} - -/* - * Save the current state of the match - the position in the input - * text as well as the position in the bytecode. The state of any - * parent expressions is also saved (preceding state). - * Contents of parenCount parentheses from parenIndex are also saved. - */ -static REBackTrackData * -PushBackTrackState(REGlobalData *gData, REOp op, - jsbytecode *target, REMatchState *x, const jschar *cp, - size_t parenIndex, size_t parenCount) -{ - size_t i; - REBackTrackData *result = - (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); - - size_t sz = sizeof(REBackTrackData) + - gData->stateStackTop * sizeof(REProgState) + - parenCount * sizeof(RECapture); - - ptrdiff_t btsize = gData->backTrackStackSize; - ptrdiff_t btincr = ((char *)result + sz) - - ((char *)gData->backTrackStack + btsize); - - if (btincr > 0) { - ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; - - btincr = JS_ROUNDUP(btincr, btsize); - JS_ARENA_GROW_CAST(gData->backTrackStack, REBackTrackData *, - &gData->pool, btsize, btincr); - if (!gData->backTrackStack) { - JS_ReportOutOfMemory(gData->cx); - gData->ok = JS_FALSE; - return NULL; - } - gData->backTrackStackSize = btsize + btincr; - result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); - } - gData->backTrackSP = result; - result->sz = gData->cursz; - gData->cursz = sz; - - result->backtrack_op = op; - result->backtrack_pc = target; - result->cp = cp; - result->parenCount = parenCount; - - result->saveStateStackTop = gData->stateStackTop; - JS_ASSERT(gData->stateStackTop); - memcpy(result + 1, gData->stateStack, - sizeof(REProgState) * result->saveStateStackTop); - - if (parenCount != 0) { - result->parenIndex = parenIndex; - memcpy((char *)(result + 1) + - sizeof(REProgState) * result->saveStateStackTop, - &x->parens[parenIndex], - sizeof(RECapture) * parenCount); - for (i = 0; i != parenCount; i++) - x->parens[parenIndex + i].index = -1; - } - - return result; -} - - -/* - * Consecutive literal characters. - */ -#if 0 -static REMatchState * -FlatNMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - size_t length) -{ - size_t i; - if (length > gData->cpend - x->cp) - return NULL; - for (i = 0; i != length; i++) { - if (matchChars[i] != x->cp[i]) - return NULL; - } - x->cp += length; - return x; -} -#endif - -static REMatchState * -FlatNIMatcher(REGlobalData *gData, REMatchState *x, jschar *matchChars, - size_t length) -{ - size_t i; - JS_ASSERT(gData->cpend >= x->cp); - if (length > (size_t)(gData->cpend - x->cp)) - return NULL; - for (i = 0; i != length; i++) { - if (upcase(matchChars[i]) != upcase(x->cp[i])) - return NULL; - } - x->cp += length; - return x; -} - -/* - * 1. Evaluate DecimalEscape to obtain an EscapeValue E. - * 2. If E is not a character then go to step 6. - * 3. Let ch be E's character. - * 4. Let A be a one-element RECharSet containing the character ch. - * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. - * 6. E must be an integer. Let n be that integer. - * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. - * 8. Return an internal Matcher closure that takes two arguments, a State x - * and a Continuation c, and performs the following: - * 1. Let cap be x's captures internal array. - * 2. Let s be cap[n]. - * 3. If s is undefined, then call c(x) and return its result. - * 4. Let e be x's endIndex. - * 5. Let len be s's length. - * 6. Let f be e+len. - * 7. If f>InputLength, return failure. - * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) - * such that Canonicalize(s[i]) is not the same character as - * Canonicalize(Input [e+i]), then return failure. - * 9. Let y be the State (f, cap). - * 10. Call c(y) and return its result. - */ -static REMatchState * -BackrefMatcher(REGlobalData *gData, REMatchState *x, size_t parenIndex) -{ - size_t len, i; - const jschar *parenContent; - RECapture *cap = &x->parens[parenIndex]; - - if (cap->index == -1) - return x; - - len = cap->length; - if (x->cp + len > gData->cpend) - return NULL; - - parenContent = &gData->cpbegin[cap->index]; - if (gData->regexp->flags & JSREG_FOLD) { - for (i = 0; i < len; i++) { - if (upcase(parenContent[i]) != upcase(x->cp[i])) - return NULL; - } - } else { - for (i = 0; i < len; i++) { - if (parenContent[i] != x->cp[i]) - return NULL; - } - } - x->cp += len; - return x; -} - - -/* Add a single character to the RECharSet */ -static void -AddCharacterToCharSet(RECharSet *cs, jschar c) -{ - uintN byteIndex = (uintN)(c >> 3); - JS_ASSERT(c <= cs->length); - cs->u.bits[byteIndex] |= 1 << (c & 0x7); -} - - -/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ -static void -AddCharacterRangeToCharSet(RECharSet *cs, jschar c1, jschar c2) -{ - uintN i; - - uintN byteIndex1 = (uintN)(c1 >> 3); - uintN byteIndex2 = (uintN)(c2 >> 3); - - JS_ASSERT((c2 <= cs->length) && (c1 <= c2)); - - c1 &= 0x7; - c2 &= 0x7; - - if (byteIndex1 == byteIndex2) { - cs->u.bits[byteIndex1] |= ((uint8)0xFF >> (7 - (c2 - c1))) << c1; - } else { - cs->u.bits[byteIndex1] |= 0xFF << c1; - for (i = byteIndex1 + 1; i < byteIndex2; i++) - cs->u.bits[i] = 0xFF; - cs->u.bits[byteIndex2] |= (uint8)0xFF >> (7 - c2); - } -} - -/* Compile the source of the class into a RECharSet */ -static JSBool -ProcessCharSet(REGlobalData *gData, RECharSet *charSet) -{ - const jschar *src, *end; - JSBool inRange = JS_FALSE; - jschar rangeStart = 0; - uintN byteLength, n; - jschar c, thisCh; - intN nDigits, i; - - JS_ASSERT(!charSet->converted); - /* - * Assert that startIndex and length points to chars inside [] inside - * source string. - */ - JS_ASSERT(1 <= charSet->u.src.startIndex); - JS_ASSERT(charSet->u.src.startIndex - < JSSTRING_LENGTH(gData->regexp->source)); - JS_ASSERT(charSet->u.src.length <= JSSTRING_LENGTH(gData->regexp->source) - - 1 - charSet->u.src.startIndex); - - charSet->converted = JS_TRUE; - src = JSSTRING_CHARS(gData->regexp->source) + charSet->u.src.startIndex; - end = src + charSet->u.src.length; - JS_ASSERT(src[-1] == '['); - JS_ASSERT(end[0] == ']'); - - byteLength = (charSet->length >> 3) + 1; - charSet->u.bits = (uint8 *)JS_malloc(gData->cx, byteLength); - if (!charSet->u.bits) { - JS_ReportOutOfMemory(gData->cx); - gData->ok = JS_FALSE; - return JS_FALSE; - } - memset(charSet->u.bits, 0, byteLength); - - if (src == end) - return JS_TRUE; - - if (*src == '^') { - JS_ASSERT(charSet->sense == JS_FALSE); - ++src; - } else { - JS_ASSERT(charSet->sense == JS_TRUE); - } - - while (src != end) { - switch (*src) { - case '\\': - ++src; - c = *src++; - switch (c) { - case 'b': - thisCh = 0x8; - break; - case 'f': - thisCh = 0xC; - break; - case 'n': - thisCh = 0xA; - break; - case 'r': - thisCh = 0xD; - break; - case 't': - thisCh = 0x9; - break; - case 'v': - thisCh = 0xB; - break; - case 'c': - if (src < end && JS_ISWORD(*src)) { - thisCh = (jschar)(*src++ & 0x1F); - } else { - --src; - thisCh = '\\'; - } - break; - case 'x': - nDigits = 2; - goto lexHex; - case 'u': - nDigits = 4; - lexHex: - n = 0; - for (i = 0; (i < nDigits) && (src < end); i++) { - uintN digit; - c = *src++; - if (!isASCIIHexDigit(c, &digit)) { - /* - * Back off to accepting the original '\' - * as a literal - */ - src -= i + 1; - n = '\\'; - break; - } - n = (n << 4) | digit; - } - thisCh = (jschar)n; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - /* - * This is a non-ECMA extension - decimal escapes (in this - * case, octal!) are supposed to be an error inside class - * ranges, but supported here for backwards compatibility. - */ - n = JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - n = 8 * n + JS7_UNDEC(c); - c = *src; - if ('0' <= c && c <= '7') { - src++; - i = 8 * n + JS7_UNDEC(c); - if (i <= 0377) - n = i; - else - src--; - } - } - thisCh = (jschar)n; - break; - - case 'd': - AddCharacterRangeToCharSet(charSet, '0', '9'); - continue; /* don't need range processing */ - case 'D': - AddCharacterRangeToCharSet(charSet, 0, '0' - 1); - AddCharacterRangeToCharSet(charSet, - (jschar)('9' + 1), - (jschar)charSet->length); - continue; - case 's': - for (i = (intN)charSet->length; i >= 0; i--) - if (JS_ISSPACE(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - case 'S': - for (i = (intN)charSet->length; i >= 0; i--) - if (!JS_ISSPACE(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - case 'w': - for (i = (intN)charSet->length; i >= 0; i--) - if (JS_ISWORD(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - case 'W': - for (i = (intN)charSet->length; i >= 0; i--) - if (!JS_ISWORD(i)) - AddCharacterToCharSet(charSet, (jschar)i); - continue; - default: - thisCh = c; - break; - - } - break; - - default: - thisCh = *src++; - break; - - } - if (inRange) { - if (gData->regexp->flags & JSREG_FOLD) { - AddCharacterRangeToCharSet(charSet, upcase(rangeStart), - upcase(thisCh)); - AddCharacterRangeToCharSet(charSet, downcase(rangeStart), - downcase(thisCh)); - } else { - AddCharacterRangeToCharSet(charSet, rangeStart, thisCh); - } - inRange = JS_FALSE; - } else { - if (gData->regexp->flags & JSREG_FOLD) { - AddCharacterToCharSet(charSet, upcase(thisCh)); - AddCharacterToCharSet(charSet, downcase(thisCh)); - } else { - AddCharacterToCharSet(charSet, thisCh); - } - if (src < end - 1) { - if (*src == '-') { - ++src; - inRange = JS_TRUE; - rangeStart = thisCh; - } - } - } - } - return JS_TRUE; -} - -void -js_DestroyRegExp(JSContext *cx, JSRegExp *re) -{ - if (JS_ATOMIC_DECREMENT(&re->nrefs) == 0) { - if (re->classList) { - uintN i; - for (i = 0; i < re->classCount; i++) { - if (re->classList[i].converted) - JS_free(cx, re->classList[i].u.bits); - re->classList[i].u.bits = NULL; - } - JS_free(cx, re->classList); - } - JS_free(cx, re); - } -} - -static JSBool -ReallocStateStack(REGlobalData *gData) -{ - size_t limit = gData->stateStackLimit; - size_t sz = sizeof(REProgState) * limit; - - JS_ARENA_GROW_CAST(gData->stateStack, REProgState *, &gData->pool, sz, sz); - if (!gData->stateStack) { - gData->ok = JS_FALSE; - return JS_FALSE; - } - gData->stateStackLimit = limit + limit; - return JS_TRUE; -} - -#define PUSH_STATE_STACK(data) \ - JS_BEGIN_MACRO \ - ++(data)->stateStackTop; \ - if ((data)->stateStackTop == (data)->stateStackLimit && \ - !ReallocStateStack((data))) { \ - return NULL; \ - } \ - JS_END_MACRO - -/* - * Apply the current op against the given input to see if it's going to match - * or fail. Return false if we don't get a match, true if we do. If updatecp is - * true, then update the current state's cp. Always update startpc to the next - * op. - */ -static REMatchState * -SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, - jsbytecode **startpc, JSBool updatecp) -{ - REMatchState *result = NULL; - jschar matchCh; - size_t parenIndex; - size_t offset, length, index; - jsbytecode *pc = *startpc; /* pc has already been incremented past op */ - jschar *source; - const jschar *startcp = x->cp; - jschar ch; - RECharSet *charSet; - - switch (op) { - case REOP_BOL: - if (x->cp != gData->cpbegin) { - if (!gData->cx->regExpStatics.multiline && - !(gData->regexp->flags & JSREG_MULTILINE)) { - break; - } - if (!RE_IS_LINE_TERM(x->cp[-1])) - break; - } - result = x; - break; - case REOP_EOL: - if (x->cp != gData->cpend) { - if (!gData->cx->regExpStatics.multiline && - !(gData->regexp->flags & JSREG_MULTILINE)) { - break; - } - if (!RE_IS_LINE_TERM(*x->cp)) - break; - } - result = x; - break; - case REOP_WBDRY: - if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ - !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) { - result = x; - } - break; - case REOP_WNONBDRY: - if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ - (x->cp != gData->cpend && JS_ISWORD(*x->cp))) { - result = x; - } - break; - case REOP_DOT: - if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_DIGIT: - if (x->cp != gData->cpend && JS_ISDIGIT(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONDIGIT: - if (x->cp != gData->cpend && !JS_ISDIGIT(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_ALNUM: - if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONALNUM: - if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_SPACE: - if (x->cp != gData->cpend && JS_ISSPACE(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_NONSPACE: - if (x->cp != gData->cpend && !JS_ISSPACE(*x->cp)) { - result = x; - result->cp++; - } - break; - case REOP_BACKREF: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - result = BackrefMatcher(gData, x, parenIndex); - break; - case REOP_FLAT: - pc = ReadCompactIndex(pc, &offset); - JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); - pc = ReadCompactIndex(pc, &length); - JS_ASSERT(1 <= length); - JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); - if (length <= (size_t)(gData->cpend - x->cp)) { - source = JSSTRING_CHARS(gData->regexp->source) + offset; - for (index = 0; index != length; index++) { - if (source[index] != x->cp[index]) - return NULL; - } - x->cp += length; - result = x; - } - break; - case REOP_FLAT1: - matchCh = *pc++; - if (x->cp != gData->cpend && *x->cp == matchCh) { - result = x; - result->cp++; - } - break; - case REOP_FLATi: - pc = ReadCompactIndex(pc, &offset); - JS_ASSERT(offset < JSSTRING_LENGTH(gData->regexp->source)); - pc = ReadCompactIndex(pc, &length); - JS_ASSERT(1 <= length); - JS_ASSERT(length <= JSSTRING_LENGTH(gData->regexp->source) - offset); - source = JSSTRING_CHARS(gData->regexp->source); - result = FlatNIMatcher(gData, x, source + offset, length); - break; - case REOP_FLAT1i: - matchCh = *pc++; - if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { - result = x; - result->cp++; - } - break; - case REOP_UCFLAT1: - matchCh = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp != gData->cpend && *x->cp == matchCh) { - result = x; - result->cp++; - } - break; - case REOP_UCFLAT1i: - matchCh = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp != gData->cpend && upcase(*x->cp) == upcase(matchCh)) { - result = x; - result->cp++; - } - break; - case REOP_CLASS: - pc = ReadCompactIndex(pc, &index); - JS_ASSERT(index < gData->regexp->classCount); - if (x->cp != gData->cpend) { - charSet = &gData->regexp->classList[index]; - JS_ASSERT(charSet->converted); - ch = *x->cp; - index = ch >> 3; - if (charSet->length != 0 && - ch <= charSet->length && - (charSet->u.bits[index] & (1 << (ch & 0x7)))) { - result = x; - result->cp++; - } - } - break; - case REOP_NCLASS: - pc = ReadCompactIndex(pc, &index); - JS_ASSERT(index < gData->regexp->classCount); - if (x->cp != gData->cpend) { - charSet = &gData->regexp->classList[index]; - JS_ASSERT(charSet->converted); - ch = *x->cp; - index = ch >> 3; - if (charSet->length == 0 || - ch > charSet->length || - !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { - result = x; - result->cp++; - } - } - break; - default: - JS_ASSERT(JS_FALSE); - } - if (result) { - if (!updatecp) - x->cp = startcp; - *startpc = pc; - return result; - } - x->cp = startcp; - return NULL; -} - -static REMatchState * -ExecuteREBytecode(REGlobalData *gData, REMatchState *x) -{ - REMatchState *result = NULL; - REBackTrackData *backTrackData; - jsbytecode *nextpc, *testpc; - REOp nextop; - RECapture *cap; - REProgState *curState; - const jschar *startcp; - size_t parenIndex, k; - size_t parenSoFar = 0; - - jschar matchCh1, matchCh2; - RECharSet *charSet; - - JSBranchCallback onbranch = gData->cx->branchCallback; - uintN onbranchCalls = 0; -#define ONBRANCH_CALLS_MASK 127 -#define CHECK_BRANCH() \ - JS_BEGIN_MACRO \ - if (onbranch && \ - (++onbranchCalls & ONBRANCH_CALLS_MASK) == 0 && \ - !(*onbranch)(gData->cx, NULL)) { \ - gData->ok = JS_FALSE; \ - return NULL; \ - } \ - JS_END_MACRO - - JSBool anchor; - jsbytecode *pc = gData->regexp->program; - REOp op = (REOp) *pc++; - - /* - * If the first node is a simple match, step the index into the string - * until that match is made, or fail if it can't be found at all. - */ - if (REOP_IS_SIMPLE(op)) { - anchor = JS_FALSE; - while (x->cp <= gData->cpend) { - nextpc = pc; /* reset back to start each time */ - result = SimpleMatch(gData, x, op, &nextpc, JS_TRUE); - if (result) { - anchor = JS_TRUE; - x = result; - pc = nextpc; /* accept skip to next opcode */ - op = (REOp) *pc++; - break; - } - gData->skipped++; - x->cp++; - } - if (!anchor) - return NULL; - } - - for (;;) { - if (REOP_IS_SIMPLE(op)) { - result = SimpleMatch(gData, x, op, &pc, JS_TRUE); - } else { - curState = &gData->stateStack[gData->stateStackTop]; - switch (op) { - case REOP_EMPTY: - result = x; - break; - - case REOP_ALTPREREQ2: - nextpc = pc + GET_OFFSET(pc); /* start of next op */ - pc += ARG_LEN; - matchCh2 = GET_ARG(pc); - pc += ARG_LEN; - k = GET_ARG(pc); - pc += ARG_LEN; - - if (x->cp != gData->cpend) { - if (*x->cp == matchCh2) - goto doAlt; - - charSet = &gData->regexp->classList[k]; - if (!charSet->converted && !ProcessCharSet(gData, charSet)) - return NULL; - matchCh1 = *x->cp; - k = matchCh1 >> 3; - if ((charSet->length == 0 || - matchCh1 > charSet->length || - !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ - charSet->sense) { - goto doAlt; - } - } - result = NULL; - break; - - case REOP_ALTPREREQ: - nextpc = pc + GET_OFFSET(pc); /* start of next op */ - pc += ARG_LEN; - matchCh1 = GET_ARG(pc); - pc += ARG_LEN; - matchCh2 = GET_ARG(pc); - pc += ARG_LEN; - if (x->cp == gData->cpend || - (*x->cp != matchCh1 && *x->cp != matchCh2)) { - result = NULL; - break; - } - /* else false thru... */ - - case REOP_ALT: - doAlt: - nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ - pc += ARG_LEN; /* start of this alternate */ - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - startcp = x->cp; - if (REOP_IS_SIMPLE(op)) { - if (!SimpleMatch(gData, x, op, &pc, JS_TRUE)) { - op = (REOp) *nextpc++; - pc = nextpc; - continue; - } - result = x; - op = (REOp) *pc++; - } - nextop = (REOp) *nextpc++; - if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) - return NULL; - continue; - - /* - * Occurs at (successful) end of REOP_ALT, - */ - case REOP_JUMP: - --gData->stateStackTop; - pc += GET_OFFSET(pc); - op = (REOp) *pc++; - continue; - - /* - * Occurs at last (successful) end of REOP_ALT, - */ - case REOP_ENDALT: - --gData->stateStackTop; - op = (REOp) *pc++; - continue; - - case REOP_LPAREN: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - if (parenIndex + 1 > parenSoFar) - parenSoFar = parenIndex + 1; - x->parens[parenIndex].index = x->cp - gData->cpbegin; - x->parens[parenIndex].length = 0; - op = (REOp) *pc++; - continue; - - case REOP_RPAREN: - pc = ReadCompactIndex(pc, &parenIndex); - JS_ASSERT(parenIndex < gData->regexp->parenCount); - cap = &x->parens[parenIndex]; - - /* - * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=346090 - * This wallpaper prevents a case where we somehow took a step - * backward in input while minimally-matching an empty string. - */ - if (x->cp < gData->cpbegin + cap->index) - cap->index = -1; - cap->length = x->cp - (gData->cpbegin + cap->index); - op = (REOp) *pc++; - continue; - - case REOP_ASSERT: - nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ - pc += ARG_LEN; /* start of ASSERT child */ - op = (REOp) *pc++; - testpc = pc; - if (REOP_IS_SIMPLE(op) && - !SimpleMatch(gData, x, op, &testpc, JS_FALSE)) { - result = NULL; - break; - } - curState->u.assertion.top = - (char *)gData->backTrackSP - (char *)gData->backTrackStack; - curState->u.assertion.sz = gData->cursz; - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_ASSERTTEST, - nextpc, x, x->cp, 0, 0)) { - return NULL; - } - continue; - - case REOP_ASSERT_NOT: - nextpc = pc + GET_OFFSET(pc); - pc += ARG_LEN; - op = (REOp) *pc++; - testpc = pc; - if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && - SimpleMatch(gData, x, op, &testpc, JS_FALSE) && - *testpc == REOP_ASSERTNOTTEST) { - result = NULL; - break; - } - curState->u.assertion.top - = (char *)gData->backTrackSP - - (char *)gData->backTrackStack; - curState->u.assertion.sz = gData->cursz; - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, - nextpc, x, x->cp, 0, 0)) { - return NULL; - } - continue; - - case REOP_ASSERTTEST: - --gData->stateStackTop; - --curState; - x->cp = gData->cpbegin + curState->index; - gData->backTrackSP = - (REBackTrackData *) ((char *)gData->backTrackStack + - curState->u.assertion.top); - gData->cursz = curState->u.assertion.sz; - if (result) - result = x; - break; - - case REOP_ASSERTNOTTEST: - --gData->stateStackTop; - --curState; - x->cp = gData->cpbegin + curState->index; - gData->backTrackSP = - (REBackTrackData *) ((char *)gData->backTrackStack + - curState->u.assertion.top); - gData->cursz = curState->u.assertion.sz; - result = (!result) ? x : NULL; - break; - - case REOP_END: - if (x) - return x; - break; - - case REOP_STAR: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uintN)-1; - goto quantcommon; - case REOP_PLUS: - curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uintN)-1; - goto quantcommon; - case REOP_OPT: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = 1; - goto quantcommon; - case REOP_QUANT: - pc = ReadCompactIndex(pc, &k); - curState->u.quantifier.min = k; - pc = ReadCompactIndex(pc, &k); - /* max is k - 1 to use one byte for (uintN)-1 sentinel. */ - curState->u.quantifier.max = k - 1; - JS_ASSERT(curState->u.quantifier.min - <= curState->u.quantifier.max); - quantcommon: - if (curState->u.quantifier.max == 0) { - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - result = x; - continue; - } - /* Step over */ - nextpc = pc + ARG_LEN; - op = (REOp) *nextpc++; - startcp = x->cp; - if (REOP_IS_SIMPLE(op)) { - if (!SimpleMatch(gData, x, op, &nextpc, JS_TRUE)) { - if (curState->u.quantifier.min == 0) - result = x; - else - result = NULL; - pc = pc + GET_OFFSET(pc); - break; - } - op = (REOp) *nextpc++; - result = x; - } - curState->index = startcp - gData->cpbegin; - curState->continue_op = REOP_REPEAT; - curState->continue_pc = pc; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min == 0 && - !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, - 0, 0)) { - return NULL; - } - pc = nextpc; - continue; - - case REOP_ENDCHILD: /* marks the end of a quantifier child */ - pc = curState[-1].continue_pc; - op = curState[-1].continue_op; - continue; - - case REOP_REPEAT: - CHECK_BRANCH(); - --curState; - do { - --gData->stateStackTop; - if (!result) { - /* Failed, see if we have enough children. */ - if (curState->u.quantifier.min == 0) - goto repeatDone; - goto break_switch; - } - if (curState->u.quantifier.min == 0 && - x->cp == gData->cpbegin + curState->index) { - /* matched an empty string, that'll get us nowhere */ - result = NULL; - goto break_switch; - } - if (curState->u.quantifier.min != 0) - curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uintN) -1) - curState->u.quantifier.max--; - if (curState->u.quantifier.max == 0) - goto repeatDone; - nextpc = pc + ARG_LEN; - nextop = (REOp) *nextpc; - startcp = x->cp; - if (REOP_IS_SIMPLE(nextop)) { - nextpc++; - if (!SimpleMatch(gData, x, nextop, &nextpc, JS_TRUE)) { - if (curState->u.quantifier.min == 0) - goto repeatDone; - result = NULL; - goto break_switch; - } - result = x; - } - curState->index = startcp - gData->cpbegin; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min == 0 && - !PushBackTrackState(gData, REOP_REPEAT, - pc, x, startcp, - curState->parenSoFar, - parenSoFar - - curState->parenSoFar)) { - return NULL; - } - } while (*nextpc == REOP_ENDCHILD); - pc = nextpc; - op = (REOp) *pc++; - parenSoFar = curState->parenSoFar; - continue; - - repeatDone: - result = x; - pc += GET_OFFSET(pc); - goto break_switch; - - case REOP_MINIMALSTAR: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = (uintN)-1; - goto minimalquantcommon; - case REOP_MINIMALPLUS: - curState->u.quantifier.min = 1; - curState->u.quantifier.max = (uintN)-1; - goto minimalquantcommon; - case REOP_MINIMALOPT: - curState->u.quantifier.min = 0; - curState->u.quantifier.max = 1; - goto minimalquantcommon; - case REOP_MINIMALQUANT: - pc = ReadCompactIndex(pc, &k); - curState->u.quantifier.min = k; - pc = ReadCompactIndex(pc, &k); - /* See REOP_QUANT comments about k - 1. */ - curState->u.quantifier.max = k - 1; - JS_ASSERT(curState->u.quantifier.min - <= curState->u.quantifier.max); - minimalquantcommon: - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (curState->u.quantifier.min != 0) { - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - /* step over */ - pc += OFFSET_LEN; - op = (REOp) *pc++; - } else { - if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, - pc, x, x->cp, 0, 0)) { - return NULL; - } - --gData->stateStackTop; - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - } - continue; - - case REOP_MINIMALREPEAT: - CHECK_BRANCH(); - --gData->stateStackTop; - --curState; - - if (!result) { - /* - * Non-greedy failure - try to consume another child. - */ - if (curState->u.quantifier.max == (uintN) -1 || - curState->u.quantifier.max > 0) { - curState->index = x->cp - gData->cpbegin; - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - pc += ARG_LEN; - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - continue; - } - /* Don't need to adjust pc since we're going to pop. */ - break; - } - if (curState->u.quantifier.min == 0 && - x->cp == gData->cpbegin + curState->index) { - /* Matched an empty string, that'll get us nowhere. */ - result = NULL; - break; - } - if (curState->u.quantifier.min != 0) - curState->u.quantifier.min--; - if (curState->u.quantifier.max != (uintN) -1) - curState->u.quantifier.max--; - if (curState->u.quantifier.min != 0) { - curState->continue_op = REOP_MINIMALREPEAT; - curState->continue_pc = pc; - pc += ARG_LEN; - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - curState->index = x->cp - gData->cpbegin; - PUSH_STATE_STACK(gData); - op = (REOp) *pc++; - continue; - } - curState->index = x->cp - gData->cpbegin; - curState->parenSoFar = parenSoFar; - PUSH_STATE_STACK(gData); - if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, - pc, x, x->cp, - curState->parenSoFar, - parenSoFar - curState->parenSoFar)) { - return NULL; - } - --gData->stateStackTop; - pc = pc + GET_OFFSET(pc); - op = (REOp) *pc++; - continue; - - default: - JS_ASSERT(JS_FALSE); - result = NULL; - } - break_switch:; - } - - /* - * If the match failed and there's a backtrack option, take it. - * Otherwise this is a complete and utter failure. - */ - if (!result) { - if (gData->cursz == 0) - return NULL; - backTrackData = gData->backTrackSP; - gData->cursz = backTrackData->sz; - gData->backTrackSP = - (REBackTrackData *) ((char *)backTrackData - backTrackData->sz); - x->cp = backTrackData->cp; - pc = backTrackData->backtrack_pc; - op = backTrackData->backtrack_op; - gData->stateStackTop = backTrackData->saveStateStackTop; - JS_ASSERT(gData->stateStackTop); - - memcpy(gData->stateStack, backTrackData + 1, - sizeof(REProgState) * backTrackData->saveStateStackTop); - curState = &gData->stateStack[gData->stateStackTop - 1]; - - if (backTrackData->parenCount) { - memcpy(&x->parens[backTrackData->parenIndex], - (char *)(backTrackData + 1) + - sizeof(REProgState) * backTrackData->saveStateStackTop, - sizeof(RECapture) * backTrackData->parenCount); - parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; - } else { - for (k = curState->parenSoFar; k < parenSoFar; k++) - x->parens[k].index = -1; - parenSoFar = curState->parenSoFar; - } - continue; - } - x = result; - - /* - * Continue with the expression. - */ - op = (REOp)*pc++; - } - return NULL; -} - -static REMatchState * -MatchRegExp(REGlobalData *gData, REMatchState *x) -{ - REMatchState *result; - const jschar *cp = x->cp; - const jschar *cp2; - uintN j; - - /* - * Have to include the position beyond the last character - * in order to detect end-of-input/line condition. - */ - for (cp2 = cp; cp2 <= gData->cpend; cp2++) { - gData->skipped = cp2 - cp; - x->cp = cp2; - for (j = 0; j < gData->regexp->parenCount; j++) - x->parens[j].index = -1; - result = ExecuteREBytecode(gData, x); - if (!gData->ok || result) - return result; - gData->backTrackSP = gData->backTrackStack; - gData->cursz = 0; - gData->stateStackTop = 0; - cp2 = cp + gData->skipped; - } - return NULL; -} - - -static REMatchState * -InitMatch(JSContext *cx, REGlobalData *gData, JSRegExp *re) -{ - REMatchState *result; - uintN i; - - gData->backTrackStackSize = INITIAL_BACKTRACK; - JS_ARENA_ALLOCATE_CAST(gData->backTrackStack, REBackTrackData *, - &gData->pool, - INITIAL_BACKTRACK); - if (!gData->backTrackStack) - goto bad; - - gData->backTrackSP = gData->backTrackStack; - gData->cursz = 0; - - gData->stateStackLimit = INITIAL_STATESTACK; - JS_ARENA_ALLOCATE_CAST(gData->stateStack, REProgState *, - &gData->pool, - sizeof(REProgState) * INITIAL_STATESTACK); - if (!gData->stateStack) - goto bad; - - gData->stateStackTop = 0; - gData->cx = cx; - gData->regexp = re; - gData->ok = JS_TRUE; - - JS_ARENA_ALLOCATE_CAST(result, REMatchState *, - &gData->pool, - offsetof(REMatchState, parens) - + re->parenCount * sizeof(RECapture)); - if (!result) - goto bad; - - for (i = 0; i < re->classCount; i++) { - if (!re->classList[i].converted && - !ProcessCharSet(gData, &re->classList[i])) { - return NULL; - } - } - - return result; - -bad: - JS_ReportOutOfMemory(cx); - gData->ok = JS_FALSE; - return NULL; -} - -JSBool -js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, jsval *rval) -{ - REGlobalData gData; - REMatchState *x, *result; - - const jschar *cp, *ep; - size_t i, length, start; - JSSubString *morepar; - JSBool ok; - JSRegExpStatics *res; - ptrdiff_t matchlen; - uintN num, morenum; - JSString *parstr, *matchstr; - JSObject *obj; - - RECapture *parsub = NULL; - - /* - * It's safe to load from cp because JSStrings have a zero at the end, - * and we never let cp get beyond cpend. - */ - start = *indexp; - length = JSSTRING_LENGTH(str); - if (start > length) - start = length; - cp = JSSTRING_CHARS(str); - gData.cpbegin = cp; - gData.cpend = cp + length; - cp += start; - gData.start = start; - gData.skipped = 0; - - JS_InitArenaPool(&gData.pool, "RegExpPool", 8096, 4); - x = InitMatch(cx, &gData, re); - if (!x) { - ok = JS_FALSE; - goto out; - } - x->cp = cp; - - /* - * Call the recursive matcher to do the real work. Return null on mismatch - * whether testing or not. On match, return an extended Array object. - */ - result = MatchRegExp(&gData, x); - ok = gData.ok; - if (!ok) - goto out; - if (!result) { - *rval = JSVAL_NULL; - goto out; - } - cp = result->cp; - i = cp - gData.cpbegin; - *indexp = i; - matchlen = i - (start + gData.skipped); - ep = cp; - cp -= matchlen; - - if (test) { - /* - * Testing for a match and updating cx->regExpStatics: don't allocate - * an array object, do return true. - */ - *rval = JSVAL_TRUE; - - /* Avoid warning. (gcc doesn't detect that obj is needed iff !test); */ - obj = NULL; - } else { - /* - * The array returned on match has element 0 bound to the matched - * string, elements 1 through state.parenCount bound to the paren - * matches, an index property telling the length of the left context, - * and an input property referring to the input string. - */ - obj = js_NewArrayObject(cx, 0, NULL); - if (!obj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(obj); - -#define DEFVAL(val, id) { \ - ok = js_DefineProperty(cx, obj, id, val, \ - JS_PropertyStub, JS_PropertyStub, \ - JSPROP_ENUMERATE, NULL); \ - if (!ok) { \ - cx->weakRoots.newborn[GCX_OBJECT] = NULL; \ - cx->weakRoots.newborn[GCX_STRING] = NULL; \ - goto out; \ - } \ -} - - matchstr = js_NewStringCopyN(cx, cp, matchlen, 0); - if (!matchstr) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - ok = JS_FALSE; - goto out; - } - DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0)); - } - - res = &cx->regExpStatics; - res->input = str; - res->parenCount = re->parenCount; - if (re->parenCount == 0) { - res->lastParen = js_EmptySubString; - } else { - for (num = 0; num < re->parenCount; num++) { - parsub = &result->parens[num]; - if (num < 9) { - if (parsub->index == -1) { - res->parens[num].chars = NULL; - res->parens[num].length = 0; - } else { - res->parens[num].chars = gData.cpbegin + parsub->index; - res->parens[num].length = parsub->length; - } - } else { - morenum = num - 9; - morepar = res->moreParens; - if (!morepar) { - res->moreLength = 10; - morepar = (JSSubString*) - JS_malloc(cx, 10 * sizeof(JSSubString)); - } else if (morenum >= res->moreLength) { - res->moreLength += 10; - morepar = (JSSubString*) - JS_realloc(cx, morepar, - res->moreLength * sizeof(JSSubString)); - } - if (!morepar) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; - ok = JS_FALSE; - goto out; - } - res->moreParens = morepar; - if (parsub->index == -1) { - morepar[morenum].chars = NULL; - morepar[morenum].length = 0; - } else { - morepar[morenum].chars = gData.cpbegin + parsub->index; - morepar[morenum].length = parsub->length; - } - } - if (test) - continue; - if (parsub->index == -1) { - ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), - JSVAL_VOID, NULL, NULL, - JSPROP_ENUMERATE, NULL); - } else { - parstr = js_NewStringCopyN(cx, gData.cpbegin + parsub->index, - parsub->length, 0); - if (!parstr) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; - ok = JS_FALSE; - goto out; - } - ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), - STRING_TO_JSVAL(parstr), NULL, NULL, - JSPROP_ENUMERATE, NULL); - } - if (!ok) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - cx->weakRoots.newborn[GCX_STRING] = NULL; - goto out; - } - } - if (parsub->index == -1) { - res->lastParen = js_EmptySubString; - } else { - res->lastParen.chars = gData.cpbegin + parsub->index; - res->lastParen.length = parsub->length; - } - } - - if (!test) { - /* - * Define the index and input properties last for better for/in loop - * order (so they come after the elements). - */ - DEFVAL(INT_TO_JSVAL(start + gData.skipped), - ATOM_TO_JSID(cx->runtime->atomState.indexAtom)); - DEFVAL(STRING_TO_JSVAL(str), - ATOM_TO_JSID(cx->runtime->atomState.inputAtom)); - } - -#undef DEFVAL - - res->lastMatch.chars = cp; - res->lastMatch.length = matchlen; - - /* - * For JS1.3 and ECMAv2, emulate Perl5 exactly: - * - * js1.3 "hi", "hi there" "hihitherehi therebye" - */ - res->leftContext.chars = JSSTRING_CHARS(str); - res->leftContext.length = start + gData.skipped; - res->rightContext.chars = ep; - res->rightContext.length = gData.cpend - ep; - -out: - JS_FinishArenaPool(&gData.pool); - return ok; -} - -/************************************************************************/ - -enum regexp_tinyid { - REGEXP_SOURCE = -1, - REGEXP_GLOBAL = -2, - REGEXP_IGNORE_CASE = -3, - REGEXP_LAST_INDEX = -4, - REGEXP_MULTILINE = -5 -}; - -#define REGEXP_PROP_ATTRS (JSPROP_PERMANENT|JSPROP_SHARED) - -static JSPropertySpec regexp_props[] = { - {"source", REGEXP_SOURCE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {"global", REGEXP_GLOBAL, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {"ignoreCase", REGEXP_IGNORE_CASE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {"lastIndex", REGEXP_LAST_INDEX, REGEXP_PROP_ATTRS,0,0}, - {"multiline", REGEXP_MULTILINE, REGEXP_PROP_ATTRS | JSPROP_READONLY,0,0}, - {0,0,0,0,0} -}; - -static JSBool -regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSRegExp *re; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - slot = JSVAL_TO_INT(id); - if (slot == REGEXP_LAST_INDEX) - return JS_GetReservedSlot(cx, obj, 0, vp); - - JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) JS_GetInstancePrivate(cx, obj, &js_RegExpClass, NULL); - if (re) { - switch (slot) { - case REGEXP_SOURCE: - *vp = STRING_TO_JSVAL(re->source); - break; - case REGEXP_GLOBAL: - *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_GLOB) != 0); - break; - case REGEXP_IGNORE_CASE: - *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_FOLD) != 0); - break; - case REGEXP_MULTILINE: - *vp = BOOLEAN_TO_JSVAL((re->flags & JSREG_MULTILINE) != 0); - break; - } - } - JS_UNLOCK_OBJ(cx, obj); - return JS_TRUE; -} - -static JSBool -regexp_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok; - jsint slot; - jsdouble lastIndex; - - ok = JS_TRUE; - if (!JSVAL_IS_INT(id)) - return ok; - slot = JSVAL_TO_INT(id); - if (slot == REGEXP_LAST_INDEX) { - if (!js_ValueToNumber(cx, *vp, &lastIndex)) - return JS_FALSE; - lastIndex = js_DoubleToInteger(lastIndex); - ok = js_NewNumberValue(cx, lastIndex, vp) && - JS_SetReservedSlot(cx, obj, 0, *vp); - } - return ok; -} - -/* - * RegExp class static properties and their Perl counterparts: - * - * RegExp.input $_ - * RegExp.multiline $* - * RegExp.lastMatch $& - * RegExp.lastParen $+ - * RegExp.leftContext $` - * RegExp.rightContext $' - */ -enum regexp_static_tinyid { - REGEXP_STATIC_INPUT = -1, - REGEXP_STATIC_MULTILINE = -2, - REGEXP_STATIC_LAST_MATCH = -3, - REGEXP_STATIC_LAST_PAREN = -4, - REGEXP_STATIC_LEFT_CONTEXT = -5, - REGEXP_STATIC_RIGHT_CONTEXT = -6 -}; - -JSBool -js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res) -{ - JS_ClearRegExpStatics(cx); - return js_AddRoot(cx, &res->input, "res->input"); -} - -void -js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res) -{ - if (res->moreParens) { - JS_free(cx, res->moreParens); - res->moreParens = NULL; - } - js_RemoveRoot(cx->runtime, &res->input); -} - -static JSBool -regexp_static_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint slot; - JSRegExpStatics *res; - JSString *str; - JSSubString *sub; - - res = &cx->regExpStatics; - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - slot = JSVAL_TO_INT(id); - switch (slot) { - case REGEXP_STATIC_INPUT: - *vp = res->input ? STRING_TO_JSVAL(res->input) - : JS_GetEmptyStringValue(cx); - return JS_TRUE; - case REGEXP_STATIC_MULTILINE: - *vp = BOOLEAN_TO_JSVAL(res->multiline); - return JS_TRUE; - case REGEXP_STATIC_LAST_MATCH: - sub = &res->lastMatch; - break; - case REGEXP_STATIC_LAST_PAREN: - sub = &res->lastParen; - break; - case REGEXP_STATIC_LEFT_CONTEXT: - sub = &res->leftContext; - break; - case REGEXP_STATIC_RIGHT_CONTEXT: - sub = &res->rightContext; - break; - default: - sub = REGEXP_PAREN_SUBSTRING(res, slot); - break; - } - str = js_NewStringCopyN(cx, sub->chars, sub->length, 0); - if (!str) - return JS_FALSE; - *vp = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -regexp_static_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSRegExpStatics *res; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - res = &cx->regExpStatics; - /* XXX use if-else rather than switch to keep MSVC1.52 from crashing */ - if (JSVAL_TO_INT(id) == REGEXP_STATIC_INPUT) { - if (!JSVAL_IS_STRING(*vp) && - !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp)) { - return JS_FALSE; - } - res->input = JSVAL_TO_STRING(*vp); - } else if (JSVAL_TO_INT(id) == REGEXP_STATIC_MULTILINE) { - if (!JSVAL_IS_BOOLEAN(*vp) && - !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp)) { - return JS_FALSE; - } - res->multiline = JSVAL_TO_BOOLEAN(*vp); - } - return JS_TRUE; -} - -static JSPropertySpec regexp_static_props[] = { - {"input", - REGEXP_STATIC_INPUT, - JSPROP_ENUMERATE|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_setProperty}, - {"multiline", - REGEXP_STATIC_MULTILINE, - JSPROP_ENUMERATE|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_setProperty}, - {"lastMatch", - REGEXP_STATIC_LAST_MATCH, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"lastParen", - REGEXP_STATIC_LAST_PAREN, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"leftContext", - REGEXP_STATIC_LEFT_CONTEXT, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"rightContext", - REGEXP_STATIC_RIGHT_CONTEXT, - JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - - /* XXX should have block scope and local $1, etc. */ - {"$1", 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$2", 1, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$3", 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$4", 3, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$5", 4, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$6", 5, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$7", 6, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$8", 7, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - {"$9", 8, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_SHARED, - regexp_static_getProperty, regexp_static_getProperty}, - - {0,0,0,0,0} -}; - -static void -regexp_finalize(JSContext *cx, JSObject *obj) -{ - JSRegExp *re; - - re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (!re) - return; - js_DestroyRegExp(cx, re); -} - -/* Forward static prototype. */ -static JSBool -regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -regexp_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return regexp_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); -} - -#if JS_HAS_XDR - -#include "jsxdrapi.h" - -static JSBool -regexp_xdrObject(JSXDRState *xdr, JSObject **objp) -{ - JSRegExp *re; - JSString *source; - uint32 flagsword; - JSObject *obj; - - if (xdr->mode == JSXDR_ENCODE) { - re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp); - if (!re) - return JS_FALSE; - source = re->source; - flagsword = ((uint32)re->cloneIndex << 16) | re->flags; - } - if (!JS_XDRString(xdr, &source) || - !JS_XDRUint32(xdr, &flagsword)) { - return JS_FALSE; - } - if (xdr->mode == JSXDR_DECODE) { - obj = js_NewObject(xdr->cx, &js_RegExpClass, NULL, NULL); - if (!obj) - return JS_FALSE; - re = js_NewRegExp(xdr->cx, NULL, source, (uint16)flagsword, JS_FALSE); - if (!re) - return JS_FALSE; - if (!JS_SetPrivate(xdr->cx, obj, re) || - !js_SetLastIndex(xdr->cx, obj, 0)) { - js_DestroyRegExp(xdr->cx, re); - return JS_FALSE; - } - re->cloneIndex = (uint16)(flagsword >> 16); - *objp = obj; - } - return JS_TRUE; -} - -#else /* !JS_HAS_XDR */ - -#define regexp_xdrObject NULL - -#endif /* !JS_HAS_XDR */ - -static uint32 -regexp_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSRegExp *re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (re) - GC_MARK(cx, re->source, "source"); - return 0; -} - -JSClass js_RegExpClass = { - js_RegExp_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | - JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), - JS_PropertyStub, JS_PropertyStub, - regexp_getProperty, regexp_setProperty, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, regexp_finalize, - NULL, NULL, - regexp_call, NULL, - regexp_xdrObject, NULL, - regexp_mark, 0 -}; - -static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0}; - -JSBool -js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSRegExp *re; - const jschar *source; - jschar *chars; - size_t length, nflags; - uintN flags; - JSString *str; - - if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (!re) { - JS_UNLOCK_OBJ(cx, obj); - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - source = JSSTRING_CHARS(re->source); - length = JSSTRING_LENGTH(re->source); - if (length == 0) { - source = empty_regexp_ucstr; - length = sizeof(empty_regexp_ucstr) / sizeof(jschar) - 1; - } - length += 2; - nflags = 0; - for (flags = re->flags; flags != 0; flags &= flags - 1) - nflags++; - chars = (jschar*) JS_malloc(cx, (length + nflags + 1) * sizeof(jschar)); - if (!chars) { - JS_UNLOCK_OBJ(cx, obj); - return JS_FALSE; - } - - chars[0] = '/'; - js_strncpy(&chars[1], source, length - 2); - chars[length-1] = '/'; - if (nflags) { - if (re->flags & JSREG_GLOB) - chars[length++] = 'g'; - if (re->flags & JSREG_FOLD) - chars[length++] = 'i'; - if (re->flags & JSREG_MULTILINE) - chars[length++] = 'm'; - } - JS_UNLOCK_OBJ(cx, obj); - chars[length] = 0; - - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -regexp_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *opt, *str; - JSRegExp *oldre, *re; - JSBool ok, ok2; - JSObject *obj2; - size_t length, nbytes; - const jschar *cp, *start, *end; - jschar *nstart, *ncp, *tmp; - - if (!JS_InstanceOf(cx, obj, &js_RegExpClass, argv)) - return JS_FALSE; - opt = NULL; - if (argc == 0) { - str = cx->runtime->emptyString; - } else { - if (JSVAL_IS_OBJECT(argv[0])) { - /* - * If we get passed in a RegExp object we construct a new - * RegExp that is a duplicate of it by re-compiling the - * original source code. ECMA requires that it be an error - * here if the flags are specified. (We must use the flags - * from the original RegExp also). - */ - obj2 = JSVAL_TO_OBJECT(argv[0]); - if (obj2 && OBJ_GET_CLASS(cx, obj2) == &js_RegExpClass) { - if (argc >= 2 && !JSVAL_IS_VOID(argv[1])) { /* 'flags' passed */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NEWREGEXP_FLAGGED); - return JS_FALSE; - } - JS_LOCK_OBJ(cx, obj2); - re = (JSRegExp *) JS_GetPrivate(cx, obj2); - if (!re) { - JS_UNLOCK_OBJ(cx, obj2); - return JS_FALSE; - } - re = js_NewRegExp(cx, NULL, re->source, re->flags, JS_FALSE); - JS_UNLOCK_OBJ(cx, obj2); - goto created; - } - } - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - if (argc > 1) { - if (JSVAL_IS_VOID(argv[1])) { - opt = NULL; - } else { - opt = js_ValueToString(cx, argv[1]); - if (!opt) - return JS_FALSE; - argv[1] = STRING_TO_JSVAL(opt); - } - } - - /* Escape any naked slashes in the regexp source. */ - length = JSSTRING_LENGTH(str); - start = JSSTRING_CHARS(str); - end = start + length; - nstart = ncp = NULL; - for (cp = start; cp < end; cp++) { - if (*cp == '/' && (cp == start || cp[-1] != '\\')) { - nbytes = (++length + 1) * sizeof(jschar); - if (!nstart) { - nstart = (jschar *) JS_malloc(cx, nbytes); - if (!nstart) - return JS_FALSE; - ncp = nstart + (cp - start); - js_strncpy(nstart, start, cp - start); - } else { - tmp = (jschar *) JS_realloc(cx, nstart, nbytes); - if (!tmp) { - JS_free(cx, nstart); - return JS_FALSE; - } - ncp = tmp + (ncp - nstart); - nstart = tmp; - } - *ncp++ = '\\'; - } - if (nstart) - *ncp++ = *cp; - } - - if (nstart) { - /* Don't forget to store the backstop after the new string. */ - JS_ASSERT((size_t)(ncp - nstart) == length); - *ncp = 0; - str = js_NewString(cx, nstart, length, 0); - if (!str) { - JS_free(cx, nstart); - return JS_FALSE; - } - argv[0] = STRING_TO_JSVAL(str); - } - } - - re = js_NewRegExpOpt(cx, NULL, str, opt, JS_FALSE); -created: - if (!re) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - oldre = (JSRegExp *) JS_GetPrivate(cx, obj); - ok = JS_SetPrivate(cx, obj, re); - ok2 = js_SetLastIndex(cx, obj, 0); - JS_UNLOCK_OBJ(cx, obj); - if (!ok) { - js_DestroyRegExp(cx, re); - return JS_FALSE; - } - if (oldre) - js_DestroyRegExp(cx, oldre); - *rval = OBJECT_TO_JSVAL(obj); - return ok2; -} - -static JSBool -regexp_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - JSBool test, jsval *rval) -{ - JSBool ok; - JSRegExp *re; - jsdouble lastIndex; - JSString *str; - size_t i; - - ok = JS_InstanceOf(cx, obj, &js_RegExpClass, argv); - if (!ok) - return JS_FALSE; - JS_LOCK_OBJ(cx, obj); - re = (JSRegExp *) JS_GetPrivate(cx, obj); - if (!re) { - JS_UNLOCK_OBJ(cx, obj); - return JS_TRUE; - } - - /* NB: we must reach out: after this paragraph, in order to drop re. */ - HOLD_REGEXP(cx, re); - if (re->flags & JSREG_GLOB) { - ok = js_GetLastIndex(cx, obj, &lastIndex); - } else { - lastIndex = 0; - } - JS_UNLOCK_OBJ(cx, obj); - if (!ok) - goto out; - - /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */ - if (argc == 0) { - str = cx->regExpStatics.input; - if (!str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NO_INPUT, - JS_GetStringBytes(re->source), - (re->flags & JSREG_GLOB) ? "g" : "", - (re->flags & JSREG_FOLD) ? "i" : "", - (re->flags & JSREG_MULTILINE) ? "m" : ""); - ok = JS_FALSE; - goto out; - } - } else { - str = js_ValueToString(cx, argv[0]); - if (!str) { - ok = JS_FALSE; - goto out; - } - argv[0] = STRING_TO_JSVAL(str); - } - - if (lastIndex < 0 || JSSTRING_LENGTH(str) < lastIndex) { - ok = js_SetLastIndex(cx, obj, 0); - *rval = JSVAL_NULL; - } else { - i = (size_t) lastIndex; - ok = js_ExecuteRegExp(cx, re, str, &i, test, rval); - if (ok && (re->flags & JSREG_GLOB)) - ok = js_SetLastIndex(cx, obj, (*rval == JSVAL_NULL) ? 0 : i); - } - -out: - DROP_REGEXP(cx, re); - return ok; -} - -static JSBool -regexp_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return regexp_exec_sub(cx, obj, argc, argv, JS_FALSE, rval); -} - -static JSBool -regexp_test(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (!regexp_exec_sub(cx, obj, argc, argv, JS_TRUE, rval)) - return JS_FALSE; - if (*rval != JSVAL_TRUE) - *rval = JSVAL_FALSE; - return JS_TRUE; -} - -static JSFunctionSpec regexp_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, js_regexp_toString, 0,0,0}, -#endif - {js_toString_str, js_regexp_toString, 0,0,0}, - {"compile", regexp_compile, 1,0,0}, - {"exec", regexp_exec, 0,0,0}, - {"test", regexp_test, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* - * If first arg is regexp and no flags are given, just return the arg. - * (regexp_compile detects the regexp + flags case and throws a - * TypeError.) See 10.15.3.1. - */ - if ((argc < 2 || JSVAL_IS_VOID(argv[1])) && - !JSVAL_IS_PRIMITIVE(argv[0]) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[0])) == &js_RegExpClass) { - *rval = argv[0]; - return JS_TRUE; - } - - /* Otherwise, replace obj with a new RegExp object. */ - obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); - if (!obj) - return JS_FALSE; - - /* - * regexp_compile does not use rval to root its temporaries - * so we can use it to root obj. - */ - *rval = OBJECT_TO_JSVAL(obj); - } - return regexp_compile(cx, obj, argc, argv, rval); -} - -JSObject * -js_InitRegExpClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *ctor; - jsval rval; - - proto = JS_InitClass(cx, obj, NULL, &js_RegExpClass, RegExp, 1, - regexp_props, regexp_methods, - regexp_static_props, NULL); - - if (!proto || !(ctor = JS_GetConstructor(cx, proto))) - return NULL; - if (!JS_AliasProperty(cx, ctor, "input", "$_") || - !JS_AliasProperty(cx, ctor, "multiline", "$*") || - !JS_AliasProperty(cx, ctor, "lastMatch", "$&") || - !JS_AliasProperty(cx, ctor, "lastParen", "$+") || - !JS_AliasProperty(cx, ctor, "leftContext", "$`") || - !JS_AliasProperty(cx, ctor, "rightContext", "$'")) { - goto bad; - } - - /* Give RegExp.prototype private data so it matches the empty string. */ - if (!regexp_compile(cx, proto, 0, NULL, &rval)) - goto bad; - return proto; - -bad: - JS_DeleteProperty(cx, obj, js_RegExpClass.name); - return NULL; -} - -JSObject * -js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, - jschar *chars, size_t length, uintN flags) -{ - JSString *str; - JSObject *obj; - JSRegExp *re; - JSTempValueRooter tvr; - - str = js_NewStringCopyN(cx, chars, length, 0); - if (!str) - return NULL; - re = js_NewRegExp(cx, ts, str, flags, JS_FALSE); - if (!re) - return NULL; - JS_PUSH_TEMP_ROOT_STRING(cx, str, &tvr); - obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, re)) { - js_DestroyRegExp(cx, re); - obj = NULL; - } - if (obj && !js_SetLastIndex(cx, obj, 0)) - obj = NULL; - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -JSObject * -js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent) -{ - JSObject *clone; - JSRegExp *re; - - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); - clone = js_NewObject(cx, &js_RegExpClass, NULL, parent); - if (!clone) - return NULL; - re = JS_GetPrivate(cx, obj); - if (!JS_SetPrivate(cx, clone, re) || !js_SetLastIndex(cx, clone, 0)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - HOLD_REGEXP(cx, re); - return clone; -} - -JSBool -js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex) -{ - jsval v; - - return JS_GetReservedSlot(cx, obj, 0, &v) && - js_ValueToNumber(cx, v, lastIndex); -} - -JSBool -js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex) -{ - jsval v; - - return js_NewNumberValue(cx, lastIndex, &v) && - JS_SetReservedSlot(cx, obj, 0, v); -} diff --git a/spidermonkey/libjs/jsregexp.h b/spidermonkey/libjs/jsregexp.h deleted file mode 100644 index 5078983..0000000 --- a/spidermonkey/libjs/jsregexp.h +++ /dev/null @@ -1,183 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsregexp_h___ -#define jsregexp_h___ -/* - * JS regular expression interface. - */ -#include -#include "jspubtd.h" -#include "jsstr.h" - -#ifdef JS_THREADSAFE -#include "jsdhash.h" -#endif - -struct JSRegExpStatics { - JSString *input; /* input string to match (perl $_, GC root) */ - JSBool multiline; /* whether input contains newlines (perl $*) */ - uint16 parenCount; /* number of valid elements in parens[] */ - uint16 moreLength; /* number of allocated elements in moreParens */ - JSSubString parens[9]; /* last set of parens matched (perl $1, $2) */ - JSSubString *moreParens; /* null or realloc'd vector for $10, etc. */ - JSSubString lastMatch; /* last string matched (perl $&) */ - JSSubString lastParen; /* last paren matched (perl $+) */ - JSSubString leftContext; /* input to left of last match (perl $`) */ - JSSubString rightContext; /* input to right of last match (perl $') */ -}; - -/* - * This struct holds a bitmap representation of a class from a regexp. - * There's a list of these referenced by the classList field in the JSRegExp - * struct below. The initial state has startIndex set to the offset in the - * original regexp source of the beginning of the class contents. The first - * use of the class converts the source representation into a bitmap. - * - */ -typedef struct RECharSet { - JSPackedBool converted; - JSPackedBool sense; - uint16 length; - union { - uint8 *bits; - struct { - size_t startIndex; - size_t length; - } src; - } u; -} RECharSet; - -/* - * This macro is safe because moreParens is guaranteed to be allocated and big - * enough to hold parenCount, or else be null when parenCount is 0. - */ -#define REGEXP_PAREN_SUBSTRING(res, num) \ - (((jsuint)(num) < (jsuint)(res)->parenCount) \ - ? ((jsuint)(num) < 9) \ - ? &(res)->parens[num] \ - : &(res)->moreParens[(num) - 9] \ - : &js_EmptySubString) - -typedef struct RENode RENode; - -struct JSRegExp { - jsrefcount nrefs; /* reference count */ - uint16 flags; /* flags, see jsapi.h's JSREG_* defines */ - uint16 cloneIndex; /* index in fp->vars or funobj->slots of - cloned regexp object */ - size_t parenCount; /* number of parenthesized submatches */ - size_t classCount; /* count [...] bitmaps */ - RECharSet *classList; /* list of [...] bitmaps */ - JSString *source; /* locked source string, sans // */ - jsbytecode program[1]; /* regular expression bytecode */ -}; - -extern JSRegExp * -js_NewRegExp(JSContext *cx, JSTokenStream *ts, - JSString *str, uintN flags, JSBool flat); - -extern JSRegExp * -js_NewRegExpOpt(JSContext *cx, JSTokenStream *ts, - JSString *str, JSString *opt, JSBool flat); - -#define HOLD_REGEXP(cx, re) JS_ATOMIC_INCREMENT(&(re)->nrefs) -#define DROP_REGEXP(cx, re) js_DestroyRegExp(cx, re) - -extern void -js_DestroyRegExp(JSContext *cx, JSRegExp *re); - -/* - * Execute re on input str at *indexp, returning null in *rval on mismatch. - * On match, return true if test is true, otherwise return an array object. - * Update *indexp and cx->regExpStatics always on match. - */ -extern JSBool -js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp, - JSBool test, jsval *rval); - -/* - * These two add and remove GC roots, respectively, so their calls must be - * well-ordered. - */ -extern JSBool -js_InitRegExpStatics(JSContext *cx, JSRegExpStatics *res); - -extern void -js_FreeRegExpStatics(JSContext *cx, JSRegExpStatics *res); - -#define JSVAL_IS_REGEXP(cx, v) \ - (JSVAL_IS_OBJECT(v) && JSVAL_TO_OBJECT(v) && \ - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_RegExpClass) - -extern JSClass js_RegExpClass; - -extern JSObject * -js_InitRegExpClass(JSContext *cx, JSObject *obj); - -/* - * Export js_regexp_toString to the decompiler. - */ -extern JSBool -js_regexp_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -/* - * Create, serialize/deserialize, or clone a RegExp object. - */ -extern JSObject * -js_NewRegExpObject(JSContext *cx, JSTokenStream *ts, - jschar *chars, size_t length, uintN flags); - -extern JSBool -js_XDRRegExp(JSXDRState *xdr, JSObject **objp); - -extern JSObject * -js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent); - -/* - * Get and set the per-object (clone or clone-parent) lastIndex slot. - */ -extern JSBool -js_GetLastIndex(JSContext *cx, JSObject *obj, jsdouble *lastIndex); - -extern JSBool -js_SetLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex); - -#endif /* jsregexp_h___ */ diff --git a/spidermonkey/libjs/jsscan.c b/spidermonkey/libjs/jsscan.c deleted file mode 100644 index f9f7436..0000000 --- a/spidermonkey/libjs/jsscan.c +++ /dev/null @@ -1,2101 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set sw=4 ts=8 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS lexical scanner. - */ -#include "jsstddef.h" -#include /* first to avoid trouble on some systems */ -#include -#include -#include -#ifdef HAVE_MEMORY_H -#include -#endif -#include -#include -#include -#include "jstypes.h" -#include "jsarena.h" /* Added by JSIFY */ -#include "jsutil.h" /* Added by JSIFY */ -#include "jsdtoa.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsemit.h" -#include "jsexn.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsscan.h" -#include "jsscript.h" - -#if JS_HAS_XML_SUPPORT -#include "jsparse.h" -#include "jsxml.h" -#endif - -#define JS_KEYWORD(keyword, type, op, version) \ - const char js_##keyword##_str[] = #keyword; -#include "jskeyword.tbl" -#undef JS_KEYWORD - -struct keyword { - const char *chars; /* C string with keyword text */ - JSTokenType tokentype; /* JSTokenType */ - JSOp op; /* JSOp */ - JSVersion version; /* JSVersion */ -}; - -static const struct keyword keyword_defs[] = { -#define JS_KEYWORD(keyword, type, op, version) \ - {js_##keyword##_str, type, op, version}, -#include "jskeyword.tbl" -#undef JS_KEYWORD -}; - -#define KEYWORD_COUNT (sizeof keyword_defs / sizeof keyword_defs[0]) - -static const struct keyword * -FindKeyword(const jschar *s, size_t length) -{ - register size_t i; - const struct keyword *kw; - const char *chars; - - JS_ASSERT(length != 0); - -#define JSKW_LENGTH() length -#define JSKW_AT(column) s[column] -#define JSKW_GOT_MATCH(index) i = (index); goto got_match; -#define JSKW_TEST_GUESS(index) i = (index); goto test_guess; -#define JSKW_NO_MATCH() goto no_match; -#include "jsautokw.h" -#undef JSKW_NO_MATCH -#undef JSKW_TEST_GUESS -#undef JSKW_GOT_MATCH -#undef JSKW_AT -#undef JSKW_LENGTH - - got_match: - return &keyword_defs[i]; - - test_guess: - kw = &keyword_defs[i]; - chars = kw->chars; - do { - if (*s++ != (unsigned char)(*chars++)) - goto no_match; - } while (--length != 0); - return kw; - - no_match: - return NULL; -} - -JSTokenType -js_CheckKeyword(const jschar *str, size_t length) -{ - const struct keyword *kw; - - JS_ASSERT(length != 0); - kw = FindKeyword(str, length); - return kw ? kw->tokentype : TOK_EOF; -} - -JS_FRIEND_API(void) -js_MapKeywords(void (*mapfun)(const char *)) -{ - size_t i; - - for (i = 0; i != KEYWORD_COUNT; ++i) - mapfun(keyword_defs[i].chars); -} - -JSTokenStream * -js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, - const char *filename, uintN lineno, - JSPrincipals *principals) -{ - JSTokenStream *ts; - - ts = js_NewBufferTokenStream(cx, base, length); - if (!ts) - return NULL; - ts->filename = filename; - ts->lineno = lineno; - if (principals) - JSPRINCIPALS_HOLD(cx, principals); - ts->principals = principals; - return ts; -} - -#define TBMIN 64 - -static JSBool -GrowTokenBuf(JSStringBuffer *sb, size_t newlength) -{ - JSContext *cx; - jschar *base; - ptrdiff_t offset, length; - size_t tbsize; - JSArenaPool *pool; - - cx = sb->data; - base = sb->base; - offset = PTRDIFF(sb->ptr, base, jschar); - pool = &cx->tempPool; - if (!base) { - tbsize = TBMIN * sizeof(jschar); - length = TBMIN - 1; - JS_ARENA_ALLOCATE_CAST(base, jschar *, pool, tbsize); - } else { - length = PTRDIFF(sb->limit, base, jschar); - if ((size_t)length >= ~(size_t)0 / sizeof(jschar)) { - base = NULL; - } else { - tbsize = (length + 1) * sizeof(jschar); - length += length + 1; - JS_ARENA_GROW_CAST(base, jschar *, pool, tbsize, tbsize); - } - } - if (!base) { - JS_ReportOutOfMemory(cx); - sb->base = STRING_BUFFER_ERROR_BASE; - return JS_FALSE; - } - sb->base = base; - sb->limit = base + length; - sb->ptr = base + offset; - return JS_TRUE; -} - -JS_FRIEND_API(JSTokenStream *) -js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length) -{ - size_t nb; - JSTokenStream *ts; - - nb = sizeof(JSTokenStream) + JS_LINE_LIMIT * sizeof(jschar); - JS_ARENA_ALLOCATE_CAST(ts, JSTokenStream *, &cx->tempPool, nb); - if (!ts) { - JS_ReportOutOfMemory(cx); - return NULL; - } - memset(ts, 0, nb); - ts->lineno = 1; - ts->linebuf.base = ts->linebuf.limit = ts->linebuf.ptr = (jschar *)(ts + 1); - ts->userbuf.base = (jschar *)base; - ts->userbuf.limit = (jschar *)base + length; - ts->userbuf.ptr = (jschar *)base; - ts->tokenbuf.grow = GrowTokenBuf; - ts->tokenbuf.data = cx; - ts->listener = cx->runtime->sourceHandler; - ts->listenerData = cx->runtime->sourceHandlerData; - return ts; -} - -JS_FRIEND_API(JSTokenStream *) -js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp) -{ - jschar *base; - JSTokenStream *ts; - FILE *file; - - JS_ARENA_ALLOCATE_CAST(base, jschar *, &cx->tempPool, - JS_LINE_LIMIT * sizeof(jschar)); - if (!base) - return NULL; - ts = js_NewBufferTokenStream(cx, base, JS_LINE_LIMIT); - if (!ts) - return NULL; - if (!filename || strcmp(filename, "-") == 0) { - file = defaultfp; - } else { - file = fopen(filename, "r"); - if (!file) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN, - filename, "No such file or directory"); - return NULL; - } - } - ts->userbuf.ptr = ts->userbuf.limit; - ts->file = file; - ts->filename = filename; - return ts; -} - -JS_FRIEND_API(JSBool) -js_CloseTokenStream(JSContext *cx, JSTokenStream *ts) -{ - if (ts->flags & TSF_OWNFILENAME) - JS_free(cx, (void *) ts->filename); - if (ts->principals) - JSPRINCIPALS_DROP(cx, ts->principals); - return !ts->file || fclose(ts->file) == 0; -} - -JS_FRIEND_API(int) -js_fgets(char *buf, int size, FILE *file) -{ - int n, i, c; - JSBool crflag; - - n = size - 1; - if (n < 0) - return -1; - - crflag = JS_FALSE; - for (i = 0; i < n && (c = getc(file)) != EOF; i++) { - buf[i] = c; - if (c == '\n') { /* any \n ends a line */ - i++; /* keep the \n; we know there is room for \0 */ - break; - } - if (crflag) { /* \r not followed by \n ends line at the \r */ - ungetc(c, file); - break; /* and overwrite c in buf with \0 */ - } - crflag = (c == '\r'); - } - - buf[i] = '\0'; - return i; -} - -static int32 -GetChar(JSTokenStream *ts) -{ - int32 c; - ptrdiff_t i, j, len, olen; - JSBool crflag; - char cbuf[JS_LINE_LIMIT]; - jschar *ubuf, *nl; - - if (ts->ungetpos != 0) { - c = ts->ungetbuf[--ts->ungetpos]; - } else { - do { - if (ts->linebuf.ptr == ts->linebuf.limit) { - len = PTRDIFF(ts->userbuf.limit, ts->userbuf.ptr, jschar); - if (len <= 0) { - if (!ts->file) { - ts->flags |= TSF_EOF; - return EOF; - } - - /* Fill ts->userbuf so that \r and \r\n convert to \n. */ - crflag = (ts->flags & TSF_CRFLAG) != 0; - len = js_fgets(cbuf, JS_LINE_LIMIT - crflag, ts->file); - if (len <= 0) { - ts->flags |= TSF_EOF; - return EOF; - } - olen = len; - ubuf = ts->userbuf.base; - i = 0; - if (crflag) { - ts->flags &= ~TSF_CRFLAG; - if (cbuf[0] != '\n') { - ubuf[i++] = '\n'; - len++; - ts->linepos--; - } - } - for (j = 0; i < len; i++, j++) - ubuf[i] = (jschar) (unsigned char) cbuf[j]; - ts->userbuf.limit = ubuf + len; - ts->userbuf.ptr = ubuf; - } - if (ts->listener) { - ts->listener(ts->filename, ts->lineno, ts->userbuf.ptr, len, - &ts->listenerTSData, ts->listenerData); - } - - nl = ts->saveEOL; - if (!nl) { - /* - * Any one of \n, \r, or \r\n ends a line (the longest - * match wins). Also allow the Unicode line and paragraph - * separators. - */ - for (nl = ts->userbuf.ptr; nl < ts->userbuf.limit; nl++) { - /* - * Try to prevent value-testing on most characters by - * filtering out characters that aren't 000x or 202x. - */ - if ((*nl & 0xDFD0) == 0) { - if (*nl == '\n') - break; - if (*nl == '\r') { - if (nl + 1 < ts->userbuf.limit && nl[1] == '\n') - nl++; - break; - } - if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) - break; - } - } - } - - /* - * If there was a line terminator, copy thru it into linebuf. - * Else copy JS_LINE_LIMIT-1 bytes into linebuf. - */ - if (nl < ts->userbuf.limit) - len = PTRDIFF(nl, ts->userbuf.ptr, jschar) + 1; - if (len >= JS_LINE_LIMIT) { - len = JS_LINE_LIMIT - 1; - ts->saveEOL = nl; - } else { - ts->saveEOL = NULL; - } - js_strncpy(ts->linebuf.base, ts->userbuf.ptr, len); - ts->userbuf.ptr += len; - olen = len; - - /* - * Make sure linebuf contains \n for EOL (don't do this in - * userbuf because the user's string might be readonly). - */ - if (nl < ts->userbuf.limit) { - if (*nl == '\r') { - if (ts->linebuf.base[len-1] == '\r') { - /* - * Does the line segment end in \r? We must check - * for a \n at the front of the next segment before - * storing a \n into linebuf. This case matters - * only when we're reading from a file. - */ - if (nl + 1 == ts->userbuf.limit && ts->file) { - len--; - ts->flags |= TSF_CRFLAG; /* clear NLFLAG? */ - if (len == 0) { - /* - * This can happen when a segment ends in - * \r\r. Start over. ptr == limit in this - * case, so we'll fall into buffer-filling - * code. - */ - return GetChar(ts); - } - } else { - ts->linebuf.base[len-1] = '\n'; - } - } - } else if (*nl == '\n') { - if (nl > ts->userbuf.base && - nl[-1] == '\r' && - ts->linebuf.base[len-2] == '\r') { - len--; - JS_ASSERT(ts->linebuf.base[len] == '\n'); - ts->linebuf.base[len-1] = '\n'; - } - } else if (*nl == LINE_SEPARATOR || *nl == PARA_SEPARATOR) { - ts->linebuf.base[len-1] = '\n'; - } - } - - /* Reset linebuf based on adjusted segment length. */ - ts->linebuf.limit = ts->linebuf.base + len; - ts->linebuf.ptr = ts->linebuf.base; - - /* Update position of linebuf within physical userbuf line. */ - if (!(ts->flags & TSF_NLFLAG)) - ts->linepos += ts->linelen; - else - ts->linepos = 0; - if (ts->linebuf.limit[-1] == '\n') - ts->flags |= TSF_NLFLAG; - else - ts->flags &= ~TSF_NLFLAG; - - /* Update linelen from original segment length. */ - ts->linelen = olen; - } - c = *ts->linebuf.ptr++; - } while (JS_ISFORMAT(c)); - } - if (c == '\n') - ts->lineno++; - return c; -} - -static void -UngetChar(JSTokenStream *ts, int32 c) -{ - if (c == EOF) - return; - JS_ASSERT(ts->ungetpos < sizeof ts->ungetbuf / sizeof ts->ungetbuf[0]); - if (c == '\n') - ts->lineno--; - ts->ungetbuf[ts->ungetpos++] = (jschar)c; -} - -static int32 -PeekChar(JSTokenStream *ts) -{ - int32 c; - - c = GetChar(ts); - UngetChar(ts, c); - return c; -} - -/* - * Peek n chars ahead into ts. Return true if n chars were read, false if - * there weren't enough characters in the input stream. This function cannot - * be used to peek into or past a newline. - */ -static JSBool -PeekChars(JSTokenStream *ts, intN n, jschar *cp) -{ - intN i, j; - int32 c; - - for (i = 0; i < n; i++) { - c = GetChar(ts); - if (c == EOF) - break; - if (c == '\n') { - UngetChar(ts, c); - break; - } - cp[i] = (jschar)c; - } - for (j = i - 1; j >= 0; j--) - UngetChar(ts, cp[j]); - return i == n; -} - -static void -SkipChars(JSTokenStream *ts, intN n) -{ - while (--n >= 0) - GetChar(ts); -} - -static JSBool -MatchChar(JSTokenStream *ts, int32 expect) -{ - int32 c; - - c = GetChar(ts); - if (c == expect) - return JS_TRUE; - UngetChar(ts, c); - return JS_FALSE; -} - -static JSBool -ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, JSErrorReport *report, - JSBool charArgs, va_list ap) -{ - JSTempValueRooter linetvr; - JSString *linestr = NULL; - JSTokenStream *ts = NULL; - JSCodeGenerator *cg = NULL; - JSParseNode *pn = NULL; - JSErrorReporter onError; - JSTokenPos *tp; - JSStackFrame *fp; - uintN index; - char *message; - JSBool warning; - - memset(report, 0, sizeof (struct JSErrorReport)); - report->flags = flags; - report->errorNumber = errorNumber; - message = NULL; - - if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, - errorNumber, &message, report, &warning, - charArgs, ap)) { - return JS_FALSE; - } - - JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &linetvr); - - switch (flags & JSREPORT_HANDLE) { - case JSREPORT_TS: - ts = handle; - break; - case JSREPORT_CG: - cg = handle; - break; - case JSREPORT_PN: - pn = handle; - ts = pn->pn_ts; - break; - } - - JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); - /* - * We are typically called with non-null ts and null cg from jsparse.c. - * We can be called with null ts from the regexp compilation functions. - * The code generator (jsemit.c) may pass null ts and non-null cg. - */ - do { - if (ts) { - report->filename = ts->filename; - if (pn) { - report->lineno = pn->pn_pos.begin.lineno; - if (report->lineno != ts->lineno) - break; - } - report->lineno = ts->lineno; - linestr = js_NewStringCopyN(cx, ts->linebuf.base, - PTRDIFF(ts->linebuf.limit, - ts->linebuf.base, - jschar), - 0); - linetvr.u.string = linestr; - report->linebuf = linestr - ? JS_GetStringBytes(linestr) - : NULL; - tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos; - if (pn) - tp = &pn->pn_pos; - - /* - * FIXME: What should instead happen here is that we should - * find error-tokens in userbuf, if !ts->file. That will - * allow us to deliver a more helpful error message, which - * includes all or part of the bad string or bad token. The - * code here yields something that looks truncated. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=352970 - */ - index = 0; - if (tp->begin.lineno == tp->end.lineno) { - if (tp->begin.index < ts->linepos) - break; - - index = tp->begin.index - ts->linepos; - } - - report->tokenptr = linestr ? report->linebuf + index : NULL; - report->uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; - report->uctokenptr = linestr ? report->uclinebuf + index : NULL; - break; - } - - if (cg) { - report->filename = cg->filename; - report->lineno = CG_CURRENT_LINE(cg); - break; - } - - /* - * If we can't find out where the error was based on the current - * frame, see if the next frame has a script/pc combo we can use. - */ - for (fp = cx->fp; fp; fp = fp->down) { - if (fp->script && fp->pc) { - report->filename = fp->script->filename; - report->lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - break; - } - } - } while (0); - - /* - * If there's a runtime exception type associated with this error - * number, set that as the pending exception. For errors occuring at - * compile time, this is very likely to be a JSEXN_SYNTAXERR. - * - * If an exception is thrown but not caught, the JSREPORT_EXCEPTION - * flag will be set in report.flags. Proper behavior for an error - * reporter is to ignore a report with this flag for all but top-level - * compilation errors. The exception will remain pending, and so long - * as the non-top-level "load", "eval", or "compile" native function - * returns false, the top-level reporter will eventually receive the - * uncaught exception report. - * - * XXX it'd probably be best if there was only one call to this - * function, but there seem to be two error reporter call points. - */ - onError = cx->errorReporter; - - /* - * Try to raise an exception only if there isn't one already set -- - * otherwise the exception will describe the last compile-time error, - * which is likely spurious. - */ - if (!ts || !(ts->flags & TSF_ERROR)) { - if (js_ErrorToException(cx, message, report)) - onError = NULL; - } - - /* - * Suppress any compile-time errors that don't occur at the top level. - * This may still fail, as interplevel may be zero in contexts where we - * don't really want to call the error reporter, as when js is called - * by other code which could catch the error. - */ - if (cx->interpLevel != 0 && !JSREPORT_IS_WARNING(flags)) - onError = NULL; - - if (onError) { - JSDebugErrorHook hook = cx->runtime->debugErrorHook; - - /* - * If debugErrorHook is present then we give it a chance to veto - * sending the error on to the regular error reporter. - */ - if (hook && !hook(cx, message, report, - cx->runtime->debugErrorHookData)) { - onError = NULL; - } - } - if (onError) - (*onError)(cx, message, report); - - if (message) - JS_free(cx, message); - if (report->ucmessage) - JS_free(cx, (void *)report->ucmessage); - - JS_POP_TEMP_ROOT(cx, &linetvr); - - if (ts && !JSREPORT_IS_WARNING(flags)) { - /* Set the error flag to suppress spurious reports. */ - ts->flags |= TSF_ERROR; - } - - return warning; -} - -JSBool -js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...) -{ - va_list ap; - JSErrorReport report; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - va_start(ap, errorNumber); - warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, - &report, JS_TRUE, ap); - va_end(ap); - - /* - * We have to do this here because js_ReportCompileErrorNumberUC doesn't - * need to do this. - */ - if (report.messageArgs) { - int i = 0; - while (report.messageArgs[i]) - JS_free(cx, (void *)report.messageArgs[i++]); - JS_free(cx, (void *)report.messageArgs); - } - - return warning; -} - -JSBool -js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...) -{ - va_list ap; - JSErrorReport report; - JSBool warning; - - if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) - return JS_TRUE; - - va_start(ap, errorNumber); - warning = ReportCompileErrorNumber(cx, handle, flags, errorNumber, - &report, JS_FALSE, ap); - va_end(ap); - - if (report.messageArgs) - JS_free(cx, (void *)report.messageArgs); - - return warning; -} - -static JSBool -GrowStringBuffer(JSStringBuffer *sb, size_t newlength) -{ - ptrdiff_t offset; - jschar *bp; - - offset = PTRDIFF(sb->ptr, sb->base, jschar); - JS_ASSERT(offset >= 0); - newlength += offset + 1; - if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar)) - bp = realloc(sb->base, newlength * sizeof(jschar)); - else - bp = NULL; - if (!bp) { - free(sb->base); - sb->base = STRING_BUFFER_ERROR_BASE; - return JS_FALSE; - } - sb->base = bp; - sb->ptr = bp + offset; - sb->limit = bp + newlength - 1; - return JS_TRUE; -} - -static void -FreeStringBuffer(JSStringBuffer *sb) -{ - JS_ASSERT(STRING_BUFFER_OK(sb)); - if (sb->base) - free(sb->base); -} - -void -js_InitStringBuffer(JSStringBuffer *sb) -{ - sb->base = sb->limit = sb->ptr = NULL; - sb->data = NULL; - sb->grow = GrowStringBuffer; - sb->free = FreeStringBuffer; -} - -void -js_FinishStringBuffer(JSStringBuffer *sb) -{ - sb->free(sb); -} - -#define ENSURE_STRING_BUFFER(sb,n) \ - ((sb)->ptr + (n) <= (sb)->limit || sb->grow(sb, n)) - -static void -FastAppendChar(JSStringBuffer *sb, jschar c) -{ - if (!STRING_BUFFER_OK(sb)) - return; - if (!ENSURE_STRING_BUFFER(sb, 1)) - return; - *sb->ptr++ = c; -} - -void -js_AppendChar(JSStringBuffer *sb, jschar c) -{ - jschar *bp; - - if (!STRING_BUFFER_OK(sb)) - return; - if (!ENSURE_STRING_BUFFER(sb, 1)) - return; - bp = sb->ptr; - *bp++ = c; - *bp = 0; - sb->ptr = bp; -} - -#if JS_HAS_XML_SUPPORT - -void -js_RepeatChar(JSStringBuffer *sb, jschar c, uintN count) -{ - jschar *bp; - - if (!STRING_BUFFER_OK(sb) || count == 0) - return; - if (!ENSURE_STRING_BUFFER(sb, count)) - return; - for (bp = sb->ptr; count; --count) - *bp++ = c; - *bp = 0; - sb->ptr = bp; -} - -void -js_AppendCString(JSStringBuffer *sb, const char *asciiz) -{ - size_t length; - jschar *bp; - - if (!STRING_BUFFER_OK(sb) || *asciiz == '\0') - return; - length = strlen(asciiz); - if (!ENSURE_STRING_BUFFER(sb, length)) - return; - for (bp = sb->ptr; length; --length) - *bp++ = (jschar) *asciiz++; - *bp = 0; - sb->ptr = bp; -} - -void -js_AppendJSString(JSStringBuffer *sb, JSString *str) -{ - size_t length; - jschar *bp; - - if (!STRING_BUFFER_OK(sb)) - return; - length = JSSTRING_LENGTH(str); - if (length == 0 || !ENSURE_STRING_BUFFER(sb, length)) - return; - bp = sb->ptr; - js_strncpy(bp, JSSTRING_CHARS(str), length); - bp += length; - *bp = 0; - sb->ptr = bp; -} - -static JSBool -GetXMLEntity(JSContext *cx, JSTokenStream *ts) -{ - ptrdiff_t offset, length, i; - int32 c, d; - JSBool ispair; - jschar *bp, digit; - char *bytes; - JSErrNum msg; - - /* Put the entity, including the '&' already scanned, in ts->tokenbuf. */ - offset = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar); - FastAppendChar(&ts->tokenbuf, '&'); - while ((c = GetChar(ts)) != ';') { - if (c == EOF || c == '\n') { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_END_OF_XML_ENTITY); - return JS_FALSE; - } - FastAppendChar(&ts->tokenbuf, (jschar) c); - } - - /* Let length be the number of jschars after the '&', including the ';'. */ - length = PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) - offset; - bp = ts->tokenbuf.base + offset; - c = d = 0; - ispair = JS_FALSE; - if (length > 2 && bp[1] == '#') { - /* Match a well-formed XML Character Reference. */ - i = 2; - if (length > 3 && JS_TOLOWER(bp[i]) == 'x') { - if (length > 9) /* at most 6 hex digits allowed */ - goto badncr; - while (++i < length) { - digit = bp[i]; - if (!JS7_ISHEX(digit)) - goto badncr; - c = (c << 4) + JS7_UNHEX(digit); - } - } else { - while (i < length) { - digit = bp[i++]; - if (!JS7_ISDEC(digit)) - goto badncr; - c = (c * 10) + JS7_UNDEC(digit); - if (c < 0) - goto badncr; - } - } - - if (0x10000 <= c && c <= 0x10FFFF) { - /* Form a surrogate pair (c, d) -- c is the high surrogate. */ - d = 0xDC00 + (c & 0x3FF); - c = 0xD7C0 + (c >> 10); - ispair = JS_TRUE; - } else { - /* Enforce the http://www.w3.org/TR/REC-xml/#wf-Legalchar WFC. */ - if (c != 0x9 && c != 0xA && c != 0xD && - !(0x20 <= c && c <= 0xD7FF) && - !(0xE000 <= c && c <= 0xFFFD)) { - goto badncr; - } - } - } else { - /* Try to match one of the five XML 1.0 predefined entities. */ - switch (length) { - case 3: - if (bp[2] == 't') { - if (bp[1] == 'l') - c = '<'; - else if (bp[1] == 'g') - c = '>'; - } - break; - case 4: - if (bp[1] == 'a' && bp[2] == 'm' && bp[3] == 'p') - c = '&'; - break; - case 5: - if (bp[3] == 'o') { - if (bp[1] == 'a' && bp[2] == 'p' && bp[4] == 's') - c = '\''; - else if (bp[1] == 'q' && bp[2] == 'u' && bp[4] == 't') - c = '"'; - } - break; - } - if (c == 0) { - msg = JSMSG_UNKNOWN_XML_ENTITY; - goto bad; - } - } - - /* If we matched, retract ts->tokenbuf and store the entity's value. */ - *bp++ = (jschar) c; - if (ispair) - *bp++ = (jschar) d; - *bp = 0; - ts->tokenbuf.ptr = bp; - return JS_TRUE; - -badncr: - msg = JSMSG_BAD_XML_NCR; -bad: - /* No match: throw a TypeError per ECMA-357 10.3.2.1 step 8(a). */ - bytes = js_DeflateString(cx, bp + 1, - PTRDIFF(ts->tokenbuf.ptr, bp, jschar) - 1); - if (bytes) { - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - msg, bytes); - JS_free(cx, bytes); - } - return JS_FALSE; -} - -#endif /* JS_HAS_XML_SUPPORT */ - -JSTokenType -js_PeekToken(JSContext *cx, JSTokenStream *ts) -{ - JSTokenType tt; - - if (ts->lookahead != 0) { - tt = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].type; - } else { - tt = js_GetToken(cx, ts); - js_UngetToken(ts); - } - return tt; -} - -JSTokenType -js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts) -{ - JSTokenType tt; - - if (!ON_CURRENT_LINE(ts, CURRENT_TOKEN(ts).pos)) - return TOK_EOL; - ts->flags |= TSF_NEWLINES; - tt = js_PeekToken(cx, ts); - ts->flags &= ~TSF_NEWLINES; - return tt; -} - -/* - * We have encountered a '\': check for a Unicode escape sequence after it, - * returning the character code value if we found a Unicode escape sequence. - * Otherwise, non-destructively return the original '\'. - */ -static int32 -GetUnicodeEscape(JSTokenStream *ts) -{ - jschar cp[5]; - int32 c; - - if (PeekChars(ts, 5, cp) && cp[0] == 'u' && - JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]) && - JS7_ISHEX(cp[3]) && JS7_ISHEX(cp[4])) - { - c = (((((JS7_UNHEX(cp[1]) << 4) - + JS7_UNHEX(cp[2])) << 4) - + JS7_UNHEX(cp[3])) << 4) - + JS7_UNHEX(cp[4]); - SkipChars(ts, 5); - return c; - } - return '\\'; -} - -static JSToken * -NewToken(JSTokenStream *ts, ptrdiff_t adjust) -{ - JSToken *tp; - - ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; - tp = &CURRENT_TOKEN(ts); - tp->ptr = ts->linebuf.ptr + adjust; - tp->pos.begin.index = ts->linepos + - PTRDIFF(tp->ptr, ts->linebuf.base, jschar) - - ts->ungetpos; - tp->pos.begin.lineno = tp->pos.end.lineno = (uint16)ts->lineno; - return tp; -} - -JSTokenType -js_GetToken(JSContext *cx, JSTokenStream *ts) -{ - JSTokenType tt; - int32 c, qc; - JSToken *tp; - JSAtom *atom; - JSBool hadUnicodeEscape; - const struct keyword *kw; - -#define INIT_TOKENBUF() (ts->tokenbuf.ptr = ts->tokenbuf.base) -#define TOKENBUF_LENGTH() PTRDIFF(ts->tokenbuf.ptr, ts->tokenbuf.base, jschar) -#define TOKENBUF_OK() STRING_BUFFER_OK(&ts->tokenbuf) -#define TOKENBUF_TO_ATOM() (TOKENBUF_OK() \ - ? js_AtomizeChars(cx, \ - TOKENBUF_BASE(), \ - TOKENBUF_LENGTH(), \ - 0) \ - : NULL) -#define ADD_TO_TOKENBUF(c) FastAppendChar(&ts->tokenbuf, (jschar) (c)) - -/* The following 4 macros should only be used when TOKENBUF_OK() is true. */ -#define TOKENBUF_BASE() (ts->tokenbuf.base) -#define TOKENBUF_CHAR(i) (ts->tokenbuf.base[i]) -#define TRIM_TOKENBUF(i) (ts->tokenbuf.ptr = ts->tokenbuf.base + i) -#define NUL_TERM_TOKENBUF() (*ts->tokenbuf.ptr = 0) - - /* Check for a pushed-back token resulting from mismatching lookahead. */ - while (ts->lookahead != 0) { - JS_ASSERT(!(ts->flags & TSF_XMLTEXTMODE)); - ts->lookahead--; - ts->cursor = (ts->cursor + 1) & NTOKENS_MASK; - tt = CURRENT_TOKEN(ts).type; - if (tt != TOK_EOL || (ts->flags & TSF_NEWLINES)) - return tt; - } - - /* If there was a fatal error, keep returning TOK_ERROR. */ - if (ts->flags & TSF_ERROR) - return TOK_ERROR; - -#if JS_HAS_XML_SUPPORT - if (ts->flags & TSF_XMLTEXTMODE) { - tt = TOK_XMLSPACE; /* veto if non-space, return TOK_XMLTEXT */ - tp = NewToken(ts, 0); - INIT_TOKENBUF(); - qc = (ts->flags & TSF_XMLONLYMODE) ? '<' : '{'; - - while ((c = GetChar(ts)) != qc && c != '<' && c != EOF) { - if (c == '&' && qc == '<') { - if (!GetXMLEntity(cx, ts)) - goto error; - tt = TOK_XMLTEXT; - continue; - } - - if (!JS_ISXMLSPACE(c)) - tt = TOK_XMLTEXT; - ADD_TO_TOKENBUF(c); - } - UngetChar(ts, c); - - if (TOKENBUF_LENGTH() == 0) { - atom = NULL; - } else { - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - } - tp->pos.end.lineno = (uint16)ts->lineno; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - goto out; - } - - if (ts->flags & TSF_XMLTAGMODE) { - tp = NewToken(ts, 0); - c = GetChar(ts); - if (JS_ISXMLSPACE(c)) { - do { - c = GetChar(ts); - } while (JS_ISXMLSPACE(c)); - UngetChar(ts, c); - tt = TOK_XMLSPACE; - goto out; - } - - if (c == EOF) { - tt = TOK_EOF; - goto out; - } - - INIT_TOKENBUF(); - if (JS_ISXMLNSSTART(c)) { - JSBool sawColon = JS_FALSE; - - ADD_TO_TOKENBUF(c); - while ((c = GetChar(ts)) != EOF && JS_ISXMLNAME(c)) { - if (c == ':') { - int nextc; - - if (sawColon || - (nextc = PeekChar(ts), - ((ts->flags & TSF_XMLONLYMODE) || nextc != '{') && - !JS_ISXMLNAME(nextc))) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_ERROR, - JSMSG_BAD_XML_QNAME); - goto error; - } - sawColon = JS_TRUE; - } - - ADD_TO_TOKENBUF(c); - } - - UngetChar(ts, c); - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - tt = TOK_XMLNAME; - goto out; - } - - switch (c) { - case '{': - if (ts->flags & TSF_XMLONLYMODE) - goto bad_xml_char; - tt = TOK_LC; - goto out; - - case '=': - tt = TOK_ASSIGN; - goto out; - - case '"': - case '\'': - qc = c; - while ((c = GetChar(ts)) != qc) { - if (c == EOF) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNTERMINATED_STRING); - goto error; - } - - /* - * XML attribute values are double-quoted when pretty-printed, - * so escape " if it is expressed directly in a single-quoted - * attribute value. - */ - if (c == '"' && !(ts->flags & TSF_XMLONLYMODE)) { - JS_ASSERT(qc == '\''); - js_AppendCString(&ts->tokenbuf, js_quot_entity_str); - continue; - } - - if (c == '&' && (ts->flags & TSF_XMLONLYMODE)) { - if (!GetXMLEntity(cx, ts)) - goto error; - continue; - } - - ADD_TO_TOKENBUF(c); - } - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->pos.end.lineno = (uint16)ts->lineno; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - tt = TOK_XMLATTR; - goto out; - - case '>': - tt = TOK_XMLTAGC; - goto out; - - case '/': - if (MatchChar(ts, '>')) { - tt = TOK_XMLPTAGC; - goto out; - } - /* FALL THROUGH */ - - bad_xml_char: - default: - js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR, - JSMSG_BAD_XML_CHARACTER); - goto error; - } - /* NOTREACHED */ - } -#endif /* JS_HAS_XML_SUPPORT */ - -retry: - do { - c = GetChar(ts); - if (c == '\n') { - ts->flags &= ~TSF_DIRTYLINE; - if (ts->flags & TSF_NEWLINES) - break; - } - } while (JS_ISSPACE(c)); - - tp = NewToken(ts, -1); - if (c == EOF) { - tt = TOK_EOF; - goto out; - } - - hadUnicodeEscape = JS_FALSE; - if (JS_ISIDSTART(c) || - (c == '\\' && - (c = GetUnicodeEscape(ts), - hadUnicodeEscape = JS_ISIDSTART(c)))) { - INIT_TOKENBUF(); - for (;;) { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - if (c == '\\') { - c = GetUnicodeEscape(ts); - if (!JS_ISIDENT(c)) - break; - hadUnicodeEscape = JS_TRUE; - } else { - if (!JS_ISIDENT(c)) - break; - } - } - UngetChar(ts, c); - - /* - * Check for keywords unless we saw Unicode escape or parser asks - * to ignore keywords. - */ - if (!hadUnicodeEscape && - !(ts->flags & TSF_KEYWORD_IS_NAME) && - TOKENBUF_OK() && - (kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) { - if (kw->tokentype == TOK_RESERVED) { - if (!js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING | - JSREPORT_STRICT, - JSMSG_RESERVED_ID, - kw->chars)) { - goto error; - } - } else if (kw->version <= JSVERSION_NUMBER(cx)) { - tt = kw->tokentype; - tp->t_op = (JSOp) kw->op; - goto out; - } - } - - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->t_op = JSOP_NAME; - tp->t_atom = atom; - tt = TOK_NAME; - goto out; - } - - if (JS7_ISDEC(c) || (c == '.' && JS7_ISDEC(PeekChar(ts)))) { - jsint radix; - const jschar *endptr; - jsdouble dval; - - radix = 10; - INIT_TOKENBUF(); - - if (c == '0') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - if (JS_TOLOWER(c) == 'x') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - radix = 16; - } else if (JS7_ISDEC(c)) { - radix = 8; - } - } - - while (JS7_ISHEX(c)) { - if (radix < 16) { - if (JS7_ISLET(c)) - break; - - /* - * We permit 08 and 09 as decimal numbers, which makes our - * behaviour a superset of the ECMA numeric grammar. We might - * not always be so permissive, so we warn about it. - */ - if (radix == 8 && c >= '8') { - if (!js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | - JSREPORT_WARNING, - JSMSG_BAD_OCTAL, - c == '8' ? "08" : "09")) { - goto error; - } - radix = 10; - } - } - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } - - if (radix == 10 && (c == '.' || JS_TOLOWER(c) == 'e')) { - if (c == '.') { - do { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } while (JS7_ISDEC(c)); - } - if (JS_TOLOWER(c) == 'e') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - if (c == '+' || c == '-') { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } - if (!JS7_ISDEC(c)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_MISSING_EXPONENT); - goto error; - } - do { - ADD_TO_TOKENBUF(c); - c = GetChar(ts); - } while (JS7_ISDEC(c)); - } - } - - /* Put back the next char and NUL-terminate tokenbuf for js_strto*. */ - UngetChar(ts, c); - ADD_TO_TOKENBUF(0); - - if (!TOKENBUF_OK()) - goto error; - if (radix == 10) { - if (!js_strtod(cx, TOKENBUF_BASE(), &endptr, &dval)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_OUT_OF_MEMORY); - goto error; - } - } else { - if (!js_strtointeger(cx, TOKENBUF_BASE(), &endptr, radix, &dval)) { - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_OUT_OF_MEMORY); - goto error; - } - } - tp->t_dval = dval; - tt = TOK_NUMBER; - goto out; - } - - if (c == '"' || c == '\'') { - qc = c; - INIT_TOKENBUF(); - while ((c = GetChar(ts)) != qc) { - if (c == '\n' || c == EOF) { - UngetChar(ts, c); - js_ReportCompileErrorNumber(cx, ts, - JSREPORT_TS | JSREPORT_ERROR, - JSMSG_UNTERMINATED_STRING); - goto error; - } - if (c == '\\') { - switch (c = GetChar(ts)) { - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'v': c = '\v'; break; - - default: - if ('0' <= c && c < '8') { - int32 val = JS7_UNDEC(c); - - c = PeekChar(ts); - if ('0' <= c && c < '8') { - val = 8 * val + JS7_UNDEC(c); - GetChar(ts); - c = PeekChar(ts); - if ('0' <= c && c < '8') { - int32 save = val; - val = 8 * val + JS7_UNDEC(c); - if (val <= 0377) - GetChar(ts); - else - val = save; - } - } - - c = (jschar)val; - } else if (c == 'u') { - jschar cp[4]; - if (PeekChars(ts, 4, cp) && - JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && - JS7_ISHEX(cp[2]) && JS7_ISHEX(cp[3])) { - c = (((((JS7_UNHEX(cp[0]) << 4) - + JS7_UNHEX(cp[1])) << 4) - + JS7_UNHEX(cp[2])) << 4) - + JS7_UNHEX(cp[3]); - SkipChars(ts, 4); - } - } else if (c == 'x') { - jschar cp[2]; - if (PeekChars(ts, 2, cp) && - JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1])) { - c = (JS7_UNHEX(cp[0]) << 4) + JS7_UNHEX(cp[1]); - SkipChars(ts, 2); - } - } else if (c == '\n' && JS_VERSION_IS_ECMA(cx)) { - /* ECMA follows C by removing escaped newlines. */ - continue; - } - break; - } - } - ADD_TO_TOKENBUF(c); - } - atom = TOKENBUF_TO_ATOM(); - if (!atom) - goto error; - tp->pos.end.lineno = (uint16)ts->lineno; - tp->t_op = JSOP_STRING; - tp->t_atom = atom; - tt = TOK_STRING; - goto out; - } - - switch (c) { - case '\n': tt = TOK_EOL; goto eol_out; - case ';': tt = TOK_SEMI; break; - case '[': tt = TOK_LB; break; - case ']': tt = TOK_RB; break; - case '{': tt = TOK_LC; break; - case '}': tt = TOK_RC; break; - case '(': tt = TOK_LP; break; - case ')': tt = TOK_RP; break; - case ',': tt = TOK_COMMA; break; - case '?': tt = TOK_HOOK; break; - - case '.': -#if JS_HAS_XML_SUPPORT - if (MatchChar(ts, c)) - tt = TOK_DBLDOT; - else -#endif - tt = TOK_DOT; - break; - - case ':': -#if JS_HAS_XML_SUPPORT - if (MatchChar(ts, c)) { - tt = TOK_DBLCOLON; - break; - } -#endif - /* - * Default so compiler can modify to JSOP_GETTER if 'p getter: v' in an - * object initializer, likewise for setter. - */ - tp->t_op = JSOP_NOP; - tt = TOK_COLON; - break; - - case '|': - if (MatchChar(ts, c)) { - tt = TOK_OR; - } else if (MatchChar(ts, '=')) { - tp->t_op = JSOP_BITOR; - tt = TOK_ASSIGN; - } else { - tt = TOK_BITOR; - } - break; - - case '^': - if (MatchChar(ts, '=')) { - tp->t_op = JSOP_BITXOR; - tt = TOK_ASSIGN; - } else { - tt = TOK_BITXOR; - } - break; - - case '&': - if (MatchChar(ts, c)) { - tt = TOK_AND; - } else if (MatchChar(ts, '=')) { - tp->t_op = JSOP_BITAND; - tt = TOK_ASSIGN; - } else { - tt = TOK_BITAND; - } - break; - - case '=': - if (MatchChar(ts, c)) { - tp->t_op = MatchChar(ts, c) ? JSOP_NEW_EQ : (JSOp)cx->jsop_eq; - tt = TOK_EQOP; - } else { - tp->t_op = JSOP_NOP; - tt = TOK_ASSIGN; - } - break; - - case '!': - if (MatchChar(ts, '=')) { - tp->t_op = MatchChar(ts, '=') ? JSOP_NEW_NE : (JSOp)cx->jsop_ne; - tt = TOK_EQOP; - } else { - tp->t_op = JSOP_NOT; - tt = TOK_UNARYOP; - } - break; - -#if JS_HAS_XML_SUPPORT - case '@': - tt = TOK_AT; - break; -#endif - - case '<': -#if JS_HAS_XML_SUPPORT - /* - * After much testing, it's clear that Postel's advice to protocol - * designers ("be liberal in what you accept, and conservative in what - * you send") invites a natural-law repercussion for JS as "protocol": - * - * "If you are liberal in what you accept, others will utterly fail to - * be conservative in what they send." - * - * Which means you will get within every //-style comment unless we have to. So we set - * TSF_IN_HTML_COMMENT when a either on a clean line, or - * only if (ts->flags & TSF_IN_HTML_COMMENT), in a //-style comment. - * - * This still works as before given a malformed comment hiding hack such as: - * - * - * - * It does not cope with malformed comment hiding hacks where --> is hidden - * by C-style comments, or on a dirty line. Such cases are already broken. - */ -#define TSF_IN_HTML_COMMENT 0x2000 - -/* Ignore keywords and return TOK_NAME instead to the parser. */ -#define TSF_KEYWORD_IS_NAME 0x4000 - -/* Unicode separators that are treated as line terminators, in addition to \n, \r */ -#define LINE_SEPARATOR 0x2028 -#define PARA_SEPARATOR 0x2029 - -/* - * Create a new token stream, either from an input buffer or from a file. - * Return null on file-open or memory-allocation failure. - * - * NB: All of js_New{,Buffer,File}TokenStream() return a pointer to transient - * memory in the current context's temp pool. This memory is deallocated via - * JS_ARENA_RELEASE() after parsing is finished. - */ -extern JSTokenStream * -js_NewTokenStream(JSContext *cx, const jschar *base, size_t length, - const char *filename, uintN lineno, JSPrincipals *principals); - -extern JS_FRIEND_API(JSTokenStream *) -js_NewBufferTokenStream(JSContext *cx, const jschar *base, size_t length); - -extern JS_FRIEND_API(JSTokenStream *) -js_NewFileTokenStream(JSContext *cx, const char *filename, FILE *defaultfp); - -extern JS_FRIEND_API(JSBool) -js_CloseTokenStream(JSContext *cx, JSTokenStream *ts); - -extern JS_FRIEND_API(int) -js_fgets(char *buf, int size, FILE *file); - -/* - * If the given char array forms JavaScript keyword, return corresponding - * token. Otherwise return TOK_EOF. - */ -extern JSTokenType -js_CheckKeyword(const jschar *chars, size_t length); - -#define js_IsKeyword(chars, length) \ - (js_CheckKeyword(chars, length) != TOK_EOF) - -/* - * Friend-exported API entry point to call a mapping function on each reserved - * identifier in the scanner's keyword table. - */ -extern JS_FRIEND_API(void) -js_MapKeywords(void (*mapfun)(const char *)); - -/* - * Report a compile-time error by its number, using ts or cg to show context. - * Return true for a warning, false for an error. - */ -extern JSBool -js_ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...); - -extern JSBool -js_ReportCompileErrorNumberUC(JSContext *cx, void *handle, uintN flags, - uintN errorNumber, ...); - -/* Steal some JSREPORT_* bits (see jsapi.h) to tell handle's type. */ -#define JSREPORT_HANDLE 0x300 -#define JSREPORT_TS 0x000 -#define JSREPORT_CG 0x100 -#define JSREPORT_PN 0x200 - -/* - * Look ahead one token and return its type. - */ -extern JSTokenType -js_PeekToken(JSContext *cx, JSTokenStream *ts); - -extern JSTokenType -js_PeekTokenSameLine(JSContext *cx, JSTokenStream *ts); - -/* - * Get the next token from ts. - */ -extern JSTokenType -js_GetToken(JSContext *cx, JSTokenStream *ts); - -/* - * Push back the last scanned token onto ts. - */ -extern void -js_UngetToken(JSTokenStream *ts); - -/* - * Get the next token from ts if its type is tt. - */ -extern JSBool -js_MatchToken(JSContext *cx, JSTokenStream *ts, JSTokenType tt); - -JS_END_EXTERN_C - -#endif /* jsscan_h___ */ diff --git a/spidermonkey/libjs/jsscope.c b/spidermonkey/libjs/jsscope.c deleted file mode 100644 index 49b55a6..0000000 --- a/spidermonkey/libjs/jsscope.c +++ /dev/null @@ -1,1776 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS symbol tables. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsarena.h" -#include "jsbit.h" -#include "jsclist.h" -#include "jsdhash.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsdbgapi.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsscope.h" -#include "jsstr.h" - -JSScope * -js_GetMutableScope(JSContext *cx, JSObject *obj) -{ - JSScope *scope, *newscope; - - scope = OBJ_SCOPE(obj); - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); - if (scope->object == obj) - return scope; - newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), - obj); - if (!newscope) - return NULL; - JS_LOCK_SCOPE(cx, newscope); - obj->map = js_HoldObjectMap(cx, &newscope->map); - scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); - JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); - return newscope; -} - -/* - * JSScope uses multiplicative hashing, _a la_ jsdhash.[ch], but specialized - * to minimize footprint. But if a scope has fewer than SCOPE_HASH_THRESHOLD - * entries, we use linear search and avoid allocating scope->table. - */ -#define SCOPE_HASH_THRESHOLD 6 -#define MIN_SCOPE_SIZE_LOG2 4 -#define MIN_SCOPE_SIZE JS_BIT(MIN_SCOPE_SIZE_LOG2) -#define SCOPE_TABLE_NBYTES(n) ((n) * sizeof(JSScopeProperty *)) - -static void -InitMinimalScope(JSScope *scope) -{ - scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2; - scope->entryCount = scope->removedCount = 0; - scope->table = NULL; - scope->lastProp = NULL; -} - -static JSBool -CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) -{ - int sizeLog2; - JSScopeProperty *sprop, **spp; - - JS_ASSERT(!scope->table); - JS_ASSERT(scope->lastProp); - - if (scope->entryCount > SCOPE_HASH_THRESHOLD) { - /* - * Ouch: calloc failed at least once already -- let's try again, - * overallocating to hold at least twice the current population. - */ - sizeLog2 = JS_CeilingLog2(2 * scope->entryCount); - scope->hashShift = JS_DHASH_BITS - sizeLog2; - } else { - JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2); - sizeLog2 = MIN_SCOPE_SIZE_LOG2; - } - - scope->table = (JSScopeProperty **) - calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *)); - if (!scope->table) { - if (report) - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *)); - - scope->hashShift = JS_DHASH_BITS - sizeLog2; - for (sprop = scope->lastProp; sprop; sprop = sprop->parent) { - spp = js_SearchScope(scope, sprop->id, JS_TRUE); - SPROP_STORE_PRESERVING_COLLISION(spp, sprop); - } - return JS_TRUE; -} - -JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj) -{ - JSScope *scope; - - scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); - if (!scope) - return NULL; - - js_InitObjectMap(&scope->map, nrefs, ops, clasp); - scope->object = obj; - scope->flags = 0; - InitMinimalScope(scope); - -#ifdef JS_THREADSAFE - scope->ownercx = cx; - memset(&scope->lock, 0, sizeof scope->lock); - - /* - * Set u.link = NULL, not u.count = 0, in case the target architecture's - * null pointer has a non-zero integer representation. - */ - scope->u.link = NULL; - -#ifdef DEBUG - scope->file[0] = scope->file[1] = scope->file[2] = scope->file[3] = NULL; - scope->line[0] = scope->line[1] = scope->line[2] = scope->line[3] = 0; -#endif -#endif - - JS_RUNTIME_METER(cx->runtime, liveScopes); - JS_RUNTIME_METER(cx->runtime, totalScopes); - return scope; -} - -#ifdef DEBUG_SCOPE_COUNT -extern void -js_unlog_scope(JSScope *scope); -#endif - -void -js_DestroyScope(JSContext *cx, JSScope *scope) -{ -#ifdef DEBUG_SCOPE_COUNT - js_unlog_scope(scope); -#endif - -#ifdef JS_THREADSAFE - /* Scope must be single-threaded at this point, so set scope->ownercx. */ - JS_ASSERT(scope->u.count == 0); - scope->ownercx = cx; - js_FinishLock(&scope->lock); -#endif - if (scope->table) - JS_free(cx, scope->table); - -#ifdef DEBUG - JS_LOCK_RUNTIME_VOID(cx->runtime, - cx->runtime->liveScopeProps -= scope->entryCount); -#endif - JS_RUNTIME_UNMETER(cx->runtime, liveScopes); - JS_free(cx, scope); -} - -#ifdef DUMP_SCOPE_STATS -typedef struct JSScopeStats { - jsrefcount searches; - jsrefcount steps; - jsrefcount hits; - jsrefcount misses; - jsrefcount stepHits; - jsrefcount stepMisses; - jsrefcount adds; - jsrefcount redundantAdds; - jsrefcount addFailures; - jsrefcount changeFailures; - jsrefcount compresses; - jsrefcount grows; - jsrefcount removes; - jsrefcount removeFrees; - jsrefcount uselessRemoves; - jsrefcount shrinks; -} JSScopeStats; - -JS_FRIEND_DATA(JSScopeStats) js_scope_stats; - -# define METER(x) JS_ATOMIC_INCREMENT(&js_scope_stats.x) -#else -# define METER(x) /* nothing */ -#endif - -/* - * Double hashing needs the second hash code to be relatively prime to table - * size, so we simply make hash2 odd. The inputs to multiplicative hash are - * the golden ratio, expressed as a fixed-point 32 bit fraction, and the int - * property index or named property's atom number (observe that most objects - * have either no indexed properties, or almost all indexed and a few names, - * so collisions between index and atom number are unlikely). - */ -#define SCOPE_HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO) -#define SCOPE_HASH1(hash0,shift) ((hash0) >> (shift)) -#define SCOPE_HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1) - -JS_FRIEND_API(JSScopeProperty **) -js_SearchScope(JSScope *scope, jsid id, JSBool adding) -{ - JSHashNumber hash0, hash1, hash2; - int hashShift, sizeLog2; - JSScopeProperty *stored, *sprop, **spp, **firstRemoved; - uint32 sizeMask; - - METER(searches); - if (!scope->table) { - /* Not enough properties to justify hashing: search from lastProp. */ - JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope)); - for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) { - if (sprop->id == id) { - METER(hits); - return spp; - } - } - METER(misses); - return spp; - } - - /* Compute the primary hash address. */ - hash0 = SCOPE_HASH0(id); - hashShift = scope->hashShift; - hash1 = SCOPE_HASH1(hash0, hashShift); - spp = scope->table + hash1; - - /* Miss: return space for a new entry. */ - stored = *spp; - if (SPROP_IS_FREE(stored)) { - METER(misses); - return spp; - } - - /* Hit: return entry. */ - sprop = SPROP_CLEAR_COLLISION(stored); - if (sprop && sprop->id == id) { - METER(hits); - return spp; - } - - /* Collision: double hash. */ - sizeLog2 = JS_DHASH_BITS - hashShift; - hash2 = SCOPE_HASH2(hash0, sizeLog2, hashShift); - sizeMask = JS_BITMASK(sizeLog2); - - /* Save the first removed entry pointer so we can recycle it if adding. */ - if (SPROP_IS_REMOVED(stored)) { - firstRemoved = spp; - } else { - firstRemoved = NULL; - if (adding && !SPROP_HAD_COLLISION(stored)) - SPROP_FLAG_COLLISION(spp, sprop); - } - - for (;;) { - METER(steps); - hash1 -= hash2; - hash1 &= sizeMask; - spp = scope->table + hash1; - - stored = *spp; - if (SPROP_IS_FREE(stored)) { - METER(stepMisses); - return (adding && firstRemoved) ? firstRemoved : spp; - } - - sprop = SPROP_CLEAR_COLLISION(stored); - if (sprop && sprop->id == id) { - METER(stepHits); - return spp; - } - - if (SPROP_IS_REMOVED(stored)) { - if (!firstRemoved) - firstRemoved = spp; - } else { - if (adding && !SPROP_HAD_COLLISION(stored)) - SPROP_FLAG_COLLISION(spp, sprop); - } - } - - /* NOTREACHED */ - return NULL; -} - -static JSBool -ChangeScope(JSContext *cx, JSScope *scope, int change) -{ - int oldlog2, newlog2; - uint32 oldsize, newsize, nbytes; - JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop; - - /* Grow, shrink, or compress by changing scope->table. */ - oldlog2 = JS_DHASH_BITS - scope->hashShift; - newlog2 = oldlog2 + change; - oldsize = JS_BIT(oldlog2); - newsize = JS_BIT(newlog2); - nbytes = SCOPE_TABLE_NBYTES(newsize); - table = (JSScopeProperty **) calloc(nbytes, 1); - if (!table) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - /* Now that we have a new table allocated, update scope members. */ - scope->hashShift = JS_DHASH_BITS - newlog2; - scope->removedCount = 0; - oldtable = scope->table; - scope->table = table; - - /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */ - cx->runtime->gcMallocBytes += nbytes; - - /* Copy only live entries, leaving removed and free ones behind. */ - for (oldspp = oldtable; oldsize != 0; oldspp++) { - sprop = SPROP_FETCH(oldspp); - if (sprop) { - spp = js_SearchScope(scope, sprop->id, JS_TRUE); - JS_ASSERT(SPROP_IS_FREE(*spp)); - *spp = sprop; - } - oldsize--; - } - - /* Finally, free the old table storage. */ - JS_free(cx, oldtable); - return JS_TRUE; -} - -/* - * Take care to exclude the mark and duplicate bits, in case we're called from - * the GC, or we are searching for a property that has not yet been flagged as - * a duplicate when making a duplicate formal parameter. - */ -#define SPROP_FLAGS_NOT_MATCHED (SPROP_MARK | SPROP_IS_DUPLICATE) - -JS_STATIC_DLL_CALLBACK(JSDHashNumber) -js_HashScopeProperty(JSDHashTable *table, const void *key) -{ - const JSScopeProperty *sprop = (const JSScopeProperty *)key; - JSDHashNumber hash; - JSPropertyOp gsop; - - /* Accumulate from least to most random so the low bits are most random. */ - hash = 0; - gsop = sprop->getter; - if (gsop) - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; - gsop = sprop->setter; - if (gsop) - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ (jsword)gsop; - - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) - ^ (sprop->flags & ~SPROP_FLAGS_NOT_MATCHED); - - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->attrs; - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->shortid; - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->slot; - hash = (hash >> (JS_DHASH_BITS - 4)) ^ (hash << 4) ^ sprop->id; - return hash; -} - -#define SPROP_MATCH(sprop, child) \ - SPROP_MATCH_PARAMS(sprop, (child)->id, (child)->getter, (child)->setter, \ - (child)->slot, (child)->attrs, (child)->flags, \ - (child)->shortid) - -#define SPROP_MATCH_PARAMS(sprop, aid, agetter, asetter, aslot, aattrs, \ - aflags, ashortid) \ - ((sprop)->id == (aid) && \ - SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ - aflags, ashortid)) - -#define SPROP_MATCH_PARAMS_AFTER_ID(sprop, agetter, asetter, aslot, aattrs, \ - aflags, ashortid) \ - ((sprop)->getter == (agetter) && \ - (sprop)->setter == (asetter) && \ - (sprop)->slot == (aslot) && \ - (sprop)->attrs == (aattrs) && \ - (((sprop)->flags ^ (aflags)) & ~SPROP_FLAGS_NOT_MATCHED) == 0 && \ - (sprop)->shortid == (ashortid)) - -JS_STATIC_DLL_CALLBACK(JSBool) -js_MatchScopeProperty(JSDHashTable *table, - const JSDHashEntryHdr *hdr, - const void *key) -{ - const JSPropertyTreeEntry *entry = (const JSPropertyTreeEntry *)hdr; - const JSScopeProperty *sprop = entry->child; - const JSScopeProperty *kprop = (const JSScopeProperty *)key; - - return SPROP_MATCH(sprop, kprop); -} - -static const JSDHashTableOps PropertyTreeHashOps = { - JS_DHashAllocTable, - JS_DHashFreeTable, - JS_DHashGetKeyStub, - js_HashScopeProperty, - js_MatchScopeProperty, - JS_DHashMoveEntryStub, - JS_DHashClearEntryStub, - JS_DHashFinalizeStub, - NULL -}; - -/* - * A property tree node on rt->propertyFreeList overlays the following prefix - * struct on JSScopeProperty. - */ -typedef struct FreeNode { - jsid id; - JSScopeProperty *next; - JSScopeProperty **prevp; -} FreeNode; - -#define FREENODE(sprop) ((FreeNode *) (sprop)) - -#define FREENODE_INSERT(list, sprop) \ - JS_BEGIN_MACRO \ - FREENODE(sprop)->next = (list); \ - FREENODE(sprop)->prevp = &(list); \ - if (list) \ - FREENODE(list)->prevp = &FREENODE(sprop)->next; \ - (list) = (sprop); \ - JS_END_MACRO - -#define FREENODE_REMOVE(sprop) \ - JS_BEGIN_MACRO \ - *FREENODE(sprop)->prevp = FREENODE(sprop)->next; \ - if (FREENODE(sprop)->next) \ - FREENODE(FREENODE(sprop)->next)->prevp = FREENODE(sprop)->prevp; \ - JS_END_MACRO - -/* NB: Called with the runtime lock held. */ -static JSScopeProperty * -NewScopeProperty(JSRuntime *rt) -{ - JSScopeProperty *sprop; - - sprop = rt->propertyFreeList; - if (sprop) { - FREENODE_REMOVE(sprop); - } else { - JS_ARENA_ALLOCATE_CAST(sprop, JSScopeProperty *, - &rt->propertyArenaPool, - sizeof(JSScopeProperty)); - if (!sprop) - return NULL; - } - - JS_RUNTIME_METER(rt, livePropTreeNodes); - JS_RUNTIME_METER(rt, totalPropTreeNodes); - return sprop; -} - -#define CHUNKY_KIDS_TAG ((jsuword)1) -#define KIDS_IS_CHUNKY(kids) ((jsuword)(kids) & CHUNKY_KIDS_TAG) -#define KIDS_TO_CHUNK(kids) ((PropTreeKidsChunk *) \ - ((jsuword)(kids) & ~CHUNKY_KIDS_TAG)) -#define CHUNK_TO_KIDS(chunk) ((JSScopeProperty *) \ - ((jsuword)(chunk) | CHUNKY_KIDS_TAG)) -#define MAX_KIDS_PER_CHUNK 10 - -typedef struct PropTreeKidsChunk PropTreeKidsChunk; - -struct PropTreeKidsChunk { - JSScopeProperty *kids[MAX_KIDS_PER_CHUNK]; - PropTreeKidsChunk *next; -}; - -static PropTreeKidsChunk * -NewPropTreeKidsChunk(JSRuntime *rt) -{ - PropTreeKidsChunk *chunk; - - chunk = calloc(1, sizeof *chunk); - if (!chunk) - return NULL; - JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0); - JS_RUNTIME_METER(rt, propTreeKidsChunks); - return chunk; -} - -static void -DestroyPropTreeKidsChunk(JSRuntime *rt, PropTreeKidsChunk *chunk) -{ - JS_RUNTIME_UNMETER(rt, propTreeKidsChunks); - free(chunk); -} - -/* NB: Called with the runtime lock held. */ -static JSBool -InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent, - JSScopeProperty *child, PropTreeKidsChunk *sweptChunk) -{ - JSPropertyTreeEntry *entry; - JSScopeProperty **childp, *kids, *sprop; - PropTreeKidsChunk *chunk, **chunkp; - uintN i; - - JS_ASSERT(!parent || child->parent != parent); - - if (!parent) { - entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); - if (!entry) - return JS_FALSE; - childp = &entry->child; - sprop = *childp; - if (!sprop) { - *childp = child; - } else { - /* - * A "Duplicate child" case. - * - * We can't do away with child, as at least one live scope entry - * still points at it. What's more, that scope's lastProp chains - * through an ancestor line to reach child, and js_Enumerate and - * others count on this linkage. We must leave child out of the - * hash table, and not require it to be there when we eventually - * GC it (see RemovePropertyTreeChild, below). - * - * It is necessary to leave the duplicate child out of the hash - * table to preserve entry uniqueness. It is safe to leave the - * child out of the hash table (unlike the duplicate child cases - * below), because the child's parent link will be null, which - * can't dangle. - */ - JS_ASSERT(sprop != child && SPROP_MATCH(sprop, child)); - JS_RUNTIME_METER(rt, duplicatePropTreeNodes); - } - } else { - childp = &parent->kids; - kids = *childp; - if (kids) { - if (KIDS_IS_CHUNKY(kids)) { - chunk = KIDS_TO_CHUNK(kids); - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - childp = &chunk->kids[i]; - sprop = *childp; - if (!sprop) - goto insert; - - JS_ASSERT(sprop != child); - if (SPROP_MATCH(sprop, child)) { - /* - * Duplicate child, see comment above. In this - * case, we must let the duplicate be inserted at - * this level in the tree, so we keep iterating, - * looking for an empty slot in which to insert. - */ - JS_ASSERT(sprop != child); - JS_RUNTIME_METER(rt, duplicatePropTreeNodes); - } - } - chunkp = &chunk->next; - } while ((chunk = *chunkp) != NULL); - - if (sweptChunk) { - chunk = sweptChunk; - } else { - chunk = NewPropTreeKidsChunk(rt); - if (!chunk) - return JS_FALSE; - } - *chunkp = chunk; - childp = &chunk->kids[0]; - } else { - sprop = kids; - JS_ASSERT(sprop != child); - if (SPROP_MATCH(sprop, child)) { - /* - * Duplicate child, see comment above. Once again, we - * must let duplicates created by deletion pile up in a - * kids-chunk-list, in order to find them when sweeping - * and thereby avoid dangling parent pointers. - */ - JS_RUNTIME_METER(rt, duplicatePropTreeNodes); - } - if (sweptChunk) { - chunk = sweptChunk; - } else { - chunk = NewPropTreeKidsChunk(rt); - if (!chunk) - return JS_FALSE; - } - parent->kids = CHUNK_TO_KIDS(chunk); - chunk->kids[0] = sprop; - childp = &chunk->kids[1]; - } - } - insert: - *childp = child; - } - - child->parent = parent; - return JS_TRUE; -} - -/* NB: Called with the runtime lock held. */ -static PropTreeKidsChunk * -RemovePropertyTreeChild(JSRuntime *rt, JSScopeProperty *child) -{ - JSPropertyTreeEntry *entry; - JSScopeProperty *parent, *kids, *kid; - PropTreeKidsChunk *list, *chunk, **chunkp, *lastChunk; - uintN i, j; - - parent = child->parent; - if (!parent) { - /* - * Don't remove child if it is not in rt->propertyTreeHash, but only - * matches a root child in the table that has compatible members. See - * the "Duplicate child" comments in InsertPropertyTreeChild, above. - */ - entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_LOOKUP); - - if (entry->child == child) - JS_DHashTableRawRemove(&rt->propertyTreeHash, &entry->hdr); - } else { - kids = parent->kids; - if (KIDS_IS_CHUNKY(kids)) { - list = chunk = KIDS_TO_CHUNK(kids); - chunkp = &list; - - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - if (chunk->kids[i] == child) { - lastChunk = chunk; - if (!lastChunk->next) { - j = i + 1; - } else { - j = 0; - do { - chunkp = &lastChunk->next; - lastChunk = *chunkp; - } while (lastChunk->next); - } - for (; j < MAX_KIDS_PER_CHUNK; j++) { - if (!lastChunk->kids[j]) - break; - } - --j; - if (chunk != lastChunk || j > i) - chunk->kids[i] = lastChunk->kids[j]; - lastChunk->kids[j] = NULL; - if (j == 0) { - *chunkp = NULL; - if (!list) - parent->kids = NULL; - return lastChunk; - } - return NULL; - } - } - - chunkp = &chunk->next; - } while ((chunk = *chunkp) != NULL); - } else { - kid = kids; - if (kid == child) - parent->kids = NULL; - } - } - return NULL; -} - -/* - * Called *without* the runtime lock held, this function acquires that lock - * only when inserting a new child. Thus there may be races to find or add - * a node that result in duplicates. We expect such races to be rare! - */ -static JSScopeProperty * -GetPropertyTreeChild(JSContext *cx, JSScopeProperty *parent, - JSScopeProperty *child) -{ - JSRuntime *rt; - JSPropertyTreeEntry *entry; - JSScopeProperty *sprop; - PropTreeKidsChunk *chunk; - uintN i; - - rt = cx->runtime; - if (!parent) { - JS_LOCK_RUNTIME(rt); - - entry = (JSPropertyTreeEntry *) - JS_DHashTableOperate(&rt->propertyTreeHash, child, JS_DHASH_ADD); - if (!entry) - goto out_of_memory; - - sprop = entry->child; - if (sprop) - goto out; - } else { - /* - * Because chunks are appended at the end and never deleted except by - * the GC, we can search without taking the runtime lock. We may miss - * a matching sprop added by another thread, and make a duplicate one, - * but that is an unlikely, therefore small, cost. The property tree - * has extremely low fan-out below its root in popular embeddings with - * real-world workloads. - * - * If workload changes so as to increase fan-out significantly below - * the property tree root, we'll want to add another tag bit stored in - * parent->kids that indicates a JSDHashTable pointer. - */ - entry = NULL; - sprop = parent->kids; - if (sprop) { - if (KIDS_IS_CHUNKY(sprop)) { - chunk = KIDS_TO_CHUNK(sprop); - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - sprop = chunk->kids[i]; - if (!sprop) - goto not_found; - - if (SPROP_MATCH(sprop, child)) - return sprop; - } - } while ((chunk = chunk->next) != NULL); - } else { - if (SPROP_MATCH(sprop, child)) - return sprop; - } - } - - not_found: - JS_LOCK_RUNTIME(rt); - } - - sprop = NewScopeProperty(rt); - if (!sprop) - goto out_of_memory; - - sprop->id = child->id; - sprop->getter = child->getter; - sprop->setter = child->setter; - sprop->slot = child->slot; - sprop->attrs = child->attrs; - sprop->flags = child->flags; - sprop->shortid = child->shortid; - sprop->parent = sprop->kids = NULL; - if (!parent) { - entry->child = sprop; - } else { - if (!InsertPropertyTreeChild(rt, parent, sprop, NULL)) - goto out_of_memory; - } - -out: - JS_UNLOCK_RUNTIME(rt); - return sprop; - -out_of_memory: - JS_UNLOCK_RUNTIME(rt); - JS_ReportOutOfMemory(cx); - return NULL; -} - -#ifdef DEBUG_notbrendan -#define CHECK_ANCESTOR_LINE(scope, sparse) \ - JS_BEGIN_MACRO \ - if ((scope)->table) CheckAncestorLine(scope, sparse); \ - JS_END_MACRO - -static void -CheckAncestorLine(JSScope *scope, JSBool sparse) -{ - uint32 size; - JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop; - uint32 entryCount, ancestorCount; - - ancestorLine = SCOPE_LAST_PROP(scope); - if (ancestorLine) - JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine)); - - entryCount = 0; - size = SCOPE_CAPACITY(scope); - start = scope->table; - for (spp = start, end = start + size; spp < end; spp++) { - sprop = SPROP_FETCH(spp); - if (sprop) { - entryCount++; - for (aprop = ancestorLine; aprop; aprop = aprop->parent) { - if (aprop == sprop) - break; - } - JS_ASSERT(aprop); - } - } - JS_ASSERT(entryCount == scope->entryCount); - - ancestorCount = 0; - for (sprop = ancestorLine; sprop; sprop = sprop->parent) { - if (SCOPE_HAD_MIDDLE_DELETE(scope) && - !SCOPE_HAS_PROPERTY(scope, sprop)) { - JS_ASSERT(sparse || (sprop->flags & SPROP_IS_DUPLICATE)); - continue; - } - ancestorCount++; - } - JS_ASSERT(ancestorCount == scope->entryCount); -} -#else -#define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */ -#endif - -static void -ReportReadOnlyScope(JSContext *cx, JSScope *scope) -{ - JSString *str; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object)); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, - str - ? JS_GetStringBytes(str) - : LOCKED_OBJ_GET_CLASS(scope->object)->name); -} - -JSScopeProperty * -js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid) -{ - JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child; - uint32 size, splen, i; - int change; - JSTempValueRooter tvr; - - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - /* - * You can't add properties to a sealed scope. But note well that you can - * change property attributes in a sealed scope, even though that replaces - * a JSScopeProperty * in the scope's hash table -- but no id is added, so - * the scope remains sealed. - */ - if (SCOPE_IS_SEALED(scope)) { - ReportReadOnlyScope(cx, scope); - return NULL; - } - - /* - * Normalize stub getter and setter values for faster is-stub testing in - * the SPROP_CALL_[GS]ETTER macros. - */ - if (getter == JS_PropertyStub) - getter = NULL; - if (setter == JS_PropertyStub) - setter = NULL; - - /* - * Search for id in order to claim its entry, allocating a property tree - * node if one doesn't already exist for our parameters. - */ - spp = js_SearchScope(scope, id, JS_TRUE); - sprop = overwriting = SPROP_FETCH(spp); - if (!sprop) { - /* Check whether we need to grow, if the load factor is >= .75. */ - size = SCOPE_CAPACITY(scope); - if (scope->entryCount + scope->removedCount >= size - (size >> 2)) { - if (scope->removedCount >= size >> 2) { - METER(compresses); - change = 0; - } else { - METER(grows); - change = 1; - } - if (!ChangeScope(cx, scope, change) && - scope->entryCount + scope->removedCount == size - 1) { - METER(addFailures); - return NULL; - } - spp = js_SearchScope(scope, id, JS_TRUE); - JS_ASSERT(!SPROP_FETCH(spp)); - } - } else { - /* Property exists: js_SearchScope must have returned a valid entry. */ - JS_ASSERT(!SPROP_IS_REMOVED(*spp)); - - /* - * If all property members match, this is a redundant add and we can - * return early. If the caller wants to allocate a slot, but doesn't - * care which slot, copy sprop->slot into slot so we can match sprop, - * if all other members match. - */ - if (!(attrs & JSPROP_SHARED) && - slot == SPROP_INVALID_SLOT && - SPROP_HAS_VALID_SLOT(sprop, scope)) { - slot = sprop->slot; - } - if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs, - flags, shortid)) { - METER(redundantAdds); - return sprop; - } - - /* - * Duplicate formal parameters require us to leave the old property - * on the ancestor line, so the decompiler can find it, even though - * its entry in scope->table is overwritten to point at a new property - * descending from the old one. The SPROP_IS_DUPLICATE flag helps us - * cope with the consequent disparity between ancestor line height and - * scope->entryCount. - */ - if (flags & SPROP_IS_DUPLICATE) { - sprop->flags |= SPROP_IS_DUPLICATE; - } else { - /* - * If we are clearing sprop to force an existing property to be - * overwritten (apart from a duplicate formal parameter), we must - * unlink it from the ancestor line at scope->lastProp, lazily if - * sprop is not lastProp. And we must remove the entry at *spp, - * precisely so the lazy "middle delete" fixup code further below - * won't find sprop in scope->table, in spite of sprop being on - * the ancestor line. - * - * When we finally succeed in finding or creating a new sprop - * and storing its pointer at *spp, we'll use the |overwriting| - * local saved when we first looked up id to decide whether we're - * indeed creating a new entry, or merely overwriting an existing - * property. - */ - if (sprop == SCOPE_LAST_PROP(scope)) { - do { - SCOPE_REMOVE_LAST_PROP(scope); - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - sprop = SCOPE_LAST_PROP(scope); - } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); - } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { - /* - * If we have no hash table yet, we need one now. The middle - * delete code is simple-minded that way! - */ - if (!scope->table) { - if (!CreateScopeTable(cx, scope, JS_TRUE)) - return NULL; - spp = js_SearchScope(scope, id, JS_TRUE); - sprop = overwriting = SPROP_FETCH(spp); - } - SCOPE_SET_MIDDLE_DELETE(scope); - } - } - - /* - * If we fail later on trying to find or create a new sprop, we will - * goto fail_overwrite and restore *spp from |overwriting|. Note that - * we don't bother to keep scope->removedCount in sync, because we'll - * fix up *spp and scope->entryCount shortly, no matter how control - * flow returns from this function. - */ - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, NULL); - scope->entryCount--; - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - sprop = NULL; - } - - if (!sprop) { - /* - * If properties were deleted from the middle of the list starting at - * scope->lastProp, we may need to fork the property tree and squeeze - * all deleted properties out of scope's ancestor line. Otherwise we - * risk adding a node with the same id as a "middle" node, violating - * the rule that properties along an ancestor line have distinct ids - * (unless flagged SPROP_IS_DUPLICATE). - */ - if (SCOPE_HAD_MIDDLE_DELETE(scope)) { - JS_ASSERT(scope->table); - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - splen = scope->entryCount; - if (splen == 0) { - JS_ASSERT(scope->lastProp == NULL); - } else { - /* - * Enumerate live entries in scope->table using a temporary - * vector, by walking the (possibly sparse, due to deletions) - * ancestor line from scope->lastProp. - */ - spvec = (JSScopeProperty **) - JS_malloc(cx, SCOPE_TABLE_NBYTES(splen)); - if (!spvec) - goto fail_overwrite; - i = splen; - sprop = SCOPE_LAST_PROP(scope); - JS_ASSERT(sprop); - do { - /* - * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY -- - * the latter insists that sprop->id maps to sprop, while - * the former simply tests whether sprop->id is bound in - * scope. We must allow for duplicate formal parameters - * along the ancestor line, and fork them as needed. - */ - if (!SCOPE_GET_PROPERTY(scope, sprop->id)) - continue; - - JS_ASSERT(sprop != overwriting); - if (i == 0) { - /* - * If our original splen estimate, scope->entryCount, - * is less than the ancestor line height, there must - * be duplicate formal parameters in this (function - * object) scope. Count remaining ancestors in order - * to realloc spvec. - */ - JSScopeProperty *tmp = sprop; - do { - if (SCOPE_GET_PROPERTY(scope, tmp->id)) - i++; - } while ((tmp = tmp->parent) != NULL); - spp2 = (JSScopeProperty **) - JS_realloc(cx, spvec, SCOPE_TABLE_NBYTES(splen+i)); - if (!spp2) { - JS_free(cx, spvec); - goto fail_overwrite; - } - - spvec = spp2; - memmove(spvec + i, spvec, SCOPE_TABLE_NBYTES(splen)); - splen += i; - } - - spvec[--i] = sprop; - } while ((sprop = sprop->parent) != NULL); - JS_ASSERT(i == 0); - - /* - * Now loop forward through spvec, forking the property tree - * whenever we see a "parent gap" due to deletions from scope. - * NB: sprop is null on first entry to the loop body. - */ - do { - if (spvec[i]->parent == sprop) { - sprop = spvec[i]; - } else { - sprop = GetPropertyTreeChild(cx, sprop, spvec[i]); - if (!sprop) { - JS_free(cx, spvec); - goto fail_overwrite; - } - - spp2 = js_SearchScope(scope, sprop->id, JS_FALSE); - JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]); - SPROP_STORE_PRESERVING_COLLISION(spp2, sprop); - } - } while (++i < splen); - JS_free(cx, spvec); - - /* - * Now sprop points to the last property in scope, where the - * ancestor line from sprop to the root is dense w.r.t. scope: - * it contains no nodes not mapped by scope->table, apart from - * any stinking ECMA-mandated duplicate formal parameters. - */ - scope->lastProp = sprop; - CHECK_ANCESTOR_LINE(scope, JS_FALSE); - JS_RUNTIME_METER(cx->runtime, middleDeleteFixups); - } - - SCOPE_CLR_MIDDLE_DELETE(scope); - } - - /* - * Aliases share another property's slot, passed in the |slot| param. - * Shared properties have no slot. Unshared properties that do not - * alias another property's slot get one here, but may lose it due to - * a JS_ClearScope call. - */ - if (!(flags & SPROP_IS_ALIAS)) { - if (attrs & JSPROP_SHARED) { - slot = SPROP_INVALID_SLOT; - } else { - /* - * We may have set slot from a nearly-matching sprop, above. - * If so, we're overwriting that nearly-matching sprop, so we - * can reuse its slot -- we don't need to allocate a new one. - * Callers should therefore pass SPROP_INVALID_SLOT for all - * non-alias, unshared property adds. - */ - if (slot != SPROP_INVALID_SLOT) - JS_ASSERT(overwriting); - else if (!js_AllocSlot(cx, scope->object, &slot)) - goto fail_overwrite; - } - } - - /* - * Check for a watchpoint on a deleted property; if one exists, change - * setter to js_watch_set. - * XXXbe this could get expensive with lots of watchpoints... - */ - if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) && - js_FindWatchPoint(cx->runtime, scope, id)) { - JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr); - setter = js_WrapWatchedSetter(cx, id, attrs, setter); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!setter) - goto fail_overwrite; - } - - /* Find or create a property tree node labeled by our arguments. */ - child.id = id; - child.getter = getter; - child.setter = setter; - child.slot = slot; - child.attrs = attrs; - child.flags = flags; - child.shortid = shortid; - sprop = GetPropertyTreeChild(cx, scope->lastProp, &child); - if (!sprop) - goto fail_overwrite; - - /* Store the tree node pointer in the table entry for id. */ - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, sprop); - scope->entryCount++; - scope->lastProp = sprop; - CHECK_ANCESTOR_LINE(scope, JS_FALSE); - if (!overwriting) { - JS_RUNTIME_METER(cx->runtime, liveScopeProps); - JS_RUNTIME_METER(cx->runtime, totalScopeProps); - } - - /* - * If we reach the hashing threshold, try to allocate scope->table. - * If we can't (a rare event, preceded by swapping to death on most - * modern OSes), stick with linear search rather than whining about - * this little set-back. Therefore we must test !scope->table and - * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the - * entry count just reached the threshold. - */ - if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD) - (void) CreateScopeTable(cx, scope, JS_FALSE); - } - - METER(adds); - return sprop; - -fail_overwrite: - if (overwriting) { - /* - * We may or may not have forked overwriting out of scope's ancestor - * line, so we must check (the alternative is to set a flag above, but - * that hurts the common, non-error case). If we did fork overwriting - * out, we'll add it back at scope->lastProp. This means enumeration - * order can change due to a failure to overwrite an id. - * XXXbe very minor incompatibility - */ - for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) { - if (!sprop) { - sprop = SCOPE_LAST_PROP(scope); - if (overwriting->parent == sprop) { - scope->lastProp = overwriting; - } else { - sprop = GetPropertyTreeChild(cx, sprop, overwriting); - if (sprop) { - JS_ASSERT(sprop != overwriting); - scope->lastProp = sprop; - } - overwriting = sprop; - } - break; - } - if (sprop == overwriting) - break; - } - if (overwriting) { - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, overwriting); - scope->entryCount++; - } - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - } - METER(addFailures); - return NULL; -} - -JSScopeProperty * -js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter) -{ - JSScopeProperty child, *newsprop, **spp; - - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - /* Allow only shared (slot-less) => unshared (slot-full) transition. */ - attrs |= sprop->attrs & mask; - JS_ASSERT(!((attrs ^ sprop->attrs) & JSPROP_SHARED) || - !(attrs & JSPROP_SHARED)); - if (getter == JS_PropertyStub) - getter = NULL; - if (setter == JS_PropertyStub) - setter = NULL; - if (sprop->attrs == attrs && - sprop->getter == getter && - sprop->setter == setter) { - return sprop; - } - - child.id = sprop->id; - child.getter = getter; - child.setter = setter; - child.slot = sprop->slot; - child.attrs = attrs; - child.flags = sprop->flags; - child.shortid = sprop->shortid; - - if (SCOPE_LAST_PROP(scope) == sprop) { - /* - * Optimize the case where the last property added to scope is changed - * to have a different attrs, getter, or setter. In the last property - * case, we need not fork the property tree. But since we do not call - * js_AddScopeProperty, we may need to allocate a new slot directly. - */ - if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) { - JS_ASSERT(child.slot == SPROP_INVALID_SLOT); - if (!js_AllocSlot(cx, scope->object, &child.slot)) - return NULL; - } - - newsprop = GetPropertyTreeChild(cx, sprop->parent, &child); - if (newsprop) { - spp = js_SearchScope(scope, sprop->id, JS_FALSE); - JS_ASSERT(SPROP_FETCH(spp) == sprop); - - if (scope->table) - SPROP_STORE_PRESERVING_COLLISION(spp, newsprop); - scope->lastProp = newsprop; - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - } - } else { - /* - * Let js_AddScopeProperty handle this |overwriting| case, including - * the conservation of sprop->slot (if it's valid). We must not call - * js_RemoveScopeProperty here, it will free a valid sprop->slot and - * js_AddScopeProperty won't re-allocate it. - */ - newsprop = js_AddScopeProperty(cx, scope, child.id, - child.getter, child.setter, child.slot, - child.attrs, child.flags, child.shortid); - } - -#ifdef DUMP_SCOPE_STATS - if (!newsprop) - METER(changeFailures); -#endif - return newsprop; -} - -JSBool -js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id) -{ - JSScopeProperty **spp, *stored, *sprop; - uint32 size; - - JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope)); - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - if (SCOPE_IS_SEALED(scope)) { - ReportReadOnlyScope(cx, scope); - return JS_FALSE; - } - METER(removes); - - spp = js_SearchScope(scope, id, JS_FALSE); - stored = *spp; - sprop = SPROP_CLEAR_COLLISION(stored); - if (!sprop) { - METER(uselessRemoves); - return JS_TRUE; - } - - /* Convert from a list to a hash so we can handle "middle deletes". */ - if (!scope->table && sprop != scope->lastProp) { - if (!CreateScopeTable(cx, scope, JS_TRUE)) - return JS_FALSE; - spp = js_SearchScope(scope, id, JS_FALSE); - stored = *spp; - sprop = SPROP_CLEAR_COLLISION(stored); - } - - /* First, if sprop is unshared and not cleared, free its slot number. */ - if (SPROP_HAS_VALID_SLOT(sprop, scope)) { - js_FreeSlot(cx, scope->object, sprop->slot); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); - } - - /* Next, remove id by setting its entry to a removed or free sentinel. */ - if (SPROP_HAD_COLLISION(stored)) { - JS_ASSERT(scope->table); - *spp = SPROP_REMOVED; - scope->removedCount++; - } else { - METER(removeFrees); - if (scope->table) - *spp = NULL; - } - scope->entryCount--; - JS_RUNTIME_UNMETER(cx->runtime, liveScopeProps); - - /* Update scope->lastProp directly, or set its deferred update flag. */ - if (sprop == SCOPE_LAST_PROP(scope)) { - do { - SCOPE_REMOVE_LAST_PROP(scope); - if (!SCOPE_HAD_MIDDLE_DELETE(scope)) - break; - sprop = SCOPE_LAST_PROP(scope); - } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop)); - } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) { - SCOPE_SET_MIDDLE_DELETE(scope); - } - CHECK_ANCESTOR_LINE(scope, JS_TRUE); - - /* Last, consider shrinking scope's table if its load factor is <= .25. */ - size = SCOPE_CAPACITY(scope); - if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) { - METER(shrinks); - (void) ChangeScope(cx, scope, -1); - } - - return JS_TRUE; -} - -void -js_ClearScope(JSContext *cx, JSScope *scope) -{ - CHECK_ANCESTOR_LINE(scope, JS_TRUE); -#ifdef DEBUG - JS_LOCK_RUNTIME_VOID(cx->runtime, - cx->runtime->liveScopeProps -= scope->entryCount); -#endif - - if (scope->table) - free(scope->table); - SCOPE_CLR_MIDDLE_DELETE(scope); - InitMinimalScope(scope); - JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals); -} - -void -js_MarkId(JSContext *cx, jsid id) -{ - if (JSID_IS_ATOM(id)) - GC_MARK_ATOM(cx, JSID_TO_ATOM(id)); - else if (JSID_IS_OBJECT(id)) - GC_MARK(cx, JSID_TO_OBJECT(id), "id"); - else - JS_ASSERT(JSID_IS_INT(id)); -} - -#if defined GC_MARK_DEBUG || defined DUMP_SCOPE_STATS -# include "jsprf.h" -#endif - -void -js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop) -{ - sprop->flags |= SPROP_MARK; - MARK_ID(cx, sprop->id); - -#if JS_HAS_GETTER_SETTER - if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) { -#ifdef GC_MARK_DEBUG - char buf[64]; - char buf2[11]; - const char *id; - - if (JSID_IS_ATOM(sprop->id)) { - JSAtom *atom = JSID_TO_ATOM(sprop->id); - - id = (atom && ATOM_IS_STRING(atom)) - ? JS_GetStringBytes(ATOM_TO_STRING(atom)) - : "unknown"; - } else if (JSID_IS_INT(sprop->id)) { - JS_snprintf(buf2, sizeof buf2, "%d", JSID_TO_INT(sprop->id)); - id = buf2; - } else { - id = ""; - } -#endif - - if (sprop->attrs & JSPROP_GETTER) { -#ifdef GC_MARK_DEBUG - JS_snprintf(buf, sizeof buf, "%s %s", - id, js_getter_str); -#endif - GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->getter), buf); - } - if (sprop->attrs & JSPROP_SETTER) { -#ifdef GC_MARK_DEBUG - JS_snprintf(buf, sizeof buf, "%s %s", - id, js_setter_str); -#endif - GC_MARK(cx, JSVAL_TO_GCTHING((jsval) sprop->setter), buf); - } - } -#endif /* JS_HAS_GETTER_SETTER */ -} - -#ifdef DUMP_SCOPE_STATS - -#include -#include - -uint32 js_nkids_max; -uint32 js_nkids_sum; -double js_nkids_sqsum; -uint32 js_nkids_hist[11]; - -static void -MeterKidCount(uintN nkids) -{ - if (nkids) { - js_nkids_sum += nkids; - js_nkids_sqsum += (double)nkids * nkids; - if (nkids > js_nkids_max) - js_nkids_max = nkids; - } - js_nkids_hist[JS_MIN(nkids, 10)]++; -} - -static void -MeterPropertyTree(JSScopeProperty *node) -{ - uintN i, nkids; - JSScopeProperty *kids, *kid; - PropTreeKidsChunk *chunk; - - nkids = 0; - kids = node->kids; - if (kids) { - if (KIDS_IS_CHUNKY(kids)) { - for (chunk = KIDS_TO_CHUNK(kids); chunk; chunk = chunk->next) { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - kid = chunk->kids[i]; - if (!kid) - break; - MeterPropertyTree(kid); - nkids++; - } - } - } else { - MeterPropertyTree(kids); - nkids = 1; - } - } - - MeterKidCount(nkids); -} - -JS_STATIC_DLL_CALLBACK(JSDHashOperator) -js_MeterPropertyTree(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, - void *arg) -{ - JSPropertyTreeEntry *entry = (JSPropertyTreeEntry *)hdr; - - MeterPropertyTree(entry->child); - return JS_DHASH_NEXT; -} - -static void -DumpSubtree(JSScopeProperty *sprop, int level, FILE *fp) -{ - char buf[10]; - JSScopeProperty *kids, *kid; - PropTreeKidsChunk *chunk; - uintN i; - - fprintf(fp, "%*sid %s g/s %p/%p slot %lu attrs %x flags %x shortid %d\n", - level, "", - JSID_IS_ATOM(sprop->id) - ? JS_GetStringBytes(ATOM_TO_STRING(JSID_TO_ATOM(sprop->id))) - : JSID_IS_OBJECT(sprop->id) - ? js_ValueToPrintableString(cx, OBJECT_JSID_TO_JSVAL(sprop->id)) - : (JS_snprintf(buf, sizeof buf, "%ld", JSVAL_TO_INT(sprop->id)), - buf) - (void *) sprop->getter, (void *) sprop->setter, - (unsigned long) sprop->slot, sprop->attrs, sprop->flags, - sprop->shortid); - kids = sprop->kids; - if (kids) { - ++level; - if (KIDS_IS_CHUNKY(kids)) { - chunk = KIDS_TO_CHUNK(kids); - do { - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - kid = chunk->kids[i]; - if (!kid) - break; - JS_ASSERT(kid->parent == sprop); - DumpSubtree(kid, level, fp); - } - } while ((chunk = chunk->next) != NULL); - } else { - kid = kids; - DumpSubtree(kid, level, fp); - } - } -} - -#endif /* DUMP_SCOPE_STATS */ - -void -js_SweepScopeProperties(JSRuntime *rt) -{ - JSArena **ap, *a; - JSScopeProperty *limit, *sprop, *parent, *kids, *kid; - uintN liveCount; - PropTreeKidsChunk *chunk, *nextChunk, *freeChunk; - uintN i; - -#ifdef DUMP_SCOPE_STATS - uint32 livePropCapacity = 0, totalLiveCount = 0; - static FILE *logfp; - if (!logfp) - logfp = fopen("/tmp/proptree.stats", "a"); - - MeterKidCount(rt->propertyTreeHash.entryCount); - JS_DHashTableEnumerate(&rt->propertyTreeHash, js_MeterPropertyTree, NULL); - - { - double mean = 0.0, var = 0.0, sigma = 0.0; - double nodesum = rt->livePropTreeNodes; - double kidsum = js_nkids_sum; - if (nodesum > 0 && kidsum >= 0) { - mean = kidsum / nodesum; - var = nodesum * js_nkids_sqsum - kidsum * kidsum; - if (var < 0.0 || nodesum <= 1) - var = 0.0; - else - var /= nodesum * (nodesum - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.0) ? sqrt(var) : 0.0; - } - - fprintf(logfp, - "props %u nodes %g beta %g meankids %g sigma %g max %u", - rt->liveScopeProps, nodesum, nodesum / rt->liveScopeProps, - mean, sigma, js_nkids_max); - } - - fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u", - js_nkids_hist[0], js_nkids_hist[1], - js_nkids_hist[2], js_nkids_hist[3], - js_nkids_hist[4], js_nkids_hist[5], - js_nkids_hist[6], js_nkids_hist[7], - js_nkids_hist[8], js_nkids_hist[9], - js_nkids_hist[10]); - js_nkids_sum = js_nkids_max = 0; - js_nkids_sqsum = 0; - memset(js_nkids_hist, 0, sizeof js_nkids_hist); -#endif - - ap = &rt->propertyArenaPool.first.next; - while ((a = *ap) != NULL) { - limit = (JSScopeProperty *) a->avail; - liveCount = 0; - for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) { - /* If the id is null, sprop is already on the freelist. */ - if (sprop->id == JSVAL_NULL) - continue; - - /* If the mark bit is set, sprop is alive, so we skip it. */ - if (sprop->flags & SPROP_MARK) { - sprop->flags &= ~SPROP_MARK; - liveCount++; - continue; - } - - /* Ok, sprop is garbage to collect: unlink it from its parent. */ - freeChunk = RemovePropertyTreeChild(rt, sprop); - - /* - * Take care to reparent all sprop's kids to their grandparent. - * InsertPropertyTreeChild can potentially fail for two reasons: - * - * 1. If parent is null, insertion into the root property hash - * table may fail. We are forced to leave the kid out of the - * table (as can already happen with duplicates) but ensure - * that the kid's parent pointer is set to null. - * - * 2. If parent is non-null, allocation of a new KidsChunk can - * fail. To prevent this from happening, we allow sprops's own - * chunks to be reused by the grandparent, which removes the - * need for InsertPropertyTreeChild to malloc a new KidsChunk. - * - * If sprop does not have chunky kids, then we rely on the - * RemovePropertyTreeChild call above (which removed sprop from - * its parent) either leaving one free entry, or else returning - * the now-unused chunk to us so we can reuse it. - * - * We also require the grandparent to have either no kids or else - * chunky kids. A single non-chunky kid would force a new chunk to - * be malloced in some cases (if sprop had a single non-chunky - * kid, or a multiple of MAX_KIDS_PER_CHUNK kids). Note that - * RemovePropertyTreeChild never converts a single-entry chunky - * kid back to a non-chunky kid, so we are assured of correct - * behaviour. - */ - kids = sprop->kids; - if (kids) { - sprop->kids = NULL; - parent = sprop->parent; - /* Validate that grandparent has no kids or chunky kids. */ - JS_ASSERT(!parent || !parent->kids || - KIDS_IS_CHUNKY(parent->kids)); - if (KIDS_IS_CHUNKY(kids)) { - chunk = KIDS_TO_CHUNK(kids); - do { - nextChunk = chunk->next; - chunk->next = NULL; - for (i = 0; i < MAX_KIDS_PER_CHUNK; i++) { - kid = chunk->kids[i]; - if (!kid) - break; - JS_ASSERT(kid->parent == sprop); - - /* - * Clear a space in the kids array for possible - * re-use by InsertPropertyTreeChild. - */ - chunk->kids[i] = NULL; - if (!InsertPropertyTreeChild(rt, parent, kid, - chunk)) { - /* - * This can happen only if we failed to add an - * entry to the root property hash table. - */ - JS_ASSERT(!parent); - kid->parent = NULL; - } - } - if (!chunk->kids[0]) { - /* The chunk wasn't reused, so we must free it. */ - DestroyPropTreeKidsChunk(rt, chunk); - } - } while ((chunk = nextChunk) != NULL); - } else { - kid = kids; - if (!InsertPropertyTreeChild(rt, parent, kid, freeChunk)) { - /* - * This can happen only if we failed to add an entry - * to the root property hash table. - */ - JS_ASSERT(!parent); - kid->parent = NULL; - } - } - } - - if (freeChunk && !freeChunk->kids[0]) { - /* The chunk wasn't reused, so we must free it. */ - DestroyPropTreeKidsChunk(rt, freeChunk); - } - - /* Clear id so we know (above) that sprop is on the freelist. */ - sprop->id = JSVAL_NULL; - FREENODE_INSERT(rt->propertyFreeList, sprop); - JS_RUNTIME_UNMETER(rt, livePropTreeNodes); - } - - /* If a contains no live properties, return it to the malloc heap. */ - if (liveCount == 0) { - for (sprop = (JSScopeProperty *) a->base; sprop < limit; sprop++) - FREENODE_REMOVE(sprop); - JS_ARENA_DESTROY(&rt->propertyArenaPool, a, ap); - } else { -#ifdef DUMP_SCOPE_STATS - livePropCapacity += limit - (JSScopeProperty *) a->base; - totalLiveCount += liveCount; -#endif - ap = &a->next; - } - } - -#ifdef DUMP_SCOPE_STATS - fprintf(logfp, " arenautil %g%%\n", - (totalLiveCount * 100.0) / livePropCapacity); - fflush(logfp); -#endif - -#ifdef DUMP_PROPERTY_TREE - { - FILE *dumpfp = fopen("/tmp/proptree.dump", "w"); - if (dumpfp) { - JSPropertyTreeEntry *pte, *end; - - pte = (JSPropertyTreeEntry *) rt->propertyTreeHash.entryStore; - end = pte + JS_DHASH_TABLE_SIZE(&rt->propertyTreeHash); - while (pte < end) { - if (pte->child) - DumpSubtree(pte->child, 0, dumpfp); - pte++; - } - fclose(dumpfp); - } - } -#endif -} - -JSBool -js_InitPropertyTree(JSRuntime *rt) -{ - if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL, - sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) { - rt->propertyTreeHash.ops = NULL; - return JS_FALSE; - } - JS_InitArenaPool(&rt->propertyArenaPool, "properties", - 256 * sizeof(JSScopeProperty), sizeof(void *)); - return JS_TRUE; -} - -void -js_FinishPropertyTree(JSRuntime *rt) -{ - if (rt->propertyTreeHash.ops) { - JS_DHashTableFinish(&rt->propertyTreeHash); - rt->propertyTreeHash.ops = NULL; - } - JS_FinishArenaPool(&rt->propertyArenaPool); -} diff --git a/spidermonkey/libjs/jsscope.h b/spidermonkey/libjs/jsscope.h deleted file mode 100644 index 0565d4d..0000000 --- a/spidermonkey/libjs/jsscope.h +++ /dev/null @@ -1,407 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsscope_h___ -#define jsscope_h___ -/* - * JS symbol tables. - */ -#include "jstypes.h" -#include "jsobj.h" -#include "jsprvtd.h" -#include "jspubtd.h" - -#ifdef JS_THREADSAFE -# include "jslock.h" -#endif - -/* - * Given P independent, non-unique properties each of size S words mapped by - * all scopes in a runtime, construct a property tree of N nodes each of size - * S+L words (L for tree linkage). A nominal L value is 2 for leftmost-child - * and right-sibling links. We hope that the N < P by enough that the space - * overhead of L, and the overhead of scope entries pointing at property tree - * nodes, is worth it. - * - * The tree construction goes as follows. If any empty scope in the runtime - * has a property X added to it, find or create a node under the tree root - * labeled X, and set scope->lastProp to point at that node. If any non-empty - * scope whose most recently added property is labeled Y has another property - * labeled Z added, find or create a node for Z under the node that was added - * for Y, and set scope->lastProp to point at that node. - * - * A property is labeled by its members' values: id, getter, setter, slot, - * attributes, tiny or short id, and a field telling for..in order. Note that - * labels are not unique in the tree, but they are unique among a node's kids - * (barring rare and benign multi-threaded race condition outcomes, see below) - * and along any ancestor line from the tree root to a given leaf node (except - * for the hard case of duplicate formal parameters to a function). - * - * Thus the root of the tree represents all empty scopes, and the first ply - * of the tree represents all scopes containing one property, etc. Each node - * in the tree can stand for any number of scopes having the same ordered set - * of properties, where that node was the last added to the scope. (We need - * not store the root of the tree as a node, and do not -- all we need are - * links to its kids.) - * - * Sidebar on for..in loop order: ECMA requires no particular order, but this - * implementation has promised and delivered property definition order, and - * compatibility is king. We could use an order number per property, which - * would require a sort in js_Enumerate, and an entry order generation number - * per scope. An order number beats a list, which should be doubly-linked for - * O(1) delete. An even better scheme is to use a parent link in the property - * tree, so that the ancestor line can be iterated from scope->lastProp when - * filling in a JSIdArray from back to front. This parent link also helps the - * GC to sweep properties iteratively. - * - * What if a property Y is deleted from a scope? If Y is the last property in - * the scope, we simply adjust the scope's lastProp member after we remove the - * scope's hash-table entry pointing at that property node. The parent link - * mentioned in the for..in sidebar above makes this adjustment O(1). But if - * Y comes between X and Z in the scope, then we might have to "fork" the tree - * at X, leaving X->Y->Z in case other scopes have those properties added in - * that order; and to finish the fork, we'd add a node labeled Z with the path - * X->Z, if it doesn't exist. This could lead to lots of extra nodes, and to - * O(n^2) growth when deleting lots of properties. - * - * Rather, for O(1) growth all around, we should share the path X->Y->Z among - * scopes having those three properties added in that order, and among scopes - * having only X->Z where Y was deleted. All such scopes have a lastProp that - * points to the Z child of Y. But a scope in which Y was deleted does not - * have a table entry for Y, and when iterating that scope by traversing the - * ancestor line from Z, we will have to test for a table entry for each node, - * skipping nodes that lack entries. - * - * What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice. - * Therefore we must fork in such a case, if not earlier. Because delete is - * "bursty", we should not fork eagerly. Delaying a fork till we are at risk - * of adding Y after it was deleted already requires a flag in the JSScope, to - * wit, SCOPE_MIDDLE_DELETE. - * - * What about thread safety? If the property tree operations done by requests - * are find-node and insert-node, then the only hazard is duplicate insertion. - * This is harmless except for minor bloat. When all requests have ended or - * been suspended, the GC is free to sweep the tree after marking all nodes - * reachable from scopes, performing remove-node operations as needed. Note - * also that the stable storage of the property nodes during active requests - * permits the property cache (see jsinterp.h) to dereference JSScopeProperty - * weak references safely. - * - * Is the property tree worth it compared to property storage in each table's - * entries? To decide, we must find the relation <> between the words used - * with a property tree and the words required without a tree. - * - * Model all scopes as one super-scope of capacity T entries (T a power of 2). - * Let alpha be the load factor of this double hash-table. With the property - * tree, each entry in the table is a word-sized pointer to a node that can be - * shared by many scopes. But all such pointers are overhead compared to the - * situation without the property tree, where the table stores property nodes - * directly, as entries each of size S words. With the property tree, we need - * L=2 extra words per node for siblings and kids pointers. Without the tree, - * (1-alpha)*S*T words are wasted on free or removed sentinel-entries required - * by double hashing. - * - * Therefore, - * - * (property tree) <> (no property tree) - * N*(S+L) + T <> S*T - * N*(S+L) + T <> P*S + (1-alpha)*S*T - * N*(S+L) + alpha*T + (1-alpha)*T <> P*S + (1-alpha)*S*T - * - * Note that P is alpha*T by definition, so - * - * N*(S+L) + P + (1-alpha)*T <> P*S + (1-alpha)*S*T - * N*(S+L) <> P*S - P + (1-alpha)*S*T - (1-alpha)*T - * N*(S+L) <> (P + (1-alpha)*T) * (S-1) - * N*(S+L) <> (P + (1-alpha)*P/alpha) * (S-1) - * N*(S+L) <> P * (1/alpha) * (S-1) - * - * Let N = P*beta for a compression ratio beta, beta <= 1: - * - * P*beta*(S+L) <> P * (1/alpha) * (S-1) - * beta*(S+L) <> (S-1)/alpha - * beta <> (S-1)/((S+L)*alpha) - * - * For S = 6 (32-bit architectures) and L = 2, the property tree wins iff - * - * beta < 5/(8*alpha) - * - * We ensure that alpha <= .75, so the property tree wins if beta < .83_. An - * average beta from recent Mozilla browser startups was around .6. - * - * Can we reduce L? Observe that the property tree degenerates into a list of - * lists if at most one property Y follows X in all scopes. In or near such a - * case, we waste a word on the right-sibling link outside of the root ply of - * the tree. Note also that the root ply tends to be large, so O(n^2) growth - * searching it is likely, indicating the need for hashing (but with increased - * thread safety costs). - * - * If only K out of N nodes in the property tree have more than one child, we - * could eliminate the sibling link and overlay a children list or hash-table - * pointer on the leftmost-child link (which would then be either null or an - * only-child link; the overlay could be tagged in the low bit of the pointer, - * or flagged elsewhere in the property tree node, although such a flag must - * not be considered when comparing node labels during tree search). - * - * For such a system, L = 1 + (K * averageChildrenTableSize) / N instead of 2. - * If K << N, L approaches 1 and the property tree wins if beta < .95. - * - * We observe that fan-out below the root ply of the property tree appears to - * have extremely low degree (see the MeterPropertyTree code that histograms - * child-counts in jsscope.c), so instead of a hash-table we use a linked list - * of child node pointer arrays ("kid chunks"). The details are isolated in - * jsscope.c; others must treat JSScopeProperty.kids as opaque. We leave it - * strongly typed for debug-ability of the common (null or one-kid) cases. - * - * One final twist (can you stand it?): the mean number of entries per scope - * in Mozilla is < 5, with a large standard deviation (~8). Instead of always - * allocating scope->table, we leave it null while initializing all the other - * scope members as if it were non-null and minimal-length. Until a property - * is added that crosses the threshold of 6 or more entries for hashing, or - * until a "middle delete" occurs, we use linear search from scope->lastProp - * to find a given id, and save on the space overhead of a hash table. - */ - -struct JSScope { - JSObjectMap map; /* base class state */ - JSObject *object; /* object that owns this scope */ - uint8 flags; /* flags, see below */ - int8 hashShift; /* multiplicative hash shift */ - uint16 spare; /* reserved */ - uint32 entryCount; /* number of entries in table */ - uint32 removedCount; /* removed entry sentinels in table */ - JSScopeProperty **table; /* table of ptrs to shared tree nodes */ - JSScopeProperty *lastProp; /* pointer to last property added */ -#ifdef JS_THREADSAFE - JSContext *ownercx; /* creating context, NULL if shared */ - JSThinLock lock; /* binary semaphore protecting scope */ - union { /* union lockful and lock-free state: */ - jsrefcount count; /* lock entry count for reentrancy */ - JSScope *link; /* next link in rt->scopeSharingTodo */ - } u; -#ifdef DEBUG - const char *file[4]; /* file where lock was (re-)taken */ - unsigned int line[4]; /* line where lock was (re-)taken */ -#endif -#endif -}; - -#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) - -/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ -#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift) - -/* Scope flags and some macros to hide them from other files than jsscope.c. */ -#define SCOPE_MIDDLE_DELETE 0x0001 -#define SCOPE_SEALED 0x0002 - -#define SCOPE_HAD_MIDDLE_DELETE(scope) ((scope)->flags & SCOPE_MIDDLE_DELETE) -#define SCOPE_SET_MIDDLE_DELETE(scope) ((scope)->flags |= SCOPE_MIDDLE_DELETE) -#define SCOPE_CLR_MIDDLE_DELETE(scope) ((scope)->flags &= ~SCOPE_MIDDLE_DELETE) - -#define SCOPE_IS_SEALED(scope) ((scope)->flags & SCOPE_SEALED) -#define SCOPE_SET_SEALED(scope) ((scope)->flags |= SCOPE_SEALED) -#if 0 -/* - * Don't define this, it can't be done safely because JS_LOCK_OBJ will avoid - * taking the lock if the object owns its scope and the scope is sealed. - */ -#define SCOPE_CLR_SEALED(scope) ((scope)->flags &= ~SCOPE_SEALED) -#endif - -/* - * A little information hiding for scope->lastProp, in case it ever becomes - * a tagged pointer again. - */ -#define SCOPE_LAST_PROP(scope) ((scope)->lastProp) -#define SCOPE_REMOVE_LAST_PROP(scope) ((scope)->lastProp = \ - (scope)->lastProp->parent) - -struct JSScopeProperty { - jsid id; /* int-tagged jsval/untagged JSAtom* */ - JSPropertyOp getter; /* getter and setter hooks or objects */ - JSPropertyOp setter; - uint32 slot; /* index in obj->slots vector */ - uint8 attrs; /* attributes, see jsapi.h JSPROP_* */ - uint8 flags; /* flags, see below for defines */ - int16 shortid; /* tinyid, or local arg/var index */ - JSScopeProperty *parent; /* parent node, reverse for..in order */ - JSScopeProperty *kids; /* null, single child, or a tagged ptr - to many-kids data structure */ -}; - -/* JSScopeProperty pointer tag bit indicating a collision. */ -#define SPROP_COLLISION ((jsuword)1) -#define SPROP_REMOVED ((JSScopeProperty *) SPROP_COLLISION) - -/* Macros to get and set sprop pointer values and collision flags. */ -#define SPROP_IS_FREE(sprop) ((sprop) == NULL) -#define SPROP_IS_REMOVED(sprop) ((sprop) == SPROP_REMOVED) -#define SPROP_IS_LIVE(sprop) ((sprop) > SPROP_REMOVED) -#define SPROP_FLAG_COLLISION(spp,sprop) (*(spp) = (JSScopeProperty *) \ - ((jsuword)(sprop) | SPROP_COLLISION)) -#define SPROP_HAD_COLLISION(sprop) ((jsuword)(sprop) & SPROP_COLLISION) -#define SPROP_FETCH(spp) SPROP_CLEAR_COLLISION(*(spp)) - -#define SPROP_CLEAR_COLLISION(sprop) \ - ((JSScopeProperty *) ((jsuword)(sprop) & ~SPROP_COLLISION)) - -#define SPROP_STORE_PRESERVING_COLLISION(spp, sprop) \ - (*(spp) = (JSScopeProperty *) ((jsuword)(sprop) \ - | SPROP_HAD_COLLISION(*(spp)))) - -/* Bits stored in sprop->flags. */ -#define SPROP_MARK 0x01 -#define SPROP_IS_DUPLICATE 0x02 -#define SPROP_IS_ALIAS 0x04 -#define SPROP_HAS_SHORTID 0x08 -#define SPROP_IS_HIDDEN 0x10 /* a normally-hidden property, - e.g., function arg or var */ - -/* - * If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather - * than id when calling sprop's getter or setter. - */ -#define SPROP_USERID(sprop) \ - (((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \ - : ID_TO_VALUE((sprop)->id)) - -#define SPROP_INVALID_SLOT 0xffffffff - -#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) -#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) - -#define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) -#define SPROP_HAS_STUB_SETTER(sprop) (!(sprop)->setter) - -/* - * NB: SPROP_GET must not be called if SPROP_HAS_STUB_GETTER(sprop). - */ -#define SPROP_GET(cx,sprop,obj,obj2,vp) \ - (((sprop)->attrs & JSPROP_GETTER) \ - ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ - OBJECT_TO_JSVAL((sprop)->getter), JSACC_READ, \ - 0, 0, vp) \ - : (sprop)->getter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) - -/* - * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) && - * !(sprop->attrs & JSPROP_GETTER)). - */ -#define SPROP_SET(cx,sprop,obj,obj2,vp) \ - (((sprop)->attrs & JSPROP_SETTER) \ - ? js_InternalGetOrSet(cx, obj, (sprop)->id, \ - OBJECT_TO_JSVAL((sprop)->setter), JSACC_WRITE, \ - 1, vp, vp) \ - : ((sprop)->attrs & JSPROP_GETTER) \ - ? (JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, \ - JSMSG_GETTER_ONLY, NULL), JS_FALSE) \ - : (sprop)->setter(cx, OBJ_THIS_OBJECT(cx,obj), SPROP_USERID(sprop), vp)) - -/* Macro for common expression to test for shared permanent attributes. */ -#define SPROP_IS_SHARED_PERMANENT(sprop) \ - ((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0) - -extern JSScope * -js_GetMutableScope(JSContext *cx, JSObject *obj); - -extern JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj); - -extern void -js_DestroyScope(JSContext *cx, JSScope *scope); - -#define ID_TO_VALUE(id) (JSID_IS_ATOM(id) ? ATOM_JSID_TO_JSVAL(id) : \ - JSID_IS_OBJECT(id) ? OBJECT_JSID_TO_JSVAL(id) : \ - (jsval)(id)) -#define HASH_ID(id) (JSID_IS_ATOM(id) ? JSID_TO_ATOM(id)->number : \ - JSID_IS_OBJECT(id) ? (jsatomid) JSID_CLRTAG(id) : \ - (jsatomid) JSID_TO_INT(id)) - -extern JS_FRIEND_API(JSScopeProperty **) -js_SearchScope(JSScope *scope, jsid id, JSBool adding); - -#define SCOPE_GET_PROPERTY(scope, id) \ - SPROP_FETCH(js_SearchScope(scope, id, JS_FALSE)) - -#define SCOPE_HAS_PROPERTY(scope, sprop) \ - (SCOPE_GET_PROPERTY(scope, (sprop)->id) == (sprop)) - -extern JSScopeProperty * -js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id, - JSPropertyOp getter, JSPropertyOp setter, uint32 slot, - uintN attrs, uintN flags, intN shortid); - -extern JSScopeProperty * -js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope, - JSScopeProperty *sprop, uintN attrs, uintN mask, - JSPropertyOp getter, JSPropertyOp setter); - -extern JSBool -js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id); - -extern void -js_ClearScope(JSContext *cx, JSScope *scope); - -/* - * These macros used to inline short code sequences, but they grew over time. - * We retain them for internal backward compatibility, and in case one or both - * ever shrink to inline-able size. - */ -#define MARK_ID(cx,id) js_MarkId(cx, id) -#define MARK_SCOPE_PROPERTY(cx,sprop) js_MarkScopeProperty(cx, sprop) - -extern void -js_MarkId(JSContext *cx, jsid id); - -extern void -js_MarkScopeProperty(JSContext *cx, JSScopeProperty *sprop); - -extern void -js_SweepScopeProperties(JSRuntime *rt); - -extern JSBool -js_InitPropertyTree(JSRuntime *rt); - -extern void -js_FinishPropertyTree(JSRuntime *rt); - -#endif /* jsscope_h___ */ diff --git a/spidermonkey/libjs/jsscript.c b/spidermonkey/libjs/jsscript.c deleted file mode 100644 index 73298a4..0000000 --- a/spidermonkey/libjs/jsscript.c +++ /dev/null @@ -1,1717 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS script operations. - */ -#include "jsstddef.h" -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsatom.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsdbgapi.h" -#include "jsemit.h" -#include "jsfun.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsopcode.h" -#include "jsscript.h" -#if JS_HAS_XDR -#include "jsxdrapi.h" -#endif - -#if JS_HAS_SCRIPT_OBJECT - -static const char js_script_exec[] = "Script.prototype.exec"; -static const char js_script_compile[] = "Script.prototype.compile"; - -/* - * This routine requires that obj has been locked previously. - */ -static jsint -GetScriptExecDepth(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); - return JSVAL_TO_INT(v); -} - -static void -AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) -{ - jsint execDepth; - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), - INT_TO_JSVAL(execDepth + delta)); - JS_UNLOCK_OBJ(cx, obj); -} - -#if JS_HAS_TOSOURCE -static JSBool -script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - uint32 indent; - JSScript *script; - size_t i, j, k, n; - char buf[16]; - jschar *s, *t; - JSString *str; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - indent = 0; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - - script = (JSScript *) JS_GetPrivate(cx, obj); - - /* Let n count the source string length, j the "front porch" length. */ - j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name); - n = j + 2; - if (!script) { - /* Let k count the constructor argument string length. */ - k = 0; - s = NULL; /* quell GCC overwarning */ - } else { - str = JS_DecompileScript(cx, script, "Script.prototype.toSource", - (uintN)indent); - if (!str) - return JS_FALSE; - str = js_QuoteString(cx, str, '\''); - if (!str) - return JS_FALSE; - s = JSSTRING_CHARS(str); - k = JSSTRING_LENGTH(str); - n += k; - } - - /* Allocate the source string and copy into it. */ - t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!t) - return JS_FALSE; - for (i = 0; i < j; i++) - t[i] = buf[i]; - for (j = 0; j < k; i++, j++) - t[i] = s[j]; - t[i++] = ')'; - t[i++] = ')'; - t[i] = 0; - - /* Create and return a JS string for t. */ - str = JS_NewUCString(cx, t, n); - if (!str) { - JS_free(cx, t); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif /* JS_HAS_TOSOURCE */ - -static JSBool -script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - uint32 indent; - JSScript *script; - JSString *str; - - indent = 0; - if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent)) - return JS_FALSE; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) { - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - str = JS_DecompileScript(cx, script, "Script.prototype.toString", - (uintN)indent); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - JSObject *scopeobj; - jsval v; - JSScript *script, *oldscript; - JSStackFrame *fp, *caller; - const char *file; - uintN line; - JSPrincipals *principals; - jsint execDepth; - - /* Make sure obj is a Script object. */ - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - /* If no args, leave private undefined and return early. */ - if (argc == 0) - goto out; - - /* Otherwise, the first arg is the script source to compile. */ - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - scopeobj = NULL; - if (argc >= 2) { - if (!js_ValueToObject(cx, argv[1], &scopeobj)) - return JS_FALSE; - argv[1] = OBJECT_TO_JSVAL(scopeobj); - } - - /* Compile using the caller's scope chain, which js_Invoke passes to fp. */ - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain); - - if (caller) { - if (!scopeobj) { - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) - return JS_FALSE; - fp->scopeChain = scopeobj; /* for the compiler's benefit */ - } - - principals = JS_EvalFramePrincipals(cx, fp, caller); - if (principals == caller->script->principals) { - file = caller->script->filename; - line = js_PCToLineNumber(cx, caller->script, caller->pc); - } else { - file = principals->codebase; - line = 0; - } - } else { - file = NULL; - line = 0; - principals = NULL; - } - - /* Ensure we compile this script with the right (inner) principals. */ - scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile); - if (!scopeobj) - return JS_FALSE; - - /* - * Compile the new script using the caller's scope chain, a la eval(). - * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's - * flags, because compilation is here separated from execution, and the - * run-time scope chain may not match the compile-time. JSFRAME_EVAL is - * tested in jsemit.c and jsscan.c to optimize based on identity of run- - * and compile-time scope. - */ - fp->flags |= JSFRAME_SCRIPT_OBJECT; - script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, - JSSTRING_CHARS(str), - JSSTRING_LENGTH(str), - file, line); - if (!script) - return JS_FALSE; - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - - /* - * execDepth must be 0 to allow compilation here, otherwise the JSScript - * struct can be released while running. - */ - if (execDepth > 0) { - JS_UNLOCK_OBJ(cx, obj); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_COMPILE_EXECED_SCRIPT); - return JS_FALSE; - } - - /* Swap script for obj's old script, if any. */ - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE); - oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); - JS_UNLOCK_OBJ(cx, obj); - - if (oldscript) - js_DestroyScript(cx, oldscript); - - script->object = obj; - js_CallNewScriptHook(cx, script, NULL); - -out: - /* Return the object. */ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *scopeobj, *parent; - JSStackFrame *fp, *caller; - JSScript *script; - JSBool ok; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - scopeobj = NULL; - if (argc) { - if (!js_ValueToObject(cx, argv[0], &scopeobj)) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(scopeobj); - } - - /* - * Emulate eval() by using caller's this, var object, sharp array, etc., - * all propagated by js_Execute via a non-null fourth (down) argument to - * js_Execute. If there is no scripted caller, js_Execute uses its second - * (chain) argument to set the exec frame's varobj, thisp, and scopeChain. - * - * Unlike eval, which the compiler detects, Script.prototype.exec may be - * called from a lightweight function, or even from native code (in which - * case fp->varobj and fp->scopeChain are null). If exec is called from - * a lightweight function, we will need to get a Call object representing - * its frame, to act as the var object and scope chain head. - */ - fp = cx->fp; - caller = JS_GetScriptedCaller(cx, fp); - if (caller && !caller->varobj) { - /* Called from a lightweight function. */ - JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags)); - - /* Scope chain links from Call object to callee's parent. */ - parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2])); - if (!js_GetCallObject(cx, caller, parent)) - return JS_FALSE; - } - - if (!scopeobj) { - /* No scope object passed in: try to use the caller's scope chain. */ - if (caller) { - /* - * Load caller->scopeChain after the conditional js_GetCallObject - * call above, which resets scopeChain as well as varobj. - */ - scopeobj = js_GetScopeChain(cx, caller); - if (!scopeobj) - return JS_FALSE; - } else { - /* - * Called from native code, so we don't know what scope object to - * use. We could use parent (see above), but Script.prototype.exec - * might be a shared/sealed "superglobal" method. A more general - * approach would use cx->globalObject, which will be the same as - * exec.__parent__ in the non-superglobal case. In the superglobal - * case it's the right object: the global, not the superglobal. - */ - scopeobj = cx->globalObject; - } - } - - scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec); - if (!scopeobj) - return JS_FALSE; - - /* Keep track of nesting depth for the script. */ - AdjustScriptExecDepth(cx, obj, 1); - - /* Must get to out label after this */ - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) { - ok = JS_FALSE; - goto out; - } - - /* Belt-and-braces: check that this script object has access to scopeobj. */ - ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals, - CLASS_ATOM(cx, Script)); - if (!ok) - goto out; - - ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); - -out: - AdjustScriptExecDepth(cx, obj, -1); - return ok; -} - -#if JS_HAS_XDR - -static JSBool -XDRAtomMap(JSXDRState *xdr, JSAtomMap *map) -{ - JSContext *cx; - uint32 natoms, i, index; - JSAtom **atoms; - - cx = xdr->cx; - - if (xdr->mode == JSXDR_ENCODE) - natoms = (uint32)map->length; - - if (!JS_XDRUint32(xdr, &natoms)) - return JS_FALSE; - - if (xdr->mode == JSXDR_ENCODE) { - atoms = map->vector; - } else { - if (natoms == 0) { - atoms = NULL; - } else { - atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms); - if (!atoms) - return JS_FALSE; -#ifdef DEBUG - memset(atoms, 0, (size_t)natoms * sizeof *atoms); -#endif - } - - map->vector = atoms; - map->length = natoms; - } - - for (i = 0; i != natoms; ++i) { - if (xdr->mode == JSXDR_ENCODE) - index = i; - if (!JS_XDRUint32(xdr, &index)) - goto bad; - - /* - * Assert that, when decoding, the read index is valid and points to - * an unoccupied element of atoms array. - */ - JS_ASSERT(index < natoms); - JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]); - if (!js_XDRAtom(xdr, &atoms[index])) - goto bad; - } - - return JS_TRUE; - - bad: - if (xdr->mode == JSXDR_DECODE) { - JS_free(cx, atoms); - map->vector = NULL; - map->length = 0; - } - - return JS_FALSE; -} - -JSBool -js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic) -{ - JSContext *cx; - JSScript *script, *newscript, *oldscript; - uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes; - uint32 prologLength, version; - JSBool filenameWasSaved; - jssrcnote *notes, *sn; - - cx = xdr->cx; - script = *scriptp; - nsrcnotes = ntrynotes = 0; - filenameWasSaved = JS_FALSE; - notes = NULL; - - /* - * Encode prologLength and version after script->length (_2 or greater), - * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. - * Version _3 supports principals serialization. Version _4 reorders the - * nsrcnotes and ntrynotes fields to come before everything except magic, - * length, prologLength, and version, so that srcnote and trynote storage - * can be allocated as part of the JSScript (along with bytecode storage). - * - * So far, the magic number has not changed for every jsopcode.tbl change. - * We stipulate forward compatibility by requiring old bytecodes never to - * change or go away (modulo a few exceptions before the XDR interfaces - * evolved, and a few exceptions during active trunk development). With - * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new - * magic number (_5) so that we know to append JSOP_STOP to old scripts - * when deserializing. - */ - if (xdr->mode == JSXDR_ENCODE) - magic = JSXDR_MAGIC_SCRIPT_CURRENT; - if (!JS_XDRUint32(xdr, &magic)) - return JS_FALSE; - JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); - if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { - if (!hasMagic) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_SCRIPT_MAGIC); - return JS_FALSE; - } - *hasMagic = JS_FALSE; - return JS_TRUE; - } - if (hasMagic) - *hasMagic = JS_TRUE; - - if (xdr->mode == JSXDR_ENCODE) { - length = script->length; - prologLength = PTRDIFF(script->main, script->code, jsbytecode); - JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN); - version = (uint32)script->version | (script->numGlobalVars << 16); - lineno = (uint32)script->lineno; - depth = (uint32)script->depth; - - /* Count the srcnotes, keeping notes pointing at the first one. */ - notes = SCRIPT_NOTES(script); - for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) - continue; - nsrcnotes = PTRDIFF(sn, notes, jssrcnote); - nsrcnotes++; /* room for the terminator */ - - /* Count the trynotes. */ - if (script->trynotes) { - while (script->trynotes[ntrynotes].catchStart) - ntrynotes++; - ntrynotes++; /* room for the end marker */ - } - } - - if (!JS_XDRUint32(xdr, &length)) - return JS_FALSE; - if (magic >= JSXDR_MAGIC_SCRIPT_2) { - if (!JS_XDRUint32(xdr, &prologLength)) - return JS_FALSE; - if (!JS_XDRUint32(xdr, &version)) - return JS_FALSE; - - /* To fuse allocations, we need srcnote and trynote counts early. */ - if (magic >= JSXDR_MAGIC_SCRIPT_4) { - if (!JS_XDRUint32(xdr, &nsrcnotes)) - return JS_FALSE; - if (!JS_XDRUint32(xdr, &ntrynotes)) - return JS_FALSE; - } - } - - if (xdr->mode == JSXDR_DECODE) { - size_t alloclength = length; - if (magic < JSXDR_MAGIC_SCRIPT_5) - ++alloclength; /* add a byte for JSOP_STOP */ - - script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); - if (!script) - return JS_FALSE; - if (magic >= JSXDR_MAGIC_SCRIPT_2) { - script->main += prologLength; - script->version = (JSVersion) (version & 0xffff); - script->numGlobalVars = (uint16) (version >> 16); - - /* If we know nsrcnotes, we allocated space for notes in script. */ - if (magic >= JSXDR_MAGIC_SCRIPT_4) - notes = SCRIPT_NOTES(script); - } - *scriptp = script; - } - - /* - * Control hereafter must goto error on failure, in order for the DECODE - * case to destroy script and conditionally free notes, which if non-null - * in the (DECODE and magic < _4) case must point at a temporary vector - * allocated just below. - */ - oldscript = xdr->script; - xdr->script = script; - if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || - !XDRAtomMap(xdr, &script->atomMap)) { - goto error; - } - - if (magic < JSXDR_MAGIC_SCRIPT_5) { - if (xdr->mode == JSXDR_DECODE) { - /* - * Append JSOP_STOP to old scripts, to relieve the interpreter - * from having to bounds-check pc. Also take care to increment - * length, as it is used below and must count all bytecode. - */ - script->code[length++] = JSOP_STOP; - } - - if (magic < JSXDR_MAGIC_SCRIPT_4) { - if (!JS_XDRUint32(xdr, &nsrcnotes)) - goto error; - if (xdr->mode == JSXDR_DECODE) { - notes = (jssrcnote *) - JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); - if (!notes) - goto error; - } - } - } - - if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || - !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || - !JS_XDRUint32(xdr, &lineno) || - !JS_XDRUint32(xdr, &depth) || - (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { - goto error; - } - - /* Script principals transcoding support comes with versions >= _3. */ - if (magic >= JSXDR_MAGIC_SCRIPT_3) { - JSPrincipals *principals; - uint32 encodeable; - - if (xdr->mode == JSXDR_ENCODE) { - principals = script->principals; - encodeable = (cx->runtime->principalsTranscoder != NULL); - if (!JS_XDRUint32(xdr, &encodeable)) - goto error; - if (encodeable && - !cx->runtime->principalsTranscoder(xdr, &principals)) { - goto error; - } - } else { - if (!JS_XDRUint32(xdr, &encodeable)) - goto error; - if (encodeable) { - if (!cx->runtime->principalsTranscoder) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_DECODE_PRINCIPALS); - goto error; - } - if (!cx->runtime->principalsTranscoder(xdr, &principals)) - goto error; - script->principals = principals; - } - } - } - - if (xdr->mode == JSXDR_DECODE) { - const char *filename = script->filename; - if (filename) { - filename = js_SaveScriptFilename(cx, filename); - if (!filename) - goto error; - JS_free(cx, (void *) script->filename); - script->filename = filename; - filenameWasSaved = JS_TRUE; - } - script->lineno = (uintN)lineno; - script->depth = (uintN)depth; - - if (magic < JSXDR_MAGIC_SCRIPT_4) { - /* - * Argh, we have to reallocate script, copy notes into the extra - * space after the bytecodes, and free the temporary notes vector. - * First, add enough slop to nsrcnotes so we can align the address - * after the srcnotes of the first trynote. - */ - uint32 osrcnotes = nsrcnotes; - - if (ntrynotes) - nsrcnotes += JSTRYNOTE_ALIGNMASK; - newscript = (JSScript *) JS_realloc(cx, script, - sizeof(JSScript) + - length * sizeof(jsbytecode) + - nsrcnotes * sizeof(jssrcnote) + - ntrynotes * sizeof(JSTryNote)); - if (!newscript) - goto error; - - *scriptp = script = newscript; - script->code = (jsbytecode *)(script + 1); - script->main = script->code + prologLength; - memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote)); - JS_free(cx, (void *) notes); - notes = NULL; - if (ntrynotes) { - script->trynotes = (JSTryNote *) - ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & - ~(jsword)JSTRYNOTE_ALIGNMASK); - memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); - } - } - } - - while (ntrynotes) { - JSTryNote *tn = &script->trynotes[--ntrynotes]; - uint32 start = (uint32) tn->start, - catchLength = (uint32) tn->length, - catchStart = (uint32) tn->catchStart; - - if (!JS_XDRUint32(xdr, &start) || - !JS_XDRUint32(xdr, &catchLength) || - !JS_XDRUint32(xdr, &catchStart)) { - goto error; - } - tn->start = (ptrdiff_t) start; - tn->length = (ptrdiff_t) catchLength; - tn->catchStart = (ptrdiff_t) catchStart; - } - - xdr->script = oldscript; - return JS_TRUE; - - error: - if (xdr->mode == JSXDR_DECODE) { - if (script->filename && !filenameWasSaved) { - JS_free(cx, (void *) script->filename); - script->filename = NULL; - } - if (notes && magic < JSXDR_MAGIC_SCRIPT_4) - JS_free(cx, (void *) notes); - js_DestroyScript(cx, script); - *scriptp = NULL; - } - return JS_FALSE; -} - -#if JS_HAS_XDR_FREEZE_THAW -/* - * These cannot be exposed to web content, and chrome does not need them, so - * we take them out of the Mozilla client altogether. Fortunately, there is - * no way to serialize a native function (see fun_xdrObject in jsfun.c). - */ - -static JSBool -script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXDRState *xdr; - JSScript *script; - JSBool ok, hasMagic; - uint32 len; - void *buf; - JSString *str; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - script = (JSScript *) JS_GetPrivate(cx, obj); - if (!script) - return JS_TRUE; - - /* create new XDR */ - xdr = JS_XDRNewMem(cx, JSXDR_ENCODE); - if (!xdr) - return JS_FALSE; - - /* write */ - ok = js_XDRScript(xdr, &script, &hasMagic); - if (!ok) - goto out; - if (!hasMagic) { - *rval = JSVAL_VOID; - goto out; - } - - buf = JS_XDRMemGetData(xdr, &len); - if (!buf) { - ok = JS_FALSE; - goto out; - } - - JS_ASSERT((jsword)buf % sizeof(jschar) == 0); - len /= sizeof(jschar); - str = JS_NewUCStringCopyN(cx, (jschar *)buf, len); - if (!str) { - ok = JS_FALSE; - goto out; - } - -#if IS_BIG_ENDIAN - { - jschar *chars; - uint32 i; - - /* Swap bytes in Unichars to keep frozen strings machine-independent. */ - chars = JS_GetStringChars(str); - for (i = 0; i < len; i++) - chars[i] = JSXDR_SWAB16(chars[i]); - } -#endif - *rval = STRING_TO_JSVAL(str); - -out: - JS_XDRDestroy(xdr); - return ok; -} - -static JSBool -script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXDRState *xdr; - JSString *str; - void *buf; - uint32 len; - jsval v; - JSScript *script, *oldscript; - JSBool ok, hasMagic; - - if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv)) - return JS_FALSE; - - if (argc == 0) - return JS_TRUE; - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - /* create new XDR */ - xdr = JS_XDRNewMem(cx, JSXDR_DECODE); - if (!xdr) - return JS_FALSE; - - buf = JS_GetStringChars(str); - len = JS_GetStringLength(str); -#if IS_BIG_ENDIAN - { - jschar *from, *to; - uint32 i; - - /* Swap bytes in Unichars to keep frozen strings machine-independent. */ - from = (jschar *)buf; - to = (jschar *) JS_malloc(cx, len * sizeof(jschar)); - if (!to) { - JS_XDRDestroy(xdr); - return JS_FALSE; - } - for (i = 0; i < len; i++) - to[i] = JSXDR_SWAB16(from[i]); - buf = (char *)to; - } -#endif - len *= sizeof(jschar); - JS_XDRMemSetData(xdr, buf, len); - - /* XXXbe should magic mismatch be error, or false return value? */ - ok = js_XDRScript(xdr, &script, &hasMagic); - if (!ok) - goto out; - if (!hasMagic) { - *rval = JSVAL_FALSE; - goto out; - } - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - - /* - * execDepth must be 0 to allow compilation here, otherwise the JSScript - * struct can be released while running. - */ - if (execDepth > 0) { - JS_UNLOCK_OBJ(cx, obj); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_COMPILE_EXECED_SCRIPT); - goto out; - } - - /* Swap script for obj's old script, if any. */ - v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL; - LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script)); - JS_UNLOCK_OBJ(cx, obj); - - if (oldscript) - js_DestroyScript(cx, oldscript); - - script->object = obj; - js_CallNewScriptHook(cx, script, NULL); - -out: - /* - * We reset the buffer to be NULL so that it doesn't free the chars - * memory owned by str (argv[0]). - */ - JS_XDRMemSetData(xdr, NULL, 0); - JS_XDRDestroy(xdr); -#if IS_BIG_ENDIAN - JS_free(cx, buf); -#endif - *rval = JSVAL_TRUE; - return ok; -} - -static const char js_thaw_str[] = "thaw"; - -#endif /* JS_HAS_XDR_FREEZE_THAW */ -#endif /* JS_HAS_XDR */ - -static JSFunctionSpec script_methods[] = { -#if JS_HAS_TOSOURCE - {js_toSource_str, script_toSource, 0,0,0}, -#endif - {js_toString_str, script_toString, 0,0,0}, - {"compile", script_compile, 2,0,0}, - {"exec", script_exec, 1,0,0}, -#if JS_HAS_XDR_FREEZE_THAW - {"freeze", script_freeze, 0,0,0}, - {js_thaw_str, script_thaw, 1,0,0}, -#endif /* JS_HAS_XDR_FREEZE_THAW */ - {0,0,0,0,0} -}; - -#endif /* JS_HAS_SCRIPT_OBJECT */ - -static void -script_finalize(JSContext *cx, JSObject *obj) -{ - JSScript *script; - - script = (JSScript *) JS_GetPrivate(cx, obj); - if (script) - js_DestroyScript(cx, script); -} - -static JSBool -script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ -#if JS_HAS_SCRIPT_OBJECT - return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval); -#else - return JS_FALSE; -#endif -} - -static uint32 -script_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSScript *script; - - script = (JSScript *) JS_GetPrivate(cx, obj); - if (script) - js_MarkScript(cx, script); - return 0; -} - -#if !JS_HAS_SCRIPT_OBJECT -const char js_Script_str[] = "Script"; - -#define JSProto_Script JSProto_Object -#endif - -JS_FRIEND_DATA(JSClass) js_ScriptClass = { - js_Script_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) | - JSCLASS_HAS_RESERVED_SLOTS(1), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, - NULL, NULL, script_call, NULL,/*XXXbe xdr*/ - NULL, NULL, script_mark, 0 -}; - -#if JS_HAS_SCRIPT_OBJECT - -static JSBool -Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - /* If not constructing, replace obj with a new Script object. */ - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return JS_FALSE; - - /* - * script_compile does not use rval to root its temporaries - * so we can use it to root obj. - */ - *rval = OBJECT_TO_JSVAL(obj); - } - - if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0))) - return JS_FALSE; - - return script_compile(cx, obj, argc, argv, rval); -} - -#if JS_HAS_XDR_FREEZE_THAW - -static JSBool -script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL); - if (!obj) - return JS_FALSE; - if (!script_thaw(cx, obj, argc, argv, rval)) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec script_static_methods[] = { - {js_thaw_str, script_static_thaw, 1,0,0}, - {0,0,0,0,0} -}; - -#else /* !JS_HAS_XDR_FREEZE_THAW */ - -#define script_static_methods NULL - -#endif /* !JS_HAS_XDR_FREEZE_THAW */ - -JSObject * -js_InitScriptClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1, - NULL, script_methods, NULL, script_static_methods); -} - -#endif /* JS_HAS_SCRIPT_OBJECT */ - -/* - * Shared script filename management. - */ -JS_STATIC_DLL_CALLBACK(int) -js_compare_strings(const void *k1, const void *k2) -{ - return strcmp(k1, k2) == 0; -} - -/* Shared with jsatom.c to save code space. */ -extern void * JS_DLL_CALLBACK -js_alloc_table_space(void *priv, size_t size); - -extern void JS_DLL_CALLBACK -js_free_table_space(void *priv, void *item); - -/* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */ -typedef struct ScriptFilenameEntry { - JSHashEntry *next; /* hash chain linkage */ - JSHashNumber keyHash; /* key hash function result */ - const void *key; /* ptr to filename, below */ - uint32 flags; /* user-defined filename prefix flags */ - JSPackedBool mark; /* GC mark flag */ - char filename[3]; /* two or more bytes, NUL-terminated */ -} ScriptFilenameEntry; - -JS_STATIC_DLL_CALLBACK(JSHashEntry *) -js_alloc_sftbl_entry(void *priv, const void *key) -{ - size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1; - - return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry))); -} - -JS_STATIC_DLL_CALLBACK(void) -js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag) -{ - if (flag != HT_FREE_ENTRY) - return; - free(he); -} - -static JSHashAllocOps sftbl_alloc_ops = { - js_alloc_table_space, js_free_table_space, - js_alloc_sftbl_entry, js_free_sftbl_entry -}; - -JSBool -js_InitRuntimeScriptState(JSRuntime *rt) -{ -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = JS_NEW_LOCK(); - if (!rt->scriptFilenameTableLock) - return JS_FALSE; -#endif - JS_ASSERT(!rt->scriptFilenameTable); - rt->scriptFilenameTable = - JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL, - &sftbl_alloc_ops, NULL); - if (!rt->scriptFilenameTable) { - js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */ - return JS_FALSE; - } - JS_INIT_CLIST(&rt->scriptFilenamePrefixes); - return JS_TRUE; -} - -typedef struct ScriptFilenamePrefix { - JSCList links; /* circular list linkage for easy deletion */ - const char *name; /* pointer to pinned ScriptFilenameEntry string */ - size_t length; /* prefix string length, precomputed */ - uint32 flags; /* user-defined flags to inherit from this prefix */ -} ScriptFilenamePrefix; - -void -js_FinishRuntimeScriptState(JSRuntime *rt) -{ - if (rt->scriptFilenameTable) { - JS_HashTableDestroy(rt->scriptFilenameTable); - rt->scriptFilenameTable = NULL; - } -#ifdef JS_THREADSAFE - if (rt->scriptFilenameTableLock) { - JS_DESTROY_LOCK(rt->scriptFilenameTableLock); - rt->scriptFilenameTableLock = NULL; - } -#endif -} - -void -js_FreeRuntimeScriptState(JSRuntime *rt) -{ - ScriptFilenamePrefix *sfp; - - if (!rt->scriptFilenameTable) - return; - - while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) { - sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next; - JS_REMOVE_LINK(&sfp->links); - free(sfp); - } - js_FinishRuntimeScriptState(rt); -} - -#ifdef DEBUG_brendan -#define DEBUG_SFTBL -#endif -#ifdef DEBUG_SFTBL -size_t sftbl_savings = 0; -#endif - -static ScriptFilenameEntry * -SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags) -{ - JSHashTable *table; - JSHashNumber hash; - JSHashEntry **hep; - ScriptFilenameEntry *sfe; - size_t length; - JSCList *head, *link; - ScriptFilenamePrefix *sfp; - - table = rt->scriptFilenameTable; - hash = JS_HashString(filename); - hep = JS_HashTableRawLookup(table, hash, filename); - sfe = (ScriptFilenameEntry *) *hep; -#ifdef DEBUG_SFTBL - if (sfe) - sftbl_savings += strlen(sfe->filename); -#endif - - if (!sfe) { - sfe = (ScriptFilenameEntry *) - JS_HashTableRawAdd(table, hep, hash, filename, NULL); - if (!sfe) - return NULL; - sfe->key = strcpy(sfe->filename, filename); - sfe->flags = 0; - sfe->mark = JS_FALSE; - } - - /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */ - if (flags != 0) { - /* Search in case filename was saved already; we must be idempotent. */ - sfp = NULL; - length = strlen(filename); - for (head = link = &rt->scriptFilenamePrefixes; - link->next != head; - link = link->next) { - /* Lag link behind sfp to insert in non-increasing length order. */ - sfp = (ScriptFilenamePrefix *) link->next; - if (!strcmp(sfp->name, filename)) - break; - if (sfp->length <= length) { - sfp = NULL; - break; - } - sfp = NULL; - } - - if (!sfp) { - /* No such prefix: add one now. */ - sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix)); - if (!sfp) - return NULL; - JS_INSERT_AFTER(&sfp->links, link); - sfp->name = sfe->filename; - sfp->length = length; - sfp->flags = 0; - } - - /* - * Accumulate flags in both sfe and sfp: sfe for later access from the - * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer - * filename entries can inherit by prefix. - */ - sfe->flags |= flags; - sfp->flags |= flags; - } - - return sfe; -} - -const char * -js_SaveScriptFilename(JSContext *cx, const char *filename) -{ - JSRuntime *rt; - ScriptFilenameEntry *sfe; - JSCList *head, *link; - ScriptFilenamePrefix *sfp; - - rt = cx->runtime; - JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); - sfe = SaveScriptFilename(rt, filename, 0); - if (!sfe) { - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - JS_ReportOutOfMemory(cx); - return NULL; - } - - /* - * Try to inherit flags by prefix. We assume there won't be more than a - * few (dozen! ;-) prefixes, so linear search is tolerable. - * XXXbe every time I've assumed that in the JS engine, I've been wrong! - */ - for (head = &rt->scriptFilenamePrefixes, link = head->next; - link != head; - link = link->next) { - sfp = (ScriptFilenamePrefix *) link; - if (!strncmp(sfp->name, filename, sfp->length)) { - sfe->flags |= sfp->flags; - break; - } - } - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - return sfe->filename; -} - -const char * -js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags) -{ - ScriptFilenameEntry *sfe; - - /* This may be called very early, via the jsdbgapi.h entry point. */ - if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt)) - return NULL; - - JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock); - sfe = SaveScriptFilename(rt, filename, flags); - JS_RELEASE_LOCK(rt->scriptFilenameTableLock); - if (!sfe) - return NULL; - - return sfe->filename; -} - -/* - * Back up from a saved filename by its offset within its hash table entry. - */ -#define FILENAME_TO_SFE(fn) \ - ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename))) - -/* - * The sfe->key member, redundant given sfe->filename but required by the old - * jshash.c code, here gives us a useful sanity check. This assertion will - * very likely botch if someone tries to mark a string that wasn't allocated - * as an sfe->filename. - */ -#define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename) - -uint32 -js_GetScriptFilenameFlags(const char *filename) -{ - ScriptFilenameEntry *sfe; - - sfe = FILENAME_TO_SFE(filename); - ASSERT_VALID_SFE(sfe); - return sfe->flags; -} - -void -js_MarkScriptFilename(const char *filename) -{ - ScriptFilenameEntry *sfe; - - sfe = FILENAME_TO_SFE(filename); - ASSERT_VALID_SFE(sfe); - sfe->mark = JS_TRUE; -} - -JS_STATIC_DLL_CALLBACK(intN) -js_script_filename_marker(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - sfe->mark = JS_TRUE; - return HT_ENUMERATE_NEXT; -} - -void -js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms) -{ - JSCList *head, *link; - ScriptFilenamePrefix *sfp; - - if (!rt->scriptFilenameTable) - return; - - if (keepAtoms) { - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_marker, - rt); - } - for (head = &rt->scriptFilenamePrefixes, link = head->next; - link != head; - link = link->next) { - sfp = (ScriptFilenamePrefix *) link; - js_MarkScriptFilename(sfp->name); - } -} - -JS_STATIC_DLL_CALLBACK(intN) -js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg) -{ - ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he; - - if (!sfe->mark) - return HT_ENUMERATE_REMOVE; - sfe->mark = JS_FALSE; - return HT_ENUMERATE_NEXT; -} - -void -js_SweepScriptFilenames(JSRuntime *rt) -{ - if (!rt->scriptFilenameTable) - return; - - JS_HashTableEnumerateEntries(rt->scriptFilenameTable, - js_script_filename_sweeper, - rt); -#ifdef DEBUG_notme -#ifdef DEBUG_SFTBL - printf("script filename table savings so far: %u\n", sftbl_savings); -#endif -#endif -} - -JSScript * -js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes) -{ - JSScript *script; - - /* Round up source note count to align script->trynotes for its type. */ - if (ntrynotes) - nsrcnotes += JSTRYNOTE_ALIGNMASK; - script = (JSScript *) JS_malloc(cx, - sizeof(JSScript) + - length * sizeof(jsbytecode) + - nsrcnotes * sizeof(jssrcnote) + - ntrynotes * sizeof(JSTryNote)); - if (!script) - return NULL; - memset(script, 0, sizeof(JSScript)); - script->code = script->main = (jsbytecode *)(script + 1); - script->length = length; - script->version = cx->version; - if (ntrynotes) { - script->trynotes = (JSTryNote *) - ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) & - ~(jsword)JSTRYNOTE_ALIGNMASK); - memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote)); - } - return script; -} - -JS_FRIEND_API(JSScript *) -js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) -{ - uint32 mainLength, prologLength, nsrcnotes, ntrynotes; - JSScript *script; - const char *filename; - - mainLength = CG_OFFSET(cg); - prologLength = CG_PROLOG_OFFSET(cg); - CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes); - CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes); - script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes); - if (!script) - return NULL; - - /* Now that we have script, error control flow must go to label bad. */ - script->main += prologLength; - memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); - memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); - script->numGlobalVars = cg->treeContext.numGlobalVars; - if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList)) - goto bad; - - filename = cg->filename; - if (filename) { - script->filename = js_SaveScriptFilename(cx, filename); - if (!script->filename) - goto bad; - } - script->lineno = cg->firstLine; - script->depth = cg->maxStackDepth; - if (cg->principals) { - script->principals = cg->principals; - JSPRINCIPALS_HOLD(cx, script->principals); - } - - if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script))) - goto bad; - if (script->trynotes) - js_FinishTakingTryNotes(cx, cg, script->trynotes); - - /* - * We initialize fun->u.script to be the script constructed above - * so that the debugger has a valid FUN_SCRIPT(fun). - */ - if (fun) { - JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); - fun->u.i.script = script; - if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) - fun->flags |= JSFUN_HEAVYWEIGHT; - } - - /* Tell the debugger about this compiled script. */ - js_CallNewScriptHook(cx, script, fun); - return script; - -bad: - js_DestroyScript(cx, script); - return NULL; -} - -JS_FRIEND_API(void) -js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun) -{ - JSRuntime *rt; - JSNewScriptHook hook; - - rt = cx->runtime; - hook = rt->newScriptHook; - if (hook) { - JS_KEEP_ATOMS(rt); - hook(cx, script->filename, script->lineno, script, fun, - rt->newScriptHookData); - JS_UNKEEP_ATOMS(rt); - } -} - -JS_FRIEND_API(void) -js_CallDestroyScriptHook(JSContext *cx, JSScript *script) -{ - JSRuntime *rt; - JSDestroyScriptHook hook; - - rt = cx->runtime; - hook = rt->destroyScriptHook; - if (hook) - hook(cx, script, rt->destroyScriptHookData); -} - -void -js_DestroyScript(JSContext *cx, JSScript *script) -{ - js_CallDestroyScriptHook(cx, script); - - JS_ClearScriptTraps(cx, script); - js_FreeAtomMap(cx, &script->atomMap); - if (script->principals) - JSPRINCIPALS_DROP(cx, script->principals); - if (JS_GSN_CACHE(cx).script == script) - JS_CLEAR_GSN_CACHE(cx); - JS_free(cx, script); -} - -void -js_MarkScript(JSContext *cx, JSScript *script) -{ - JSAtomMap *map; - uintN i, length; - JSAtom **vector; - - map = &script->atomMap; - length = map->length; - vector = map->vector; - for (i = 0; i < length; i++) - GC_MARK_ATOM(cx, vector[i]); - - if (script->filename) - js_MarkScriptFilename(script->filename); -} - -typedef struct GSNCacheEntry { - JSDHashEntryHdr hdr; - jsbytecode *pc; - jssrcnote *sn; -} GSNCacheEntry; - -#define GSN_CACHE_THRESHOLD 100 - -jssrcnote * -js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - ptrdiff_t target, offset; - GSNCacheEntry *entry; - jssrcnote *sn, *result; - uintN nsrcnotes; - - - target = PTRDIFF(pc, script->code, jsbytecode); - if ((uint32)target >= script->length) - return NULL; - - if (JS_GSN_CACHE(cx).script == script) { - JS_METER_GSN_CACHE(cx, hits); - entry = (GSNCacheEntry *) - JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, - JS_DHASH_LOOKUP); - return entry->sn; - } - - JS_METER_GSN_CACHE(cx, misses); - offset = 0; - for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) { - if (SN_IS_TERMINATOR(sn)) { - result = NULL; - break; - } - offset += SN_DELTA(sn); - if (offset == target && SN_IS_GETTABLE(sn)) { - result = sn; - break; - } - } - - if (JS_GSN_CACHE(cx).script != script && - script->length >= GSN_CACHE_THRESHOLD) { - JS_CLEAR_GSN_CACHE(cx); - nsrcnotes = 0; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - if (SN_IS_GETTABLE(sn)) - ++nsrcnotes; - } - if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(), - NULL, sizeof(GSNCacheEntry), nsrcnotes)) { - JS_GSN_CACHE(cx).table.ops = NULL; - } else { - pc = script->code; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); - sn = SN_NEXT(sn)) { - pc += SN_DELTA(sn); - if (SN_IS_GETTABLE(sn)) { - entry = (GSNCacheEntry *) - JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc, - JS_DHASH_ADD); - entry->pc = pc; - entry->sn = sn; - } - } - JS_GSN_CACHE(cx).script = script; - JS_METER_GSN_CACHE(cx, fills); - } - } - - return result; -} - -uintN -js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) -{ - JSAtom *atom; - JSFunction *fun; - uintN lineno; - ptrdiff_t offset, target; - jssrcnote *sn; - JSSrcNoteType type; - - /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */ - if (!pc) - return 0; - - /* - * Special case: function definition needs no line number note because - * the function's script contains its starting line number. - */ - if (*pc == JSOP_DEFFUN || - (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) { - atom = js_GetAtom(cx, &script->atomMap, - (*pc == JSOP_DEFFUN) - ? GET_ATOM_INDEX(pc) - : GET_LITERAL_INDEX(pc)); - fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom)); - JS_ASSERT(FUN_INTERPRETED(fun)); - return fun->u.i.script->lineno; - } - - /* - * General case: walk through source notes accumulating their deltas, - * keeping track of line-number notes, until we pass the note for pc's - * offset within script->code. - */ - lineno = script->lineno; - offset = 0; - target = PTRDIFF(pc, script->code, jsbytecode); - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - offset += SN_DELTA(sn); - type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) { - if (offset <= target) - lineno = (uintN) js_GetSrcNoteOffset(sn, 0); - } else if (type == SRC_NEWLINE) { - if (offset <= target) - lineno++; - } - if (offset > target) - break; - } - return lineno; -} - -/* The line number limit is the same as the jssrcnote offset limit. */ -#define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16) - -jsbytecode * -js_LineNumberToPC(JSScript *script, uintN target) -{ - ptrdiff_t offset, best; - uintN lineno, bestdiff, diff; - jssrcnote *sn; - JSSrcNoteType type; - - offset = 0; - best = -1; - lineno = script->lineno; - bestdiff = SN_LINE_LIMIT; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - if (lineno == target) - goto out; - if (lineno > target) { - diff = lineno - target; - if (diff < bestdiff) { - bestdiff = diff; - best = offset; - } - } - offset += SN_DELTA(sn); - type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = (uintN) js_GetSrcNoteOffset(sn, 0); - } else if (type == SRC_NEWLINE) { - lineno++; - } - } - if (best >= 0) - offset = best; -out: - return script->code + offset; -} - -JS_FRIEND_API(uintN) -js_GetScriptLineExtent(JSScript *script) -{ - uintN lineno; - jssrcnote *sn; - JSSrcNoteType type; - - lineno = script->lineno; - for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) { - type = (JSSrcNoteType) SN_TYPE(sn); - if (type == SRC_SETLINE) { - lineno = (uintN) js_GetSrcNoteOffset(sn, 0); - } else if (type == SRC_NEWLINE) { - lineno++; - } - } - return 1 + lineno - script->lineno; -} - -#if JS_HAS_GENERATORS - -jsbytecode * -js_FindFinallyHandler(JSScript *script, jsbytecode *pc) -{ - JSTryNote *tn; - ptrdiff_t off; - JSOp op2; - - tn = script->trynotes; - if (!tn) - return NULL; - - off = pc - script->main; - if (off < 0) - return NULL; - - JS_ASSERT(tn->catchStart != 0); - do { - if ((jsuword)(off - tn->start) < (jsuword)tn->length) { - /* - * We have a handler: is it the finally one, or a catch handler? - * - * Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK - * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION) - */ - pc = script->main + tn->catchStart; - JS_ASSERT(*pc == JSOP_SETSP); - op2 = pc[JSOP_SETSP_LENGTH]; - if (op2 != JSOP_ENTERBLOCK) { - JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION); - return pc; - } - } - } while ((++tn)->catchStart != 0); - return NULL; -} - -#endif diff --git a/spidermonkey/libjs/jsscript.h b/spidermonkey/libjs/jsscript.h deleted file mode 100644 index 18ad373..0000000 --- a/spidermonkey/libjs/jsscript.h +++ /dev/null @@ -1,225 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsscript_h___ -#define jsscript_h___ -/* - * JS script descriptor. - */ -#include "jsatom.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -/* - * Exception handling runtime information. - * - * All fields except length are code offsets relative to the main entry point - * of the script. If script->trynotes is not null, it points to a vector of - * these structs terminated by one with catchStart == 0. - */ -struct JSTryNote { - ptrdiff_t start; /* start of try statement */ - ptrdiff_t length; /* count of try statement bytecodes */ - ptrdiff_t catchStart; /* start of catch block (0 if end) */ -}; - -#define JSTRYNOTE_GRAIN sizeof(ptrdiff_t) -#define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1) - -struct JSScript { - jsbytecode *code; /* bytecodes and their immediate operands */ - uint32 length; /* length of code vector */ - jsbytecode *main; /* main entry point, after predef'ing prolog */ - uint16 version; /* JS version under which script was compiled */ - uint16 numGlobalVars; /* declared global var/const/function count */ - JSAtomMap atomMap; /* maps immediate index to literal struct */ - const char *filename; /* source filename or null */ - uintN lineno; /* base line number of script */ - uintN depth; /* maximum stack depth in slots */ - JSTryNote *trynotes; /* exception table for this script */ - JSPrincipals *principals; /* principals for this script */ - JSObject *object; /* optional Script-class object wrapper */ -}; - -/* No need to store script->notes now that it is allocated right after code. */ -#define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length)) - -#define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \ - JS_BEGIN_MACRO \ - JSTryNote *tn_ = (script)->trynotes; \ - jsbytecode *catchpc_ = NULL; \ - if (tn_) { \ - ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \ - if (off_ >= 0) { \ - while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \ - ++tn_; \ - if (tn_->catchStart) \ - catchpc_ = (script)->main + tn_->catchStart; \ - } \ - } \ - catchpc = catchpc_; \ - JS_END_MACRO - -/* - * Find the innermost finally block that handles the given pc. This is a - * version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used - * to implement generator.close(). - */ -jsbytecode * -js_FindFinallyHandler(JSScript *script, jsbytecode *pc); - -extern JS_FRIEND_DATA(JSClass) js_ScriptClass; - -extern JSObject * -js_InitScriptClass(JSContext *cx, JSObject *obj); - -/* - * On first new context in rt, initialize script runtime state, specifically - * the script filename table and its lock. - */ -extern JSBool -js_InitRuntimeScriptState(JSRuntime *rt); - -/* - * On last context destroy for rt, if script filenames are all GC'd, free the - * script filename table and its lock. - */ -extern void -js_FinishRuntimeScriptState(JSRuntime *rt); - -/* - * On JS_DestroyRuntime(rt), forcibly free script filename prefixes and any - * script filename table entries that have not been GC'd, the latter using - * js_FinishRuntimeScriptState. - * - * This allows script filename prefixes to outlive any context in rt. - */ -extern void -js_FreeRuntimeScriptState(JSRuntime *rt); - -extern const char * -js_SaveScriptFilename(JSContext *cx, const char *filename); - -extern const char * -js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags); - -extern uint32 -js_GetScriptFilenameFlags(const char *filename); - -extern void -js_MarkScriptFilename(const char *filename); - -extern void -js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms); - -extern void -js_SweepScriptFilenames(JSRuntime *rt); - -/* - * Two successively less primitive ways to make a new JSScript. The first - * does *not* call a non-null cx->runtime->newScriptHook -- only the second, - * js_NewScriptFromCG, calls this optional debugger hook. - * - * The js_NewScript function can't know whether the script it creates belongs - * to a function, or is top-level or eval code, but the debugger wants access - * to the newly made script's function, if any -- so callers of js_NewScript - * are responsible for notifying the debugger after successfully creating any - * kind (function or other) of new JSScript. - */ -extern JSScript * -js_NewScript(JSContext *cx, uint32 length, uint32 snlength, uint32 tnlength); - -extern JS_FRIEND_API(JSScript *) -js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); - -/* - * New-script-hook calling is factored from js_NewScriptFromCG so that it - * and callers of js_XDRScript can share this code. In the case of callers - * of js_XDRScript, the hook should be invoked only after successful decode - * of any owning function (the fun parameter) or script object (null fun). - */ -extern JS_FRIEND_API(void) -js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun); - -extern JS_FRIEND_API(void) -js_CallDestroyScriptHook(JSContext *cx, JSScript *script); - -extern void -js_DestroyScript(JSContext *cx, JSScript *script); - -extern void -js_MarkScript(JSContext *cx, JSScript *script); - -/* - * To perturb as little code as possible, we introduce a js_GetSrcNote lookup - * cache without adding an explicit cx parameter. Thus js_GetSrcNote becomes - * a macro that uses cx from its calls' lexical environments. - */ -#define js_GetSrcNote(script,pc) js_GetSrcNoteCached(cx, script, pc) - -extern jssrcnote * -js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc); - -/* XXX need cx to lock function objects declared by prolog bytecodes. */ -extern uintN -js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc); - -extern jsbytecode * -js_LineNumberToPC(JSScript *script, uintN lineno); - -extern JS_FRIEND_API(uintN) -js_GetScriptLineExtent(JSScript *script); - -/* - * If magic is non-null, js_XDRScript succeeds on magic number mismatch but - * returns false in *magic; it reflects a match via a true *magic out param. - * If magic is null, js_XDRScript returns false on bad magic number errors, - * which it reports. - * - * NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE - * and subsequent set-up of owning function or script object, if any. - */ -extern JSBool -js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *magic); - -JS_END_EXTERN_C - -#endif /* jsscript_h___ */ diff --git a/spidermonkey/libjs/jsshell.msg b/spidermonkey/libjs/jsshell.msg deleted file mode 100644 index 4b811ac..0000000 --- a/spidermonkey/libjs/jsshell.msg +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - Error messages for JSShell. See js.msg for format. -*/ - -MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "") -MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_NONE, "can't open {0}: {1}") -MSG_DEF(JSSMSG_TRAP_USAGE, 2, 0, JSEXN_NONE, "usage: trap [fun] [pc] expr") -MSG_DEF(JSSMSG_LINE2PC_USAGE, 3, 0, JSEXN_NONE, "usage: line2pc [fun] line") -MSG_DEF(JSSMSG_FILE_SCRIPTS_ONLY, 4, 0, JSEXN_NONE, "only works on JS scripts read from files") -MSG_DEF(JSSMSG_UNEXPECTED_EOF, 5, 1, JSEXN_NONE, "unexpected EOF in {0}") -MSG_DEF(JSSMSG_DOEXP_USAGE, 6, 0, JSEXN_NONE, "usage: doexp obj id") diff --git a/spidermonkey/libjs/jsstddef.h b/spidermonkey/libjs/jsstddef.h deleted file mode 100644 index addaa88..0000000 --- a/spidermonkey/libjs/jsstddef.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * stddef inclusion here to first declare ptrdif as a signed long instead of a - * signed int. - */ - -#ifdef _WINDOWS -# ifndef XP_WIN -# define XP_WIN -# endif -#if defined(_WIN32) || defined(WIN32) -# ifndef XP_WIN32 -# define XP_WIN32 -# endif -#else -# ifndef XP_WIN16 -# define XP_WIN16 -# endif -#endif -#endif - -#ifdef XP_WIN16 -#ifndef _PTRDIFF_T_DEFINED -typedef long ptrdiff_t; - -/* - * The Win16 compiler treats pointer differences as 16-bit signed values. - * This macro allows us to treat them as 17-bit signed values, stored in - * a 32-bit type. - */ -#define PTRDIFF(p1, p2, type) \ - ((((unsigned long)(p1)) - ((unsigned long)(p2))) / sizeof(type)) - -#define _PTRDIFF_T_DEFINED -#endif /*_PTRDIFF_T_DEFINED*/ -#else /*WIN16*/ - -#define PTRDIFF(p1, p2, type) \ - ((p1) - (p2)) - -#endif - -#include - - diff --git a/spidermonkey/libjs/jsstr.c b/spidermonkey/libjs/jsstr.c deleted file mode 100644 index e38f652..0000000 --- a/spidermonkey/libjs/jsstr.c +++ /dev/null @@ -1,4818 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=80: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * JS string type implementation. - * - * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these - * native methods store strings (possibly newborn) converted from their 'this' - * parameter and arguments on the stack: 'this' conversions at argv[-1], arg - * conversions at their index (argv[0], argv[1]). This is a legitimate method - * of rooting things that might lose their newborn root due to subsequent GC - * allocations in the same native method. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jshash.h" /* Added by JSIFY */ -#include "jsprf.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsconfig.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsregexp.h" -#include "jsstr.h" - -#define JSSTRDEP_RECURSION_LIMIT 100 - -size_t -js_MinimizeDependentStrings(JSString *str, int level, JSString **basep) -{ - JSString *base; - size_t start, length; - - JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); - base = JSSTRDEP_BASE(str); - start = JSSTRDEP_START(str); - if (JSSTRING_IS_DEPENDENT(base)) { - if (level < JSSTRDEP_RECURSION_LIMIT) { - start += js_MinimizeDependentStrings(base, level + 1, &base); - } else { - do { - start += JSSTRDEP_START(base); - base = JSSTRDEP_BASE(base); - } while (JSSTRING_IS_DEPENDENT(base)); - } - if (start == 0) { - JS_ASSERT(JSSTRING_IS_PREFIX(str)); - JSPREFIX_SET_BASE(str, base); - } else if (start <= JSSTRDEP_START_MASK) { - length = JSSTRDEP_LENGTH(str); - JSSTRDEP_SET_START_AND_LENGTH(str, start, length); - JSSTRDEP_SET_BASE(str, base); - } - } - *basep = base; - return start; -} - -jschar * -js_GetDependentStringChars(JSString *str) -{ - size_t start; - JSString *base; - - start = js_MinimizeDependentStrings(str, 0, &base); - JS_ASSERT(!JSSTRING_IS_DEPENDENT(base)); - JS_ASSERT(start < base->length); - return base->chars + start; -} - -jschar * -js_GetStringChars(JSString *str) -{ - if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(NULL, str)) - return NULL; - - *js_GetGCThingFlags(str) &= ~GCF_MUTABLE; - return str->chars; -} - -JSString * -js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) -{ - size_t rn, ln, lrdist, n; - jschar *rs, *ls, *s; - JSDependentString *ldep; /* non-null if left should become dependent */ - JSString *str; - - if (JSSTRING_IS_DEPENDENT(right)) { - rn = JSSTRDEP_LENGTH(right); - rs = JSSTRDEP_CHARS(right); - } else { - rn = right->length; - rs = right->chars; - } - if (rn == 0) - return left; - - if (JSSTRING_IS_DEPENDENT(left) || - !(*js_GetGCThingFlags(left) & GCF_MUTABLE)) { - /* We must copy if left does not own a buffer to realloc. */ - ln = JSSTRING_LENGTH(left); - if (ln == 0) - return right; - ls = JSSTRING_CHARS(left); - s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar)); - if (!s) - return NULL; - js_strncpy(s, ls, ln); - ldep = NULL; - } else { - /* We can realloc left's space and make it depend on our result. */ - ln = left->length; - if (ln == 0) - return right; - ls = left->chars; - s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar)); - if (!s) - return NULL; - - /* Take care: right could depend on left! */ - lrdist = (size_t)(rs - ls); - if (lrdist < ln) - rs = s + lrdist; - left->chars = ls = s; - ldep = JSSTRDEP(left); - } - - js_strncpy(s + ln, rs, rn); - n = ln + rn; - s[n] = 0; - str = js_NewString(cx, s, n, GCF_MUTABLE); - if (!str) { - /* Out of memory: clean up any space we (re-)allocated. */ - if (!ldep) { - JS_free(cx, s); - } else { - s = JS_realloc(cx, ls, (ln + 1) * sizeof(jschar)); - if (s) - left->chars = s; - } - } else { - /* Morph left into a dependent prefix if we realloc'd its buffer. */ - if (ldep) { - JSPREFIX_SET_LENGTH(ldep, ln); - JSPREFIX_SET_BASE(ldep, str); -#ifdef DEBUG - { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveDependentStrings); - JS_RUNTIME_METER(rt, totalDependentStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum += (double)ln, - rt->strdepLengthSquaredSum += (double)ln * (double)ln)); - } -#endif - } - } - - return str; -} - -/* - * May be called with null cx by js_GetStringChars, above; and by the jslock.c - * MAKE_STRING_IMMUTABLE file-local macro. - */ -const jschar * -js_UndependString(JSContext *cx, JSString *str) -{ - size_t n, size; - jschar *s; - - if (JSSTRING_IS_DEPENDENT(str)) { - n = JSSTRDEP_LENGTH(str); - size = (n + 1) * sizeof(jschar); - s = (jschar *) (cx ? JS_malloc(cx, size) : malloc(size)); - if (!s) - return NULL; - - js_strncpy(s, JSSTRDEP_CHARS(str), n); - s[n] = 0; - str->length = n; - str->chars = s; - -#ifdef DEBUG - if (cx) { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_UNMETER(rt, liveDependentStrings); - JS_RUNTIME_UNMETER(rt, totalDependentStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum -= (double)n, - rt->strdepLengthSquaredSum -= (double)n * (double)n)); - } -#endif - } - - return str->chars; -} - -/* - * Forward declarations for URI encode/decode and helper routines - */ -static JSBool -str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static JSBool -str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -static uint32 -Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length); - -/* - * Contributions from the String class to the set of methods defined for the - * global object. escape and unescape used to be defined in the Mocha library, - * but as ECMA decided to spec them, they've been moved to the core engine - * and made ECMA-compliant. (Incomplete escapes are interpreted as literal - * characters by unescape.) - */ - -/* - * Stuff to emulate the old libmocha escape, which took a second argument - * giving the type of escape to perform. Retained for compatibility, and - * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes. - */ - -#define URL_XALPHAS ((uint8) 1) -#define URL_XPALPHAS ((uint8) 2) -#define URL_PATH ((uint8) 4) - -static const uint8 urlCharType[256] = -/* Bit 0 xalpha -- the alphas - * Bit 1 xpalpha -- as xalpha but - * converts spaces to plus and plus to %20 - * Bit 2 ... path -- as xalphas but doesn't escape '/' - */ - /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ - { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */ - 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */ - 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */ - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */ - 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */ - 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */ - 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */ - 0, }; - -/* This matches the ECMA escape set when mask is 7 (default.) */ - -#define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask)) - -/* See ECMA-262 15.1.2.4. */ -JSBool -js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - size_t i, ni, length, newlength; - const jschar *chars; - jschar *newchars; - jschar ch; - jsint mask; - jsdouble d; - const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - - mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH; - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - if (!JSDOUBLE_IS_FINITE(d) || - (mask = (jsint)d) != d || - mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH)) - { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_STRING_MASK, numBuf); - return JS_FALSE; - } - } - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - chars = JSSTRING_CHARS(str); - length = newlength = JSSTRING_LENGTH(str); - - /* Take a first pass and see how big the result string will need to be. */ - for (i = 0; i < length; i++) { - if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) - continue; - if (ch < 256) { - if (mask == URL_XPALPHAS && ch == ' ') - continue; /* The character will be encoded as '+' */ - newlength += 2; /* The character will be encoded as %XX */ - } else { - newlength += 5; /* The character will be encoded as %uXXXX */ - } - - /* - * This overflow test works because newlength is incremented by at - * most 5 on each iteration. - */ - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - - if (newlength >= ~(size_t)0 / sizeof(jschar)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!newchars) - return JS_FALSE; - for (i = 0, ni = 0; i < length; i++) { - if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) { - newchars[ni++] = ch; - } else if (ch < 256) { - if (mask == URL_XPALPHAS && ch == ' ') { - newchars[ni++] = '+'; /* convert spaces to pluses */ - } else { - newchars[ni++] = '%'; - newchars[ni++] = digits[ch >> 4]; - newchars[ni++] = digits[ch & 0xF]; - } - } else { - newchars[ni++] = '%'; - newchars[ni++] = 'u'; - newchars[ni++] = digits[ch >> 12]; - newchars[ni++] = digits[(ch & 0xF00) >> 8]; - newchars[ni++] = digits[(ch & 0xF0) >> 4]; - newchars[ni++] = digits[ch & 0xF]; - } - } - JS_ASSERT(ni == newlength); - newchars[newlength] = 0; - - str = js_NewString(cx, newchars, newlength, 0); - if (!str) { - JS_free(cx, newchars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#undef IS_OK - -/* See ECMA-262 15.1.2.5 */ -static JSBool -str_unescape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - size_t i, ni, length; - const jschar *chars; - jschar *newchars; - jschar ch; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - - chars = JSSTRING_CHARS(str); - length = JSSTRING_LENGTH(str); - - /* Don't bother allocating less space for the new string. */ - newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!newchars) - return JS_FALSE; - ni = i = 0; - while (i < length) { - ch = chars[i++]; - if (ch == '%') { - if (i + 1 < length && - JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1])) - { - ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]); - i += 2; - } else if (i + 4 < length && chars[i] == 'u' && - JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) && - JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4])) - { - ch = (((((JS7_UNHEX(chars[i + 1]) << 4) - + JS7_UNHEX(chars[i + 2])) << 4) - + JS7_UNHEX(chars[i + 3])) << 4) - + JS7_UNHEX(chars[i + 4]); - i += 5; - } - } - newchars[ni++] = ch; - } - newchars[ni] = 0; - - str = js_NewString(cx, newchars, ni, 0); - if (!str) { - JS_free(cx, newchars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#if JS_HAS_UNEVAL -static JSBool -str_uneval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - str = js_ValueToSource(cx, argv[0]); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif - -const char js_escape_str[] = "escape"; -const char js_unescape_str[] = "unescape"; -#if JS_HAS_UNEVAL -const char js_uneval_str[] = "uneval"; -#endif -const char js_decodeURI_str[] = "decodeURI"; -const char js_encodeURI_str[] = "encodeURI"; -const char js_decodeURIComponent_str[] = "decodeURIComponent"; -const char js_encodeURIComponent_str[] = "encodeURIComponent"; - -static JSFunctionSpec string_functions[] = { - {js_escape_str, js_str_escape, 1,0,0}, - {js_unescape_str, str_unescape, 1,0,0}, -#if JS_HAS_UNEVAL - {js_uneval_str, str_uneval, 1,0,0}, -#endif - {js_decodeURI_str, str_decodeURI, 1,0,0}, - {js_encodeURI_str, str_encodeURI, 1,0,0}, - {js_decodeURIComponent_str, str_decodeURI_Component, 1,0,0}, - {js_encodeURIComponent_str, str_encodeURI_Component, 1,0,0}, - - {0,0,0,0,0} -}; - -jschar js_empty_ucstr[] = {0}; -JSSubString js_EmptySubString = {0, js_empty_ucstr}; - -enum string_tinyid { - STRING_LENGTH = -1 -}; - -static JSPropertySpec string_props[] = { - {js_length_str, STRING_LENGTH, - JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0}, - {0,0,0,0,0} -}; - -static JSBool -str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsval v; - JSString *str; - jsint slot; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - slot = JSVAL_TO_INT(id); - if (slot == STRING_LENGTH) { - if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) { - /* Follow ECMA-262 by fetching intrinsic length of our string. */ - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - } else { - /* Preserve compatibility: convert obj to a string primitive. */ - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - } - - *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str)); - } - return JS_TRUE; -} - -#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT) - -static JSBool -str_enumerate(JSContext *cx, JSObject *obj) -{ - jsval v; - JSString *str, *str1; - size_t i, length; - - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - - length = JSSTRING_LENGTH(str); - for (i = 0; i < length; i++) { - str1 = js_NewDependentString(cx, str, i, 1, 0); - if (!str1) - return JS_FALSE; - if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i), - STRING_TO_JSVAL(str1), NULL, NULL, - STRING_ELEMENT_ATTRS, NULL)) { - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, - JSObject **objp) -{ - jsval v; - JSString *str, *str1; - jsint slot; - - if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING)) - return JS_TRUE; - - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - JS_ASSERT(JSVAL_IS_STRING(v)); - str = JSVAL_TO_STRING(v); - - slot = JSVAL_TO_INT(id); - if ((size_t)slot < JSSTRING_LENGTH(str)) { - str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0); - if (!str1) - return JS_FALSE; - if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot), - STRING_TO_JSVAL(str1), NULL, NULL, - STRING_ELEMENT_ATTRS, NULL)) { - return JS_FALSE; - } - *objp = obj; - } - return JS_TRUE; -} - -JSClass js_StringClass = { - js_String_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | - JSCLASS_HAS_CACHED_PROTO(JSProto_String), - JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub, - str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -#if JS_HAS_TOSOURCE - -/* - * String.prototype.quote is generic (as are most string methods), unlike - * toSource, toString, and valueOf. - */ -static JSBool -str_quote(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - str = js_QuoteString(cx, str, '"'); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSString *str; - size_t i, j, k, n; - char buf[16]; - jschar *s, *t; - - if (JSVAL_IS_STRING((jsval)obj)) { - v = (jsval)obj; - } else { - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_STRING(v)) - return js_obj_toSource(cx, obj, argc, argv, rval); - } - str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); - if (!str) - return JS_FALSE; - j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name); - s = JSSTRING_CHARS(str); - k = JSSTRING_LENGTH(str); - n = j + k + 2; - t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!t) - return JS_FALSE; - for (i = 0; i < j; i++) - t[i] = buf[i]; - for (j = 0; j < k; i++, j++) - t[i] = s[j]; - t[i++] = ')'; - t[i++] = ')'; - t[i] = 0; - str = js_NewString(cx, t, n, 0); - if (!str) { - JS_free(cx, t); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#endif /* JS_HAS_TOSOURCE */ - -static JSBool -str_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - - if (JSVAL_IS_STRING((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - if (!JSVAL_IS_STRING(v)) - return js_obj_toString(cx, obj, argc, argv, rval); - *rval = v; - return JS_TRUE; -} - -static JSBool -str_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - if (JSVAL_IS_STRING((jsval)obj)) { - *rval = (jsval)obj; - return JS_TRUE; - } - if (!JS_InstanceOf(cx, obj, &js_StringClass, argv)) - return JS_FALSE; - *rval = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE); - return JS_TRUE; -} - -/* - * Java-like string native methods. - */ -static JSBool -str_substring(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - jsdouble d; - jsdouble length, begin, end; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - length = JSSTRING_LENGTH(str); - begin = js_DoubleToInteger(d); - if (begin < 0) - begin = 0; - else if (begin > length) - begin = length; - - if (argc == 1) { - end = length; - } else { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) - end = 0; - else if (end > length) - end = length; - if (end < begin) { - /* ECMA emulates old JDK1.0 java.lang.String.substring. */ - jsdouble tmp = begin; - begin = end; - end = tmp; - } - } - - str = js_NewDependentString(cx, str, (size_t)begin, - (size_t)(end - begin), 0); - if (!str) - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - size_t i, n; - jschar *s, *news; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - n = JSSTRING_LENGTH(str); - news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!news) - return JS_FALSE; - s = JSSTRING_CHARS(str); - for (i = 0; i < n; i++) - news[i] = JS_TOLOWER(s[i]); - news[n] = 0; - str = js_NewString(cx, news, n, 0); - if (!str) { - JS_free(cx, news); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toLocaleLowerCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - /* - * Forcefully ignore the first (or any) argument and return toLowerCase(), - * ECMA has reserved that argument, presumably for defining the locale. - */ - if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - return cx->localeCallbacks->localeToLowerCase(cx, str, rval); - } - return str_toLowerCase(cx, obj, 0, argv, rval); -} - -static JSBool -str_toUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - size_t i, n; - jschar *s, *news; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - n = JSSTRING_LENGTH(str); - news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!news) - return JS_FALSE; - s = JSSTRING_CHARS(str); - for (i = 0; i < n; i++) - news[i] = JS_TOUPPER(s[i]); - news[n] = 0; - str = js_NewString(cx, news, n, 0); - if (!str) { - JS_free(cx, news); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_toLocaleUpperCase(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - /* - * Forcefully ignore the first (or any) argument and return toUpperCase(), - * ECMA has reserved that argument, presumbaly for defining the locale. - */ - if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - return cx->localeCallbacks->localeToUpperCase(cx, str, rval); - } - return str_toUpperCase(cx, obj, 0, argv, rval); -} - -static JSBool -str_localeCompare(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str, *thatStr; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc == 0) { - *rval = JSVAL_ZERO; - } else { - thatStr = js_ValueToString(cx, argv[0]); - if (!thatStr) - return JS_FALSE; - if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) { - argv[0] = STRING_TO_JSVAL(thatStr); - return cx->localeCallbacks->localeCompare(cx, str, thatStr, rval); - } - *rval = INT_TO_JSVAL(js_CompareStrings(str, thatStr)); - } - return JS_TRUE; -} - -static JSBool -str_charAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - size_t index; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc == 0) { - d = 0.0; - } else { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - } - - if (d < 0 || JSSTRING_LENGTH(str) <= d) { - *rval = JS_GetEmptyStringValue(cx); - } else { - index = (size_t)d; - str = js_NewDependentString(cx, str, index, 1, 0); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - } - return JS_TRUE; -} - -static JSBool -str_charCodeAt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - jsdouble d; - size_t index; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc == 0) { - d = 0.0; - } else { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - } - - if (d < 0 || JSSTRING_LENGTH(str) <= d) { - *rval = JS_GetNaNValue(cx); - } else { - index = (size_t)d; - *rval = INT_TO_JSVAL((jsint) JSSTRING_CHARS(str)[index]); - } - return JS_TRUE; -} - -jsint -js_BoyerMooreHorspool(const jschar *text, jsint textlen, - const jschar *pat, jsint patlen, - jsint start) -{ - jsint i, j, k, m; - uint8 skip[BMH_CHARSET_SIZE]; - jschar c; - - JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX); - for (i = 0; i < BMH_CHARSET_SIZE; i++) - skip[i] = (uint8)patlen; - m = patlen - 1; - for (i = 0; i < m; i++) { - c = pat[i]; - if (c >= BMH_CHARSET_SIZE) - return BMH_BAD_PATTERN; - skip[c] = (uint8)(m - i); - } - for (k = start + m; - k < textlen; - k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) { - for (i = k, j = m; ; i--, j--) { - if (j < 0) - return i + 1; - if (text[i] != pat[j]) - break; - } - } - return -1; -} - -static JSBool -str_indexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str, *str2; - jsint i, j, index, textlen, patlen; - const jschar *text, *pat; - jsdouble d; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - text = JSSTRING_CHARS(str); - textlen = (jsint) JSSTRING_LENGTH(str); - - str2 = js_ValueToString(cx, argv[0]); - if (!str2) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str2); - pat = JSSTRING_CHARS(str2); - patlen = (jsint) JSSTRING_LENGTH(str2); - - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - d = js_DoubleToInteger(d); - if (d < 0) - i = 0; - else if (d > textlen) - i = textlen; - else - i = (jsint)d; - } else { - i = 0; - } - if (patlen == 0) { - *rval = INT_TO_JSVAL(i); - return JS_TRUE; - } - - /* XXX tune the BMH threshold (512) */ - if ((jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2 && textlen >= 512) { - index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i); - if (index != BMH_BAD_PATTERN) - goto out; - } - - index = -1; - j = 0; - while (i + j < textlen) { - if (text[i + j] == pat[j]) { - if (++j == patlen) { - index = i; - break; - } - } else { - i++; - j = 0; - } - } - -out: - *rval = INT_TO_JSVAL(index); - return JS_TRUE; -} - -static JSBool -str_lastIndexOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str, *str2; - const jschar *text, *pat; - jsint i, j, textlen, patlen; - jsdouble d; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - text = JSSTRING_CHARS(str); - textlen = (jsint) JSSTRING_LENGTH(str); - - str2 = js_ValueToString(cx, argv[0]); - if (!str2) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str2); - pat = JSSTRING_CHARS(str2); - patlen = (jsint) JSSTRING_LENGTH(str2); - - if (argc > 1) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - if (JSDOUBLE_IS_NaN(d)) { - i = textlen; - } else { - d = js_DoubleToInteger(d); - if (d < 0) - i = 0; - else if (d > textlen) - i = textlen; - else - i = (jsint)d; - } - } else { - i = textlen; - } - - if (patlen == 0) { - *rval = INT_TO_JSVAL(i); - return JS_TRUE; - } - - j = 0; - while (i >= 0) { - /* Don't assume that text is NUL-terminated: it could be dependent. */ - if (i + j < textlen && text[i + j] == pat[j]) { - if (++j == patlen) - break; - } else { - i--; - j = 0; - } - } - *rval = INT_TO_JSVAL(i); - return JS_TRUE; -} - -/* - * Perl-inspired string functions. - */ -typedef struct GlobData { - uintN flags; /* inout: mode and flag bits, see below */ - uintN optarg; /* in: index of optional flags argument */ - JSString *str; /* out: 'this' parameter object as string */ - JSRegExp *regexp; /* out: regexp parameter object private data */ -} GlobData; - -/* - * Mode and flag bit definitions for match_or_replace's GlobData.flags field. - */ -#define MODE_MATCH 0x00 /* in: return match array on success */ -#define MODE_REPLACE 0x01 /* in: match and replace */ -#define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */ -#define GET_MODE(f) ((f) & 0x03) -#define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */ -#define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller - of match_or_replace; if set on input - but clear on output, regexp ownership - does not pass to caller */ -#define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */ - -static JSBool -match_or_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - JSBool (*glob)(JSContext *cx, jsint count, GlobData *data), - GlobData *data, jsval *rval) -{ - JSString *str, *src, *opt; - JSObject *reobj; - JSRegExp *re; - size_t index, length; - JSBool ok, test; - jsint count; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - data->str = str; - - if (JSVAL_IS_REGEXP(cx, argv[0])) { - reobj = JSVAL_TO_OBJECT(argv[0]); - re = (JSRegExp *) JS_GetPrivate(cx, reobj); - } else { - src = js_ValueToString(cx, argv[0]); - if (!src) - return JS_FALSE; - if (data->optarg < argc) { - argv[0] = STRING_TO_JSVAL(src); - opt = js_ValueToString(cx, argv[data->optarg]); - if (!opt) - return JS_FALSE; - } else { - opt = NULL; - } - re = js_NewRegExpOpt(cx, NULL, src, opt, - (data->flags & FORCE_FLAT) != 0); - if (!re) - return JS_FALSE; - reobj = NULL; - } - /* From here on, all control flow must reach the matching DROP. */ - data->regexp = re; - HOLD_REGEXP(cx, re); - - if (re->flags & JSREG_GLOB) - data->flags |= GLOBAL_REGEXP; - index = 0; - if (GET_MODE(data->flags) == MODE_SEARCH) { - ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); - if (ok) { - *rval = (*rval == JSVAL_TRUE) - ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length) - : INT_TO_JSVAL(-1); - } - } else if (data->flags & GLOBAL_REGEXP) { - if (reobj) { - /* Set the lastIndex property's reserved slot to 0. */ - ok = js_SetLastIndex(cx, reobj, 0); - } else { - ok = JS_TRUE; - } - if (ok) { - length = JSSTRING_LENGTH(str); - for (count = 0; index <= length; count++) { - ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, rval); - if (!ok || *rval != JSVAL_TRUE) - break; - ok = glob(cx, count, data); - if (!ok) - break; - if (cx->regExpStatics.lastMatch.length == 0) { - if (index == length) - break; - index++; - } - } - } - } else { - if (GET_MODE(data->flags) == MODE_REPLACE) { - test = JS_TRUE; - } else { - /* - * MODE_MATCH implies str_match is being called from a script or a - * scripted function. If the caller cares only about testing null - * vs. non-null return value, optimize away the array object that - * would normally be returned in *rval. - */ - JSStackFrame *fp = cx->fp->down; - - /* Skip Function.prototype.call and .apply frames. */ - while (fp && !fp->pc) { - JS_ASSERT(!fp->script); - fp = fp->down; - } - - /* Assume a full array result is required, then prove otherwise. */ - test = JS_FALSE; - if (fp) { - JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW); - JS_ASSERT(js_CodeSpec[*fp->pc].length == 3); - switch (fp->pc[3]) { - case JSOP_POP: - case JSOP_IFEQ: - case JSOP_IFNE: - case JSOP_IFEQX: - case JSOP_IFNEX: - test = JS_TRUE; - break; - default:; - } - } - } - ok = js_ExecuteRegExp(cx, re, str, &index, test, rval); - } - - DROP_REGEXP(cx, re); - if (reobj) { - /* Tell our caller that it doesn't need to destroy data->regexp. */ - data->flags &= ~KEEP_REGEXP; - } else if (!(data->flags & KEEP_REGEXP)) { - /* Caller didn't want to keep data->regexp, so null and destroy it. */ - data->regexp = NULL; - js_DestroyRegExp(cx, re); - } - - return ok; -} - -typedef struct MatchData { - GlobData base; - jsval *arrayval; /* NB: local root pointer */ -} MatchData; - -static JSBool -match_glob(JSContext *cx, jsint count, GlobData *data) -{ - MatchData *mdata; - JSObject *arrayobj; - JSSubString *matchsub; - JSString *matchstr; - jsval v; - - mdata = (MatchData *)data; - arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval); - if (!arrayobj) { - arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj); - } - matchsub = &cx->regExpStatics.lastMatch; - matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length, 0); - if (!matchstr) - return JS_FALSE; - v = STRING_TO_JSVAL(matchstr); - return js_SetProperty(cx, arrayobj, INT_TO_JSID(count), &v); -} - -static JSBool -str_match(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - MatchData mdata; - JSBool ok; - - mdata.base.flags = MODE_MATCH; - mdata.base.optarg = 1; - mdata.arrayval = &argv[2]; - *mdata.arrayval = JSVAL_NULL; - ok = match_or_replace(cx, obj, argc, argv, match_glob, &mdata.base, rval); - if (ok && !JSVAL_IS_NULL(*mdata.arrayval)) - *rval = *mdata.arrayval; - return ok; -} - -static JSBool -str_search(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - GlobData data; - - data.flags = MODE_SEARCH; - data.optarg = 1; - return match_or_replace(cx, obj, argc, argv, NULL, &data, rval); -} - -typedef struct ReplaceData { - GlobData base; /* base struct state */ - JSObject *lambda; /* replacement function object or null */ - JSString *repstr; /* replacement string */ - jschar *dollar; /* null or pointer to first $ in repstr */ - jschar *dollarEnd; /* limit pointer for js_strchr_limit */ - jschar *chars; /* result chars, null initially */ - size_t length; /* result length, 0 initially */ - jsint index; /* index in result of next replacement */ - jsint leftIndex; /* left context index in base.str->chars */ - JSSubString dollarStr; /* for "$$" interpret_dollar result */ -} ReplaceData; - -static JSSubString * -interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata, - size_t *skip) -{ - JSRegExpStatics *res; - jschar dc, *cp; - uintN num, tmp; - - JS_ASSERT(*dp == '$'); - - /* If there is only a dollar, bail now */ - if (dp + 1 >= ep) - return NULL; - - /* Interpret all Perl match-induced dollar variables. */ - res = &cx->regExpStatics; - dc = dp[1]; - if (JS7_ISDEC(dc)) { - /* ECMA-262 Edition 3: 1-9 or 01-99 */ - num = JS7_UNDEC(dc); - if (num > res->parenCount) - return NULL; - - cp = dp + 2; - if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) { - tmp = 10 * num + JS7_UNDEC(dc); - if (tmp <= res->parenCount) { - cp++; - num = tmp; - } - } - if (num == 0) - return NULL; - - /* Adjust num from 1 $n-origin to 0 array-index-origin. */ - num--; - *skip = cp - dp; - return REGEXP_PAREN_SUBSTRING(res, num); - } - - *skip = 2; - switch (dc) { - case '$': - rdata->dollarStr.chars = dp; - rdata->dollarStr.length = 1; - return &rdata->dollarStr; - case '&': - return &res->lastMatch; - case '+': - return &res->lastParen; - case '`': - return &res->leftContext; - case '\'': - return &res->rightContext; - } - return NULL; -} - -static JSBool -find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep) -{ - JSString *repstr; - size_t replen, skip; - jschar *dp, *ep; - JSSubString *sub; - JSObject *lambda; - - lambda = rdata->lambda; - if (lambda) { - uintN argc, i, j, m, n, p; - jsval *sp, *oldsp, rval; - void *mark; - JSStackFrame *fp; - JSBool ok; - - /* - * Save the regExpStatics from the current regexp, since they may be - * clobbered by a RegExp usage in the lambda function. Note that all - * members of JSRegExpStatics are JSSubStrings, so not GC roots, save - * input, which is rooted otherwise via argv[-1] in str_replace. - */ - JSRegExpStatics save = cx->regExpStatics; - JSBool freeMoreParens = JS_FALSE; - - /* - * In the lambda case, not only do we find the replacement string's - * length, we compute repstr and return it via rdata for use within - * do_replace. The lambda is called with arguments ($&, $1, $2, ..., - * index, input), i.e., all the properties of a regexp match array. - * For $&, etc., we must create string jsvals from cx->regExpStatics. - * We grab up stack space to keep the newborn strings GC-rooted. - */ - p = rdata->base.regexp->parenCount; - argc = 1 + p + 2; - sp = js_AllocStack(cx, 2 + argc, &mark); - if (!sp) - return JS_FALSE; - - /* Push lambda and its 'this' parameter. */ - *sp++ = OBJECT_TO_JSVAL(lambda); - *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda)); - -#define PUSH_REGEXP_STATIC(sub) \ - JS_BEGIN_MACRO \ - JSString *str = js_NewStringCopyN(cx, \ - cx->regExpStatics.sub.chars, \ - cx->regExpStatics.sub.length, \ - 0); \ - if (!str) { \ - ok = JS_FALSE; \ - goto lambda_out; \ - } \ - *sp++ = STRING_TO_JSVAL(str); \ - JS_END_MACRO - - /* Push $&, $1, $2, ... */ - PUSH_REGEXP_STATIC(lastMatch); - i = 0; - m = cx->regExpStatics.parenCount; - n = JS_MIN(m, 9); - for (j = 0; i < n; i++, j++) - PUSH_REGEXP_STATIC(parens[j]); - for (j = 0; i < m; i++, j++) - PUSH_REGEXP_STATIC(moreParens[j]); - - /* - * We need to clear moreParens in the top-of-stack cx->regExpStatics - * to it won't be possibly realloc'ed, leaving the bottom-of-stack - * moreParens pointing to freed memory. - */ - cx->regExpStatics.moreParens = NULL; - freeMoreParens = JS_TRUE; - -#undef PUSH_REGEXP_STATIC - - /* Make sure to push undefined for any unmatched parens. */ - for (; i < p; i++) - *sp++ = JSVAL_VOID; - - /* Push match index and input string. */ - *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length); - *sp++ = STRING_TO_JSVAL(rdata->base.str); - - /* Lift current frame to include the args and do the call. */ - fp = cx->fp; - oldsp = fp->sp; - fp->sp = sp; - ok = js_Invoke(cx, argc, JSINVOKE_INTERNAL); - rval = fp->sp[-1]; - fp->sp = oldsp; - - if (ok) { - /* - * NB: we count on the newborn string root to hold any string - * created by this js_ValueToString that would otherwise be GC- - * able, until we use rdata->repstr in do_replace. - */ - repstr = js_ValueToString(cx, rval); - if (!repstr) { - ok = JS_FALSE; - } else { - rdata->repstr = repstr; - *sizep = JSSTRING_LENGTH(repstr); - } - } - - lambda_out: - js_FreeStack(cx, mark); - if (freeMoreParens) - JS_free(cx, cx->regExpStatics.moreParens); - cx->regExpStatics = save; - return ok; - } - - repstr = rdata->repstr; - replen = JSSTRING_LENGTH(repstr); - for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; - dp = js_strchr_limit(dp, '$', ep)) { - sub = interpret_dollar(cx, dp, ep, rdata, &skip); - if (sub) { - replen += sub->length - skip; - dp += skip; - } - else - dp++; - } - *sizep = replen; - return JS_TRUE; -} - -static void -do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars) -{ - JSString *repstr; - jschar *bp, *cp, *dp, *ep; - size_t len, skip; - JSSubString *sub; - - repstr = rdata->repstr; - bp = cp = JSSTRING_CHARS(repstr); - for (dp = rdata->dollar, ep = rdata->dollarEnd; dp; - dp = js_strchr_limit(dp, '$', ep)) { - len = dp - cp; - js_strncpy(chars, cp, len); - chars += len; - cp = dp; - sub = interpret_dollar(cx, dp, ep, rdata, &skip); - if (sub) { - len = sub->length; - js_strncpy(chars, sub->chars, len); - chars += len; - cp += skip; - dp += skip; - } else { - dp++; - } - } - js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (cp - bp)); -} - -static JSBool -replace_glob(JSContext *cx, jsint count, GlobData *data) -{ - ReplaceData *rdata; - JSString *str; - size_t leftoff, leftlen, replen, growth; - const jschar *left; - jschar *chars; - - rdata = (ReplaceData *)data; - str = data->str; - leftoff = rdata->leftIndex; - left = JSSTRING_CHARS(str) + leftoff; - leftlen = cx->regExpStatics.lastMatch.chars - left; - rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str); - rdata->leftIndex += cx->regExpStatics.lastMatch.length; - if (!find_replen(cx, rdata, &replen)) - return JS_FALSE; - growth = leftlen + replen; - chars = (jschar *) - (rdata->chars - ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1) - * sizeof(jschar)) - : JS_malloc(cx, (growth + 1) * sizeof(jschar))); - if (!chars) { - JS_free(cx, rdata->chars); - rdata->chars = NULL; - return JS_FALSE; - } - rdata->chars = chars; - rdata->length += growth; - chars += rdata->index; - rdata->index += growth; - js_strncpy(chars, left, leftlen); - chars += leftlen; - do_replace(cx, rdata, chars); - return JS_TRUE; -} - -static JSBool -str_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *lambda; - JSString *repstr, *str; - ReplaceData rdata; - JSBool ok; - jschar *chars; - size_t leftlen, rightlen, length; - - if (JS_TypeOfValue(cx, argv[1]) == JSTYPE_FUNCTION) { - lambda = JSVAL_TO_OBJECT(argv[1]); - repstr = NULL; - } else { - if (!JS_ConvertValue(cx, argv[1], JSTYPE_STRING, &argv[1])) - return JS_FALSE; - repstr = JSVAL_TO_STRING(argv[1]); - lambda = NULL; - } - - /* - * For ECMA Edition 3, the first argument is to be converted to a string - * to match in a "flat" sense (without regular expression metachars having - * special meanings) UNLESS the first arg is a RegExp object. - */ - rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT; - rdata.base.optarg = 2; - - rdata.lambda = lambda; - rdata.repstr = repstr; - if (repstr) { - rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr); - rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$', - rdata.dollarEnd); - } else { - rdata.dollar = rdata.dollarEnd = NULL; - } - rdata.chars = NULL; - rdata.length = 0; - rdata.index = 0; - rdata.leftIndex = 0; - - ok = match_or_replace(cx, obj, argc, argv, replace_glob, &rdata.base, rval); - if (!ok) - return JS_FALSE; - - if (!rdata.chars) { - if ((rdata.base.flags & GLOBAL_REGEXP) || *rval != JSVAL_TRUE) { - /* Didn't match even once. */ - *rval = STRING_TO_JSVAL(rdata.base.str); - goto out; - } - leftlen = cx->regExpStatics.leftContext.length; - ok = find_replen(cx, &rdata, &length); - if (!ok) - goto out; - length += leftlen; - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) { - ok = JS_FALSE; - goto out; - } - js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen); - do_replace(cx, &rdata, chars + leftlen); - rdata.chars = chars; - rdata.length = length; - } - - rightlen = cx->regExpStatics.rightContext.length; - length = rdata.length + rightlen; - chars = (jschar *) - JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar)); - if (!chars) { - JS_free(cx, rdata.chars); - ok = JS_FALSE; - goto out; - } - js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars, - rightlen); - chars[length] = 0; - - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - ok = JS_FALSE; - goto out; - } - *rval = STRING_TO_JSVAL(str); - -out: - /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */ - if (rdata.base.flags & KEEP_REGEXP) - js_DestroyRegExp(cx, rdata.base.regexp); - return ok; -} - -/* - * Subroutine used by str_split to find the next split point in str, starting - * at offset *ip and looking either for the separator substring given by sep, - * or for the next re match. In the re case, return the matched separator in - * *sep, and the possibly updated offset in *ip. - * - * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next - * separator occurrence if found, or str->length if no separator is found. - */ -static jsint -find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip, - JSSubString *sep) -{ - jsint i, j, k; - size_t length; - jschar *chars; - - /* - * Stop if past end of string. If at end of string, we will compare the - * null char stored there (by js_NewString*) to sep->chars[j] in the while - * loop at the end of this function, so that - * - * "ab,".split(',') => ["ab", ""] - * - * and the resulting array converts back to the string "ab," for symmetry. - * However, we ape Perl and do this only if there is a sufficiently large - * limit argument (see str_split). - */ - i = *ip; - length = JSSTRING_LENGTH(str); - if ((size_t)i > length) - return -1; - - chars = JSSTRING_CHARS(str); - - /* - * Match a regular expression against the separator at or above index i. - * Call js_ExecuteRegExp with true for the test argument. On successful - * match, get the separator from cx->regExpStatics.lastMatch. - */ - if (re) { - size_t index; - jsval rval; - - again: - /* JS1.2 deviated from Perl by never matching at end of string. */ - index = (size_t)i; - if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval)) - return -2; - if (rval != JSVAL_TRUE) { - /* Mismatch: ensure our caller advances i past end of string. */ - sep->length = 1; - return length; - } - i = (jsint)index; - *sep = cx->regExpStatics.lastMatch; - if (sep->length == 0) { - /* - * Empty string match: never split on an empty match at the start - * of a find_split cycle. Same rule as for an empty global match - * in match_or_replace. - */ - if (i == *ip) { - /* - * "Bump-along" to avoid sticking at an empty match, but don't - * bump past end of string -- our caller must do that by adding - * sep->length to our return value. - */ - if ((size_t)i == length) - return -1; - i++; - goto again; - } - if ((size_t)i == length) { - /* - * If there was a trivial zero-length match at the end of the - * split, then we shouldn't output the matched string at the end - * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15. - */ - sep->chars = NULL; - } - } - JS_ASSERT((size_t)i >= sep->length); - return i - sep->length; - } - - /* - * Deviate from ECMA by never splitting an empty string by any separator - * string into a non-empty array (an array of length 1 that contains the - * empty string). - */ - if (!JS_VERSION_IS_ECMA(cx) && length == 0) - return -1; - - /* - * Special case: if sep is the empty string, split str into one character - * substrings. Let our caller worry about whether to split once at end of - * string into an empty substring. - */ - if (sep->length == 0) - return ((size_t)i == length) ? -1 : i + 1; - - /* - * Now that we know sep is non-empty, search starting at i in str for an - * occurrence of all of sep's chars. If we find them, return the index of - * the first separator char. Otherwise, return length. - */ - j = 0; - while ((size_t)(k = i + j) < length) { - if (chars[k] == sep->chars[j]) { - if ((size_t)++j == sep->length) - return i; - } else { - i++; - j = 0; - } - } - return k; -} - -static JSBool -str_split(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str, *sub; - JSObject *arrayobj; - jsval v; - JSBool ok, limited; - JSRegExp *re; - JSSubString *sep, tmp; - jsdouble d; - jsint i, j; - uint32 len, limit; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(arrayobj); - - if (argc == 0) { - v = STRING_TO_JSVAL(str); - ok = JS_SetElement(cx, arrayobj, 0, &v); - } else { - if (JSVAL_IS_REGEXP(cx, argv[0])) { - re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0])); - sep = &tmp; - - /* Set a magic value so we can detect a successful re match. */ - sep->chars = NULL; - sep->length = 0; - } else { - JSString *str2 = js_ValueToString(cx, argv[0]); - if (!str2) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str2); - - /* - * Point sep at a local copy of str2's header because find_split - * will modify sep->length. - */ - tmp.length = JSSTRING_LENGTH(str2); - tmp.chars = JSSTRING_CHARS(str2); - sep = &tmp; - re = NULL; - } - - /* Use the second argument as the split limit, if given. */ - limited = (argc > 1) && !JSVAL_IS_VOID(argv[1]); - limit = 0; /* Avoid warning. */ - if (limited) { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - - /* Clamp limit between 0 and 1 + string length. */ - if (!js_DoubleToECMAUint32(cx, d, &limit)) - return JS_FALSE; - if (limit > JSSTRING_LENGTH(str)) - limit = 1 + JSSTRING_LENGTH(str); - } - - len = i = 0; - while ((j = find_split(cx, str, re, &i, sep)) >= 0) { - if (limited && len >= limit) - break; - sub = js_NewDependentString(cx, str, i, (size_t)(j - i), 0); - if (!sub) - return JS_FALSE; - v = STRING_TO_JSVAL(sub); - if (!JS_SetElement(cx, arrayobj, len, &v)) - return JS_FALSE; - len++; - - /* - * Imitate perl's feature of including parenthesized substrings - * that matched part of the delimiter in the new array, after the - * split substring that was delimited. - */ - if (re && sep->chars) { - uintN num; - JSSubString *parsub; - - for (num = 0; num < cx->regExpStatics.parenCount; num++) { - if (limited && len >= limit) - break; - parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num); - sub = js_NewStringCopyN(cx, parsub->chars, parsub->length, - 0); - if (!sub) - return JS_FALSE; - v = STRING_TO_JSVAL(sub); - if (!JS_SetElement(cx, arrayobj, len, &v)) - return JS_FALSE; - len++; - } - sep->chars = NULL; - } - - i = j + sep->length; - if (!JS_VERSION_IS_ECMA(cx)) { - /* - * Deviate from ECMA to imitate Perl, which omits a final - * split unless a limit argument is given and big enough. - */ - if (!limited && (size_t)i == JSSTRING_LENGTH(str)) - break; - } - } - ok = (j != -2); - } - return ok; -} - -#if JS_HAS_PERL_SUBSTR -static JSBool -str_substr(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - jsdouble length, begin, end; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - length = JSSTRING_LENGTH(str); - begin = js_DoubleToInteger(d); - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; - } - - if (argc == 1) { - end = length; - } else { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) - end = 0; - end += begin; - if (end > length) - end = length; - } - - str = js_NewDependentString(cx, str, (size_t)begin, - (size_t)(end - begin), 0); - if (!str) - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} -#endif /* JS_HAS_PERL_SUBSTR */ - -/* - * Python-esque sequence operations. - */ -static JSBool -str_concat(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str, *str2; - uintN i; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - for (i = 0; i < argc; i++) { - str2 = js_ValueToString(cx, argv[i]); - if (!str2) - return JS_FALSE; - argv[i] = STRING_TO_JSVAL(str2); - - str = js_ConcatStrings(cx, str, str2); - if (!str) - return JS_FALSE; - } - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -str_slice(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - jsdouble d; - jsdouble length, begin, end; - - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - - if (argc != 0) { - if (!js_ValueToNumber(cx, argv[0], &d)) - return JS_FALSE; - length = JSSTRING_LENGTH(str); - begin = js_DoubleToInteger(d); - if (begin < 0) { - begin += length; - if (begin < 0) - begin = 0; - } else if (begin > length) { - begin = length; - } - - if (argc == 1) { - end = length; - } else { - if (!js_ValueToNumber(cx, argv[1], &d)) - return JS_FALSE; - end = js_DoubleToInteger(d); - if (end < 0) { - end += length; - if (end < 0) - end = 0; - } else if (end > length) { - end = length; - } - if (end < begin) - end = begin; - } - - str = js_NewDependentString(cx, str, (size_t)begin, - (size_t)(end - begin), 0); - if (!str) - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -#if JS_HAS_STR_HTML_HELPERS -/* - * HTML composition aids. - */ -static JSBool -tagify(JSContext *cx, JSObject *obj, jsval *argv, - const char *begin, JSString *param, const char *end, - jsval *rval) -{ - JSString *str; - jschar *tagbuf; - size_t beglen, endlen, parlen, taglen; - size_t i, j; - - if (JSVAL_IS_STRING((jsval)obj)) { - str = JSVAL_TO_STRING((jsval)obj); - } else { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - argv[-1] = STRING_TO_JSVAL(str); - } - - if (!end) - end = begin; - - beglen = strlen(begin); - taglen = 1 + beglen + 1; /* '' */ - parlen = 0; /* Avoid warning. */ - if (param) { - parlen = JSSTRING_LENGTH(param); - taglen += 2 + parlen + 1; /* '="param"' */ - } - endlen = strlen(end); - taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1; /* 'str' */ - - if (taglen >= ~(size_t)0 / sizeof(jschar)) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar)); - if (!tagbuf) - return JS_FALSE; - - j = 0; - tagbuf[j++] = '<'; - for (i = 0; i < beglen; i++) - tagbuf[j++] = (jschar)begin[i]; - if (param) { - tagbuf[j++] = '='; - tagbuf[j++] = '"'; - js_strncpy(&tagbuf[j], JSSTRING_CHARS(param), parlen); - j += parlen; - tagbuf[j++] = '"'; - } - tagbuf[j++] = '>'; - js_strncpy(&tagbuf[j], JSSTRING_CHARS(str), JSSTRING_LENGTH(str)); - j += JSSTRING_LENGTH(str); - tagbuf[j++] = '<'; - tagbuf[j++] = '/'; - for (i = 0; i < endlen; i++) - tagbuf[j++] = (jschar)end[i]; - tagbuf[j++] = '>'; - JS_ASSERT(j == taglen); - tagbuf[j] = 0; - - str = js_NewString(cx, tagbuf, taglen, 0); - if (!str) { - free((char *)tagbuf); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -tagify_value(JSContext *cx, JSObject *obj, jsval *argv, - const char *begin, const char *end, - jsval *rval) -{ - JSString *param; - - param = js_ValueToString(cx, argv[0]); - if (!param) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(param); - return tagify(cx, obj, argv, begin, param, end, rval); -} - -static JSBool -str_bold(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "b", NULL, NULL, rval); -} - -static JSBool -str_italics(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "i", NULL, NULL, rval); -} - -static JSBool -str_fixed(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "tt", NULL, NULL, rval); -} - -static JSBool -str_fontsize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify_value(cx, obj, argv, "font size", "font", rval); -} - -static JSBool -str_fontcolor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - return tagify_value(cx, obj, argv, "font color", "font", rval); -} - -static JSBool -str_link(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify_value(cx, obj, argv, "a href", "a", rval); -} - -static JSBool -str_anchor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify_value(cx, obj, argv, "a name", "a", rval); -} - -static JSBool -str_strike(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "strike", NULL, NULL, rval); -} - -static JSBool -str_small(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "small", NULL, NULL, rval); -} - -static JSBool -str_big(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "big", NULL, NULL, rval); -} - -static JSBool -str_blink(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "blink", NULL, NULL, rval); -} - -static JSBool -str_sup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "sup", NULL, NULL, rval); -} - -static JSBool -str_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - return tagify(cx, obj, argv, "sub", NULL, NULL, rval); -} -#endif /* JS_HAS_STR_HTML_HELPERS */ - -static JSFunctionSpec string_methods[] = { -#if JS_HAS_TOSOURCE - {"quote", str_quote, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {js_toSource_str, str_toSource, 0,JSFUN_THISP_STRING,0}, -#endif - - /* Java-like methods. */ - {js_toString_str, str_toString, 0,JSFUN_THISP_STRING,0}, - {js_valueOf_str, str_valueOf, 0,JSFUN_THISP_STRING,0}, - {"substring", str_substring, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLowerCase", str_toLowerCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toUpperCase", str_toUpperCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"charAt", str_charAt, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"charCodeAt", str_charCodeAt, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"indexOf", str_indexOf, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"lastIndexOf", str_lastIndexOf, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLocaleLowerCase", str_toLocaleLowerCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"toLocaleUpperCase", str_toLocaleUpperCase, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"localeCompare", str_localeCompare, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - - /* Perl-ish methods (search is actually Python-esque). */ - {"match", str_match, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,2}, - {"search", str_search, 1,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"replace", str_replace, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"split", str_split, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, -#if JS_HAS_PERL_SUBSTR - {"substr", str_substr, 2,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, -#endif - - /* Python-esque sequence methods. */ - {"concat", str_concat, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - {"slice", str_slice, 0,JSFUN_GENERIC_NATIVE| - JSFUN_THISP_PRIMITIVE,0}, - - /* HTML string methods. */ -#if JS_HAS_STR_HTML_HELPERS - {"bold", str_bold, 0,JSFUN_THISP_PRIMITIVE,0}, - {"italics", str_italics, 0,JSFUN_THISP_PRIMITIVE,0}, - {"fixed", str_fixed, 0,JSFUN_THISP_PRIMITIVE,0}, - {"fontsize", str_fontsize, 1,JSFUN_THISP_PRIMITIVE,0}, - {"fontcolor", str_fontcolor, 1,JSFUN_THISP_PRIMITIVE,0}, - {"link", str_link, 1,JSFUN_THISP_PRIMITIVE,0}, - {"anchor", str_anchor, 1,JSFUN_THISP_PRIMITIVE,0}, - {"strike", str_strike, 0,JSFUN_THISP_PRIMITIVE,0}, - {"small", str_small, 0,JSFUN_THISP_PRIMITIVE,0}, - {"big", str_big, 0,JSFUN_THISP_PRIMITIVE,0}, - {"blink", str_blink, 0,JSFUN_THISP_PRIMITIVE,0}, - {"sup", str_sup, 0,JSFUN_THISP_PRIMITIVE,0}, - {"sub", str_sub, 0,JSFUN_THISP_PRIMITIVE,0}, -#endif - - {0,0,0,0,0} -}; - -static JSBool -String(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSString *str; - - if (argc > 0) { - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - } else { - str = cx->runtime->emptyString; - } - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; - } - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); - return JS_TRUE; -} - -static JSBool -str_fromCharCode(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jschar *chars; - uintN i; - uint16 code; - JSString *str; - - JS_ASSERT(argc < ARRAY_INIT_LIMIT); - chars = (jschar *) JS_malloc(cx, (argc + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - for (i = 0; i < argc; i++) { - if (!js_ValueToUint16(cx, argv[i], &code)) { - JS_free(cx, chars); - return JS_FALSE; - } - chars[i] = (jschar)code; - } - chars[i] = 0; - str = js_NewString(cx, chars, argc, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSFunctionSpec string_static_methods[] = { - {"fromCharCode", str_fromCharCode, 1,0,0}, - {0,0,0,0,0} -}; - -JSBool -js_InitRuntimeStringState(JSContext *cx) -{ - JSRuntime *rt; - JSString *empty; - JSAtom *atom; - - rt = cx->runtime; - - /* Initialize string cache */ -#ifdef JS_THREADSAFE - JS_ASSERT(!rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = JS_NEW_LOCK(); - if (!rt->deflatedStringCacheLock) - return JS_FALSE; -#endif - - /* Make a permanently locked empty string. */ - JS_ASSERT(!rt->emptyString); - empty = js_NewStringCopyN(cx, js_empty_ucstr, 0, GCF_LOCK); - if (!empty) - goto bad; - - /* Atomize it for scripts that use '' + x to convert x to string. */ - atom = js_AtomizeString(cx, empty, ATOM_PINNED); - if (!atom) - goto bad; - - rt->emptyString = empty; - rt->atomState.emptyAtom = atom; - - return JS_TRUE; - - bad: -#ifdef JS_THREADSAFE - JS_DESTROY_LOCK(rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = NULL; -#endif - return JS_FALSE; - -} - -void -js_FinishRuntimeStringState(JSContext *cx) -{ - JSRuntime *rt = cx->runtime; - - js_UnlockGCThingRT(rt, rt->emptyString); - rt->emptyString = NULL; -} - -void -js_FinishDeflatedStringCache(JSRuntime *rt) -{ - if (rt->deflatedStringCache) { - JS_HashTableDestroy(rt->deflatedStringCache); - rt->deflatedStringCache = NULL; - } -#ifdef JS_THREADSAFE - if (rt->deflatedStringCacheLock) { - JS_DESTROY_LOCK(rt->deflatedStringCacheLock); - rt->deflatedStringCacheLock = NULL; - } -#endif -} - -JSObject * -js_InitStringClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto; - - /* Define the escape, unescape functions in the global object. */ - if (!JS_DefineFunctions(cx, obj, string_functions)) - return NULL; - - proto = JS_InitClass(cx, obj, NULL, &js_StringClass, String, 1, - string_props, string_methods, - NULL, string_static_methods); - if (!proto) - return NULL; - OBJ_SET_SLOT(cx, proto, JSSLOT_PRIVATE, - STRING_TO_JSVAL(cx->runtime->emptyString)); - return proto; -} - -JSString * -js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag) -{ - JSString *str; - - if (length > JSSTRING_LENGTH_MASK) { - JS_ReportOutOfMemory(cx); - return NULL; - } - - str = (JSString *) js_NewGCThing(cx, gcflag | GCX_STRING, sizeof(JSString)); - if (!str) - return NULL; - str->length = length; - str->chars = chars; -#ifdef DEBUG - { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveStrings); - JS_RUNTIME_METER(rt, totalStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->lengthSum += (double)length, - rt->lengthSquaredSum += (double)length * (double)length)); - } -#endif - return str; -} - -JSString * -js_NewDependentString(JSContext *cx, JSString *base, size_t start, - size_t length, uintN gcflag) -{ - JSDependentString *ds; - - if (length == 0) - return cx->runtime->emptyString; - - if (start == 0 && length == JSSTRING_LENGTH(base)) - return base; - - if (start > JSSTRDEP_START_MASK || - (start != 0 && length > JSSTRDEP_LENGTH_MASK)) { - return js_NewStringCopyN(cx, JSSTRING_CHARS(base) + start, length, - gcflag); - } - - ds = (JSDependentString *) - js_NewGCThing(cx, gcflag | GCX_MUTABLE_STRING, sizeof(JSString)); - if (!ds) - return NULL; - if (start == 0) { - JSPREFIX_SET_LENGTH(ds, length); - JSPREFIX_SET_BASE(ds, base); - } else { - JSSTRDEP_SET_START_AND_LENGTH(ds, start, length); - JSSTRDEP_SET_BASE(ds, base); - } -#ifdef DEBUG - { - JSRuntime *rt = cx->runtime; - JS_RUNTIME_METER(rt, liveDependentStrings); - JS_RUNTIME_METER(rt, totalDependentStrings); - JS_RUNTIME_METER(rt, liveStrings); - JS_RUNTIME_METER(rt, totalStrings); - JS_LOCK_RUNTIME_VOID(rt, - (rt->strdepLengthSum += (double)length, - rt->strdepLengthSquaredSum += (double)length * (double)length)); - JS_LOCK_RUNTIME_VOID(rt, - (rt->lengthSum += (double)length, - rt->lengthSquaredSum += (double)length * (double)length)); - } -#endif - return (JSString *)ds; -} - -#ifdef DEBUG -#include - -void printJSStringStats(JSRuntime *rt) { - double mean = 0., var = 0., sigma = 0.; - jsrefcount count = rt->totalStrings; - if (count > 0 && rt->lengthSum >= 0) { - mean = rt->lengthSum / count; - var = count * rt->lengthSquaredSum - rt->lengthSum * rt->lengthSum; - if (var < 0.0 || count <= 1) - var = 0.0; - else - var /= count * (count - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.) ? sqrt(var) : 0.; - } - fprintf(stderr, "%lu total strings, mean length %g (sigma %g)\n", - (unsigned long)count, mean, sigma); - - mean = var = sigma = 0.; - count = rt->totalDependentStrings; - if (count > 0 && rt->strdepLengthSum >= 0) { - mean = rt->strdepLengthSum / count; - var = count * rt->strdepLengthSquaredSum - - rt->strdepLengthSum * rt->strdepLengthSum; - if (var < 0.0 || count <= 1) - var = 0.0; - else - var /= count * (count - 1); - - /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ - sigma = (var != 0.) ? sqrt(var) : 0.; - } - fprintf(stderr, "%lu total dependent strings, mean length %g (sigma %g)\n", - (unsigned long)count, mean, sigma); -} -#endif - -JSString * -js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag) -{ - jschar *news; - JSString *str; - - news = (jschar *)JS_malloc(cx, (n + 1) * sizeof(jschar)); - if (!news) - return NULL; - js_strncpy(news, s, n); - news[n] = 0; - str = js_NewString(cx, news, n, gcflag); - if (!str) - JS_free(cx, news); - return str; -} - -JSString * -js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag) -{ - size_t n, m; - jschar *news; - JSString *str; - - n = js_strlen(s); - m = (n + 1) * sizeof(jschar); - news = (jschar *) JS_malloc(cx, m); - if (!news) - return NULL; - memcpy(news, s, m); - str = js_NewString(cx, news, n, gcflag); - if (!str) - JS_free(cx, news); - return str; -} - -JS_STATIC_DLL_CALLBACK(JSHashNumber) -js_hash_string_pointer(const void *key) -{ - return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS; -} - -void -js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str) -{ - JSHashNumber hash; - JSHashEntry *he, **hep; - - if (!rt->deflatedStringCache) - return; - - hash = js_hash_string_pointer(str); - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - hep = JS_HashTableRawLookup(rt->deflatedStringCache, hash, str); - he = *hep; - if (he) { -#ifdef DEBUG - rt->deflatedStringCacheBytes -= JSSTRING_LENGTH(str); -#endif - free(he->value); - JS_HashTableRawRemove(rt->deflatedStringCache, hep, he); - } - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); -} - -void -js_FinalizeString(JSContext *cx, JSString *str) -{ - js_FinalizeStringRT(cx->runtime, str); -} - -void -js_FinalizeStringRT(JSRuntime *rt, JSString *str) -{ - JSBool valid; - - JS_RUNTIME_UNMETER(rt, liveStrings); - if (JSSTRING_IS_DEPENDENT(str)) { - /* If JSSTRFLAG_DEPENDENT is set, this string must be valid. */ - JS_ASSERT(JSSTRDEP_BASE(str)); - JS_RUNTIME_UNMETER(rt, liveDependentStrings); - valid = JS_TRUE; - } else { - /* A stillborn string has null chars, so is not valid. */ - valid = (str->chars != NULL); - if (valid) - free(str->chars); - } - if (valid) { - js_PurgeDeflatedStringCache(rt, str); - str->chars = NULL; - } - str->length = 0; -} - -JSObject * -js_StringToObject(JSContext *cx, JSString *str) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_StringClass, NULL, NULL); - if (!obj) - return NULL; - OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, STRING_TO_JSVAL(str)); - return obj; -} - -JS_FRIEND_API(const char *) -js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun) -{ - JSString *str; - const char *bytes; - - str = v2sfun(cx, v); - if (!str) - return NULL; - str = js_QuoteString(cx, str, 0); - if (!str) - return NULL; - bytes = js_GetStringBytes(cx->runtime, str); - if (!bytes) - JS_ReportOutOfMemory(cx); - return bytes; -} - -JS_FRIEND_API(JSString *) -js_ValueToString(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - - if (JSVAL_IS_OBJECT(v)) { - obj = JSVAL_TO_OBJECT(v); - if (!obj) - return ATOM_TO_STRING(cx->runtime->atomState.nullAtom); - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) - return NULL; - } - if (JSVAL_IS_STRING(v)) { - str = JSVAL_TO_STRING(v); - } else if (JSVAL_IS_INT(v)) { - str = js_NumberToString(cx, JSVAL_TO_INT(v)); - } else if (JSVAL_IS_DOUBLE(v)) { - str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(v)); - } else if (JSVAL_IS_BOOLEAN(v)) { - str = js_BooleanToString(cx, JSVAL_TO_BOOLEAN(v)); - } else { - str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); - } - return str; -} - -JS_FRIEND_API(JSString *) -js_ValueToSource(JSContext *cx, jsval v) -{ - JSTempValueRooter tvr; - JSString *str; - - if (JSVAL_IS_STRING(v)) - return js_QuoteString(cx, JSVAL_TO_STRING(v), '"'); - if (JSVAL_IS_PRIMITIVE(v)) { - /* Special case to preserve negative zero, _contra_ toString. */ - if (JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v))) { - /* NB: _ucNstr rather than _ucstr to indicate non-terminated. */ - static const jschar js_negzero_ucNstr[] = {'-', '0'}; - - return js_NewStringCopyN(cx, js_negzero_ucNstr, 2, 0); - } - return js_ValueToString(cx, v); - } - - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - if (!js_TryMethod(cx, JSVAL_TO_OBJECT(v), - cx->runtime->atomState.toSourceAtom, - 0, NULL, &tvr.u.value)) { - str = NULL; - } else { - str = js_ValueToString(cx, tvr.u.value); - } - JS_POP_TEMP_ROOT(cx, &tvr); - return str; -} - -JSHashNumber -js_HashString(JSString *str) -{ - JSHashNumber h; - const jschar *s; - size_t n; - - h = 0; - for (s = JSSTRING_CHARS(str), n = JSSTRING_LENGTH(str); n; s++, n--) - h = (h >> (JS_HASH_BITS - 4)) ^ (h << 4) ^ *s; - return h; -} - -intN -js_CompareStrings(JSString *str1, JSString *str2) -{ - size_t l1, l2, n, i; - const jschar *s1, *s2; - intN cmp; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return 0; - - l1 = JSSTRING_LENGTH(str1), l2 = JSSTRING_LENGTH(str2); - s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); - n = JS_MIN(l1, l2); - for (i = 0; i < n; i++) { - cmp = s1[i] - s2[i]; - if (cmp != 0) - return cmp; - } - return (intN)(l1 - l2); -} - -JSBool -js_EqualStrings(JSString *str1, JSString *str2) -{ - size_t n; - const jschar *s1, *s2; - - JS_ASSERT(str1); - JS_ASSERT(str2); - - /* Fast case: pointer equality could be a quick win. */ - if (str1 == str2) - return JS_TRUE; - - n = JSSTRING_LENGTH(str1); - if (n != JSSTRING_LENGTH(str2)) - return JS_FALSE; - - if (n == 0) - return JS_TRUE; - - s1 = JSSTRING_CHARS(str1), s2 = JSSTRING_CHARS(str2); - do { - if (*s1 != *s2) - return JS_FALSE; - ++s1, ++s2; - } while (--n != 0); - - return JS_TRUE; -} - -size_t -js_strlen(const jschar *s) -{ - const jschar *t; - - for (t = s; *t != 0; t++) - continue; - return (size_t)(t - s); -} - -jschar * -js_strchr(const jschar *s, jschar c) -{ - while (*s != 0) { - if (*s == c) - return (jschar *)s; - s++; - } - return NULL; -} - -jschar * -js_strchr_limit(const jschar *s, jschar c, const jschar *limit) -{ - while (s < limit) { - if (*s == c) - return (jschar *)s; - s++; - } - return NULL; -} - -const jschar * -js_SkipWhiteSpace(const jschar *s) -{ - /* JS_ISSPACE is false on a null. */ - while (JS_ISSPACE(*s)) - s++; - return s; -} - -#ifdef JS_C_STRINGS_ARE_UTF8 - -jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *length) -{ - jschar *chars = NULL; - size_t dstlen = 0; - - if (!js_InflateStringToBuffer(cx, bytes, *length, NULL, &dstlen)) - return NULL; - chars = (jschar *) JS_malloc(cx, (dstlen + 1) * sizeof (jschar)); - if (!chars) - return NULL; - js_InflateStringToBuffer(cx, bytes, *length, chars, &dstlen); - chars[dstlen] = 0; - *length = dstlen; - return chars; -} - -/* - * May be called with null cx by js_GetStringBytes, see below. - */ -char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length) -{ - size_t size = 0; - char *bytes = NULL; - if (!js_DeflateStringToBuffer(cx, chars, length, NULL, &size)) - return NULL; - bytes = (char *) (cx ? JS_malloc(cx, size+1) : malloc(size+1)); - if (!bytes) - return NULL; - js_DeflateStringToBuffer(cx, chars, length, bytes, &size); - bytes[size] = 0; - return bytes; -} - -JSBool -js_DeflateStringToBuffer(JSContext *cx, const jschar *src, size_t srclen, - char *dst, size_t *dstlenp) -{ - size_t i, utf8Len, dstlen = *dstlenp, origDstlen = dstlen; - jschar c, c2; - uint32 v; - uint8 utf8buf[6]; - - if (!dst) - dstlen = origDstlen = (size_t) -1; - - while (srclen) { - c = *src++; - srclen--; - if ((c >= 0xDC00) && (c <= 0xDFFF)) - goto badSurrogate; - if (c < 0xD800 || c > 0xDBFF) { - v = c; - } else { - if (srclen < 1) - goto bufferTooSmall; - c2 = *src++; - srclen--; - if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { - c = c2; - goto badSurrogate; - } - v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; - } - if (v < 0x0080) { - /* no encoding necessary - performance hack */ - if (!dstlen) - goto bufferTooSmall; - if (dst) - *dst++ = (char) v; - utf8Len = 1; - } else { - utf8Len = js_OneUcs4ToUtf8Char(utf8buf, v); - if (utf8Len > dstlen) - goto bufferTooSmall; - if (dst) { - for (i = 0; i < utf8Len; i++) - *dst++ = (char) utf8buf[i]; - } - } - dstlen -= utf8Len; - } - *dstlenp = (origDstlen - dstlen); - return JS_TRUE; - -badSurrogate: - *dstlenp = (origDstlen - dstlen); - if (cx) { - char buffer[10]; - JS_snprintf(buffer, 10, "0x%x", c); - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_BAD_SURROGATE_CHAR, - buffer); - } - return JS_FALSE; - -bufferTooSmall: - *dstlenp = (origDstlen - dstlen); - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; -} - -JSBool -js_InflateStringToBuffer(JSContext *cx, const char *src, size_t srclen, - jschar *dst, size_t *dstlenp) -{ - uint32 v; - size_t offset = 0, j, n, dstlen = *dstlenp, origDstlen = dstlen; - - if (!dst) - dstlen = origDstlen = (size_t) -1; - - while (srclen) { - v = (uint8) *src; - n = 1; - if (v & 0x80) { - while (v & (0x80 >> n)) - n++; - if (n > srclen) - goto bufferTooSmall; - if (n == 1 || n > 6) - goto badCharacter; - for (j = 1; j < n; j++) { - if ((src[j] & 0xC0) != 0x80) - goto badCharacter; - } - v = Utf8ToOneUcs4Char(src, n); - if (v >= 0x10000) { - v -= 0x10000; - if (v > 0xFFFFF || dstlen < 2) { - *dstlenp = (origDstlen - dstlen); - if (cx) { - char buffer[10]; - JS_snprintf(buffer, 10, "0x%x", v + 0x10000); - JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_UTF8_CHAR_TOO_LARGE, - buffer); - } - return JS_FALSE; - } - if (dstlen < 2) - goto bufferTooSmall; - if (dst) { - *dst++ = (jschar)((v >> 10) + 0xD800); - v = (jschar)((v & 0x3FF) + 0xDC00); - } - dstlen--; - } - } - if (!dstlen) - goto bufferTooSmall; - if (dst) - *dst++ = (jschar) v; - dstlen--; - offset += n; - src += n; - srclen -= n; - } - *dstlenp = (origDstlen - dstlen); - return JS_TRUE; - -badCharacter: - *dstlenp = (origDstlen - dstlen); - if (cx) { - char buffer[10]; - JS_snprintf(buffer, 10, "%d", offset); - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_MALFORMED_UTF8_CHAR, - buffer); - } - return JS_FALSE; - -bufferTooSmall: - *dstlenp = (origDstlen - dstlen); - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; -} - -#else - -JSBool -js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, - jschar *chars, size_t* charsLength) -{ - size_t i; - - if (length > *charsLength) { - for (i = 0; i < *charsLength; i++) - chars[i] = (unsigned char) bytes[i]; - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; - } - for (i = 0; i < length; i++) - chars[i] = (unsigned char) bytes[i]; - *charsLength = length; - return JS_TRUE; -} - -jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *bytesLength) -{ - jschar *chars; - size_t i, length = *bytesLength; - - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) { - *bytesLength = 0; - return NULL; - } - for (i = 0; i < length; i++) - chars[i] = (unsigned char) bytes[i]; - chars[length] = 0; - *bytesLength = length; - return chars; -} - -JSBool -js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, size_t length, - char *bytes, size_t* bytesLength) -{ - size_t i; - - if (length > *bytesLength) { - for (i = 0; i < *bytesLength; i++) - bytes[i] = (char) chars[i]; - if (cx) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BUFFER_TOO_SMALL); - } - return JS_FALSE; - } - for (i = 0; i < length; i++) - bytes[i] = (char) chars[i]; - *bytesLength = length; - return JS_TRUE; -} - -/* - * May be called with null cx by js_GetStringBytes, see below. - */ -char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length) -{ - size_t i, size; - char *bytes; - - size = (length + 1) * sizeof(char); - bytes = (char *) (cx ? JS_malloc(cx, size) : malloc(size)); - if (!bytes) - return NULL; - - for (i = 0; i < length; i++) - bytes[i] = (char) chars[i]; - - bytes[length] = 0; - return bytes; -} - -#endif - -static JSHashTable * -GetDeflatedStringCache(JSRuntime *rt) -{ - JSHashTable *cache; - - cache = rt->deflatedStringCache; - if (!cache) { - cache = JS_NewHashTable(8, js_hash_string_pointer, - JS_CompareValues, JS_CompareValues, - NULL, NULL); - rt->deflatedStringCache = cache; - } - return cache; -} - -JSBool -js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length) -{ - JSHashTable *cache; - JSBool ok; - JSHashNumber hash; - JSHashEntry **hep; - - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - - cache = GetDeflatedStringCache(rt); - if (!cache) { - ok = JS_FALSE; - } else { - hash = js_hash_string_pointer(str); - hep = JS_HashTableRawLookup(cache, hash, str); - JS_ASSERT(*hep == NULL); - ok = JS_HashTableRawAdd(cache, hep, hash, str, bytes) != NULL; -#ifdef DEBUG - if (ok) - rt->deflatedStringCacheBytes += length; -#endif - } - - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); - return ok; -} - -char * -js_GetStringBytes(JSRuntime *rt, JSString *str) -{ - JSHashTable *cache; - char *bytes; - JSHashNumber hash; - JSHashEntry *he, **hep; - - JS_ACQUIRE_LOCK(rt->deflatedStringCacheLock); - - cache = GetDeflatedStringCache(rt); - if (!cache) { - bytes = NULL; - } else { - hash = js_hash_string_pointer(str); - hep = JS_HashTableRawLookup(cache, hash, str); - he = *hep; - if (he) { - bytes = (char *) he->value; - - /* Try to catch failure to JS_ShutDown between runtime epochs. */ - JS_ASSERT((*bytes == '\0' && JSSTRING_LENGTH(str) == 0) || - *bytes == (char) JSSTRING_CHARS(str)[0]); - } else { - bytes = js_DeflateString(NULL, JSSTRING_CHARS(str), - JSSTRING_LENGTH(str)); - if (bytes) { - if (JS_HashTableRawAdd(cache, hep, hash, str, bytes)) { -#ifdef DEBUG - rt->deflatedStringCacheBytes += JSSTRING_LENGTH(str); -#endif - } else { - free(bytes); - bytes = NULL; - } - } - } - } - - JS_RELEASE_LOCK(rt->deflatedStringCacheLock); - return bytes; -} - -/* - * From java.lang.Character.java: - * - * The character properties are currently encoded into 32 bits in the - * following manner: - * - * 10 bits signed offset used for converting case - * 1 bit if 1, adding the signed offset converts the character to - * lowercase - * 1 bit if 1, subtracting the signed offset converts the character to - * uppercase - * 1 bit if 1, character has a titlecase equivalent (possibly itself) - * 3 bits 0 may not be part of an identifier - * 1 ignorable control; may continue a Unicode identifier or JS - * identifier - * 2 may continue a JS identifier but not a Unicode identifier - * (unused) - * 3 may continue a Unicode identifier or JS identifier - * 4 is a JS whitespace character - * 5 may start or continue a JS identifier; - * may continue but not start a Unicode identifier (_) - * 6 may start or continue a JS identifier but not a Unicode - * identifier ($) - * 7 may start or continue a Unicode identifier or JS identifier - * Thus: - * 5, 6, 7 may start a JS identifier - * 1, 2, 3, 5, 6, 7 may continue a JS identifier - * 7 may start a Unicode identifier - * 1, 3, 5, 7 may continue a Unicode identifier - * 1 is ignorable within an identifier - * 4 is JS whitespace - * 2 bits 0 this character has no numeric property - * 1 adding the digit offset to the character code and then - * masking with 0x1F will produce the desired numeric value - * 2 this character has a "strange" numeric value - * 3 a JS supradecimal digit: adding the digit offset to the - * character code, then masking with 0x1F, then adding 10 - * will produce the desired numeric value - * 5 bits digit offset - * 1 bit XML 1.0 name start character - * 1 bit XML 1.0 name character - * 2 bits reserved for future use - * 5 bits character type - */ - -/* The X table has 1024 entries for a total of 1024 bytes. */ - -const uint8 js_X[] = { - 0, 1, 2, 3, 4, 5, 6, 7, /* 0x0000 */ - 8, 9, 10, 11, 12, 13, 14, 15, /* 0x0200 */ - 16, 17, 18, 19, 20, 21, 22, 23, /* 0x0400 */ - 24, 25, 26, 27, 28, 28, 28, 28, /* 0x0600 */ - 28, 28, 28, 28, 29, 30, 31, 32, /* 0x0800 */ - 33, 34, 35, 36, 37, 38, 39, 40, /* 0x0A00 */ - 41, 42, 43, 44, 45, 46, 28, 28, /* 0x0C00 */ - 47, 48, 49, 50, 51, 52, 53, 28, /* 0x0E00 */ - 28, 28, 54, 55, 56, 57, 58, 59, /* 0x1000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x1C00 */ - 60, 60, 61, 62, 63, 64, 65, 66, /* 0x1E00 */ - 67, 68, 69, 70, 71, 72, 73, 74, /* 0x2000 */ - 75, 75, 75, 76, 77, 78, 28, 28, /* 0x2200 */ - 79, 80, 81, 82, 83, 83, 84, 85, /* 0x2400 */ - 86, 85, 28, 28, 87, 88, 89, 28, /* 0x2600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2C00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x2E00 */ - 90, 91, 92, 93, 94, 56, 95, 28, /* 0x3000 */ - 96, 97, 98, 99, 83, 100, 83, 101, /* 0x3200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3C00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x3E00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4A00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0x4C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x4E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x5E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x6E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x7E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8C00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x8E00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9A00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0x9C00 */ - 56, 56, 56, 56, 56, 56, 102, 28, /* 0x9E00 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA000 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA200 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA400 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA600 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xA800 */ - 28, 28, 28, 28, 28, 28, 28, 28, /* 0xAA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xAE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xB800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xBE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC400 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC600 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xC800 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCA00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCC00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xCE00 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD000 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD200 */ - 56, 56, 56, 56, 56, 56, 56, 56, /* 0xD400 */ - 56, 56, 56, 56, 56, 56, 103, 28, /* 0xD600 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xD800 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDA00 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDC00 */ -104, 104, 104, 104, 104, 104, 104, 104, /* 0xDE00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE000 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE200 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE400 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE600 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xE800 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEA00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEC00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xEE00 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF000 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF200 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF400 */ -105, 105, 105, 105, 105, 105, 105, 105, /* 0xF600 */ -105, 105, 105, 105, 56, 56, 56, 56, /* 0xF800 */ -106, 28, 28, 28, 107, 108, 109, 110, /* 0xFA00 */ - 56, 56, 56, 56, 111, 112, 113, 114, /* 0xFC00 */ -115, 116, 56, 117, 118, 119, 120, 121 /* 0xFE00 */ -}; - -/* The Y table has 7808 entries for a total of 7808 bytes. */ - -const uint8 js_Y[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 0, 1, 1, 1, 1, 1, 0, 0, /* 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */ - 2, 3, 3, 3, 4, 3, 3, 3, /* 0 */ - 5, 6, 3, 7, 3, 8, 3, 3, /* 0 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 0 */ - 9, 9, 3, 3, 7, 7, 7, 3, /* 0 */ - 3, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 1 */ - 10, 10, 10, 5, 3, 6, 11, 12, /* 1 */ - 11, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 1 */ - 13, 13, 13, 5, 7, 6, 7, 0, /* 1 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */ - 2, 3, 4, 4, 4, 4, 15, 15, /* 2 */ - 11, 15, 16, 5, 7, 8, 15, 11, /* 2 */ - 15, 7, 17, 17, 11, 16, 15, 3, /* 2 */ - 11, 18, 16, 6, 19, 19, 19, 3, /* 2 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 7, /* 3 */ - 20, 20, 20, 20, 20, 20, 20, 16, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 7, /* 3 */ - 21, 21, 21, 21, 21, 21, 21, 22, /* 3 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 4 */ - 25, 26, 23, 24, 23, 24, 23, 24, /* 4 */ - 16, 23, 24, 23, 24, 23, 24, 23, /* 4 */ - 24, 23, 24, 23, 24, 23, 24, 23, /* 5 */ - 24, 16, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 5 */ - 27, 23, 24, 23, 24, 23, 24, 28, /* 5 */ - 16, 29, 23, 24, 23, 24, 30, 23, /* 6 */ - 24, 31, 31, 23, 24, 16, 32, 32, /* 6 */ - 33, 23, 24, 31, 34, 16, 35, 36, /* 6 */ - 23, 24, 16, 16, 35, 37, 16, 38, /* 6 */ - 23, 24, 23, 24, 23, 24, 38, 23, /* 6 */ - 24, 39, 40, 16, 23, 24, 39, 23, /* 6 */ - 24, 41, 41, 23, 24, 23, 24, 42, /* 6 */ - 23, 24, 16, 40, 23, 24, 40, 40, /* 6 */ - 40, 40, 40, 40, 43, 44, 45, 43, /* 7 */ - 44, 45, 43, 44, 45, 23, 24, 23, /* 7 */ - 24, 23, 24, 23, 24, 23, 24, 23, /* 7 */ - 24, 23, 24, 23, 24, 16, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 7 */ - 16, 43, 44, 45, 23, 24, 46, 46, /* 7 */ - 46, 46, 23, 24, 23, 24, 23, 24, /* 7 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 8 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 9 */ - 16, 16, 16, 47, 48, 16, 49, 49, /* 9 */ - 50, 50, 16, 51, 16, 16, 16, 16, /* 9 */ - 49, 16, 16, 52, 16, 16, 16, 16, /* 9 */ - 53, 54, 16, 16, 16, 16, 16, 54, /* 9 */ - 16, 16, 55, 16, 16, 16, 16, 16, /* 9 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 9 */ - 16, 16, 16, 56, 16, 16, 16, 16, /* 10 */ - 56, 16, 57, 57, 16, 16, 16, 16, /* 10 */ - 16, 16, 58, 16, 16, 16, 16, 16, /* 10 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 10 */ - 16, 46, 46, 46, 46, 46, 46, 46, /* 10 */ - 59, 59, 59, 59, 59, 59, 59, 59, /* 10 */ - 59, 11, 11, 59, 59, 59, 59, 59, /* 10 */ - 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ - 11, 11, 11, 11, 11, 11, 11, 11, /* 11 */ - 59, 59, 11, 11, 11, 11, 11, 11, /* 11 */ - 11, 11, 11, 11, 11, 11, 11, 46, /* 11 */ - 59, 59, 59, 59, 59, 11, 11, 11, /* 11 */ - 11, 11, 46, 46, 46, 46, 46, 46, /* 11 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 11 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 12 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 60, 60, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 13 */ - 46, 46, 46, 46, 3, 3, 46, 46, /* 13 */ - 46, 46, 59, 46, 46, 46, 3, 46, /* 13 */ - 46, 46, 46, 46, 11, 11, 61, 3, /* 14 */ - 62, 62, 62, 46, 63, 46, 64, 64, /* 14 */ - 16, 20, 20, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 46, 20, 20, 20, 20, 20, /* 14 */ - 20, 20, 20, 20, 65, 66, 66, 66, /* 14 */ - 16, 21, 21, 21, 21, 21, 21, 21, /* 14 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 14 */ - 21, 21, 16, 21, 21, 21, 21, 21, /* 15 */ - 21, 21, 21, 21, 67, 68, 68, 46, /* 15 */ - 69, 70, 38, 38, 38, 71, 72, 46, /* 15 */ - 46, 46, 38, 46, 38, 46, 38, 46, /* 15 */ - 38, 46, 23, 24, 23, 24, 23, 24, /* 15 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 15 */ - 73, 74, 16, 40, 46, 46, 46, 46, /* 15 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 15 */ - 46, 75, 75, 75, 75, 75, 75, 75, /* 16 */ - 75, 75, 75, 75, 75, 46, 75, 75, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 20, 20, 20, 20, 20, 20, 20, 20, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 16 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ - 21, 21, 21, 21, 21, 21, 21, 21, /* 17 */ - 46, 74, 74, 74, 74, 74, 74, 74, /* 17 */ - 74, 74, 74, 74, 74, 46, 74, 74, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 17 */ - 23, 24, 15, 60, 60, 60, 60, 46, /* 18 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 18 */ - 40, 23, 24, 23, 24, 46, 46, 23, /* 19 */ - 24, 46, 46, 23, 24, 46, 46, 46, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 19 */ - 23, 24, 23, 24, 46, 46, 23, 24, /* 19 */ - 23, 24, 23, 24, 23, 24, 46, 46, /* 19 */ - 23, 24, 46, 46, 46, 46, 46, 46, /* 19 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 20 */ - 46, 76, 76, 76, 76, 76, 76, 76, /* 20 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 20 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 21 */ - 76, 76, 76, 76, 76, 76, 76, 46, /* 21 */ - 46, 59, 3, 3, 3, 3, 3, 3, /* 21 */ - 46, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 77, /* 21 */ - 77, 77, 77, 77, 77, 77, 77, 16, /* 22 */ - 46, 3, 46, 46, 46, 46, 46, 46, /* 22 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 46, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 22 */ - 60, 60, 46, 60, 60, 60, 3, 60, /* 22 */ - 3, 60, 60, 3, 60, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 23 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 23 */ - 40, 40, 40, 3, 3, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 23 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ - 46, 46, 46, 46, 3, 46, 46, 46, /* 24 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 24 */ - 46, 46, 46, 3, 46, 46, 46, 3, /* 24 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 24 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 24 */ - 59, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 60, 60, 60, 60, 60, /* 25 */ - 60, 60, 60, 46, 46, 46, 46, 46, /* 25 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 25 */ - 78, 78, 78, 78, 78, 78, 78, 78, /* 25 */ - 78, 78, 3, 3, 3, 3, 46, 46, /* 25 */ - 60, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 25 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 26 */ - 46, 46, 40, 40, 40, 40, 40, 46, /* 26 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 27 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 27 */ - 40, 40, 40, 40, 3, 40, 60, 60, /* 27 */ - 60, 60, 60, 60, 60, 79, 79, 60, /* 27 */ - 60, 60, 60, 60, 60, 59, 59, 60, /* 27 */ - 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ - 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 60, 60, 80, 46, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 29 */ - 40, 40, 46, 46, 60, 40, 80, 80, /* 29 */ - 80, 60, 60, 60, 60, 60, 60, 60, /* 30 */ - 60, 80, 80, 80, 80, 60, 46, 46, /* 30 */ - 15, 60, 60, 60, 60, 46, 46, 46, /* 30 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 30 */ - 40, 40, 60, 60, 3, 3, 81, 81, /* 30 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 30 */ - 3, 46, 46, 46, 46, 46, 46, 46, /* 30 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 30 */ - 46, 60, 80, 80, 46, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 46, 46, 40, /* 31 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 31 */ - 40, 46, 40, 46, 46, 46, 40, 40, /* 31 */ - 40, 40, 46, 46, 60, 46, 80, 80, /* 31 */ - 80, 60, 60, 60, 60, 46, 46, 80, /* 32 */ - 80, 46, 46, 80, 80, 60, 46, 46, /* 32 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 32 */ - 46, 46, 46, 46, 40, 40, 46, 40, /* 32 */ - 40, 40, 60, 60, 46, 46, 81, 81, /* 32 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 32 */ - 40, 40, 4, 4, 82, 82, 82, 82, /* 32 */ - 19, 83, 15, 46, 46, 46, 46, 46, /* 32 */ - 46, 46, 60, 46, 46, 40, 40, 40, /* 33 */ - 40, 40, 40, 46, 46, 46, 46, 40, /* 33 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 33 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 33 */ - 40, 46, 40, 40, 46, 40, 40, 46, /* 33 */ - 40, 40, 46, 46, 60, 46, 80, 80, /* 33 */ - 80, 60, 60, 46, 46, 46, 46, 60, /* 34 */ - 60, 46, 46, 60, 60, 60, 46, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ - 46, 40, 40, 40, 40, 46, 40, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 81, 81, /* 34 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 34 */ - 60, 60, 40, 40, 40, 46, 46, 46, /* 34 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 34 */ - 46, 60, 60, 80, 46, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 46, 40, 46, 40, /* 35 */ - 40, 40, 46, 40, 40, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 35 */ - 40, 46, 40, 40, 46, 40, 40, 40, /* 35 */ - 40, 40, 46, 46, 60, 40, 80, 80, /* 35 */ - 80, 60, 60, 60, 60, 60, 46, 60, /* 36 */ - 60, 80, 46, 80, 80, 60, 46, 46, /* 36 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 40, 46, 46, 46, 46, 46, 81, 81, /* 36 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 36 */ - 46, 60, 80, 80, 46, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 46, 46, 40, /* 37 */ - 40, 46, 46, 40, 40, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 37 */ - 40, 46, 40, 40, 46, 46, 40, 40, /* 37 */ - 40, 40, 46, 46, 60, 40, 80, 60, /* 37 */ - 80, 60, 60, 60, 46, 46, 46, 80, /* 38 */ - 80, 46, 46, 80, 80, 60, 46, 46, /* 38 */ - 46, 46, 46, 46, 46, 46, 60, 80, /* 38 */ - 46, 46, 46, 46, 40, 40, 46, 40, /* 38 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 38 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 38 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 38 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 38 */ - 46, 46, 60, 80, 46, 40, 40, 40, /* 39 */ - 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ - 40, 46, 40, 40, 40, 40, 46, 46, /* 39 */ - 46, 40, 40, 46, 40, 46, 40, 40, /* 39 */ - 46, 46, 46, 40, 40, 46, 46, 46, /* 39 */ - 40, 40, 40, 46, 46, 46, 40, 40, /* 39 */ - 40, 40, 40, 40, 40, 40, 46, 40, /* 39 */ - 40, 40, 46, 46, 46, 46, 80, 80, /* 39 */ - 60, 80, 80, 46, 46, 46, 80, 80, /* 40 */ - 80, 46, 80, 80, 80, 60, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 81, /* 40 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 40 */ - 84, 19, 19, 46, 46, 46, 46, 46, /* 40 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 40 */ - 46, 80, 80, 80, 46, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 41 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 41 */ - 40, 40, 40, 40, 46, 40, 40, 40, /* 41 */ - 40, 40, 46, 46, 46, 46, 60, 60, /* 41 */ - 60, 80, 80, 80, 80, 46, 60, 60, /* 42 */ - 60, 46, 60, 60, 60, 60, 46, 46, /* 42 */ - 46, 46, 46, 46, 46, 60, 60, 46, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 42 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 42 */ - 46, 46, 80, 80, 46, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 43 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 43 */ - 40, 40, 40, 40, 46, 40, 40, 40, /* 43 */ - 40, 40, 46, 46, 46, 46, 80, 60, /* 43 */ - 80, 80, 80, 80, 80, 46, 60, 80, /* 44 */ - 80, 46, 80, 80, 60, 60, 46, 46, /* 44 */ - 46, 46, 46, 46, 46, 80, 80, 46, /* 44 */ - 46, 46, 46, 46, 46, 46, 40, 46, /* 44 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 44 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 44 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 44 */ - 46, 46, 80, 80, 46, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 46, 40, 40, /* 45 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 46, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 45 */ - 40, 40, 46, 46, 46, 46, 80, 80, /* 45 */ - 80, 60, 60, 60, 46, 46, 80, 80, /* 46 */ - 80, 46, 80, 80, 80, 60, 46, 46, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 80, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 40, 40, 46, 46, 46, 46, 81, 81, /* 46 */ - 81, 81, 81, 81, 81, 81, 81, 81, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 46 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 47 */ - 40, 40, 40, 40, 40, 40, 40, 3, /* 47 */ - 40, 60, 40, 40, 60, 60, 60, 60, /* 47 */ - 60, 60, 60, 46, 46, 46, 46, 4, /* 47 */ - 40, 40, 40, 40, 40, 40, 59, 60, /* 48 */ - 60, 60, 60, 60, 60, 60, 60, 15, /* 48 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 48 */ - 9, 9, 3, 3, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 48 */ - 46, 40, 40, 46, 40, 46, 46, 40, /* 49 */ - 40, 46, 40, 46, 46, 40, 46, 46, /* 49 */ - 46, 46, 46, 46, 40, 40, 40, 40, /* 49 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 49 */ - 46, 40, 40, 40, 46, 40, 46, 40, /* 49 */ - 46, 46, 40, 40, 46, 40, 40, 3, /* 49 */ - 40, 60, 40, 40, 60, 60, 60, 60, /* 49 */ - 60, 60, 46, 60, 60, 40, 46, 46, /* 49 */ - 40, 40, 40, 40, 40, 46, 59, 46, /* 50 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 50 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 50 */ - 9, 9, 46, 46, 40, 40, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 50 */ - 15, 15, 15, 15, 3, 3, 3, 3, /* 51 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 51 */ - 3, 3, 3, 15, 15, 15, 15, 15, /* 51 */ - 60, 60, 15, 15, 15, 15, 15, 15, /* 51 */ - 78, 78, 78, 78, 78, 78, 78, 78, /* 51 */ - 78, 78, 85, 85, 85, 85, 85, 85, /* 51 */ - 85, 85, 85, 85, 15, 60, 15, 60, /* 51 */ - 15, 60, 5, 6, 5, 6, 80, 80, /* 51 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 52 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 52 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 52 */ - 60, 60, 60, 60, 60, 60, 60, 80, /* 52 */ - 60, 60, 60, 60, 60, 3, 60, 60, /* 53 */ - 60, 60, 60, 60, 46, 46, 46, 46, /* 53 */ - 60, 60, 60, 60, 60, 60, 46, 60, /* 53 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 60, 60, 60, 60, 60, 60, 46, 46, /* 53 */ - 46, 60, 60, 60, 60, 60, 60, 60, /* 53 */ - 46, 60, 46, 46, 46, 46, 46, 46, /* 53 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 76, 76, /* 54 */ - 76, 76, 76, 76, 76, 76, 46, 46, /* 55 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 16, /* 55 */ - 16, 16, 16, 16, 16, 16, 16, 46, /* 55 */ - 46, 46, 46, 3, 46, 46, 46, 46, /* 55 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 56 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 46, 46, 46, 46, 46, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 57 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 46, 46, 46, 46, 46, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 58 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 59 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 59 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 60 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 16, 16, /* 61 */ - 16, 16, 16, 16, 46, 46, 46, 46, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 61 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 23, 24, 23, 24, 23, 24, /* 62 */ - 23, 24, 46, 46, 46, 46, 46, 46, /* 62 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 46, 46, /* 63 */ - 87, 87, 87, 87, 87, 87, 46, 46, /* 63 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 63 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 63 */ - 86, 86, 86, 86, 86, 86, 46, 46, /* 64 */ - 87, 87, 87, 87, 87, 87, 46, 46, /* 64 */ - 16, 86, 16, 86, 16, 86, 16, 86, /* 64 */ - 46, 87, 46, 87, 46, 87, 46, 87, /* 64 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 64 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 64 */ - 88, 88, 89, 89, 89, 89, 90, 90, /* 64 */ - 91, 91, 92, 92, 93, 93, 46, 46, /* 64 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 86, 86, 86, 86, 86, 86, /* 65 */ - 87, 87, 87, 87, 87, 87, 87, 87, /* 65 */ - 86, 86, 16, 94, 16, 46, 16, 16, /* 65 */ - 87, 87, 95, 95, 96, 11, 38, 11, /* 65 */ - 11, 11, 16, 94, 16, 46, 16, 16, /* 66 */ - 97, 97, 97, 97, 96, 11, 11, 11, /* 66 */ - 86, 86, 16, 16, 46, 46, 16, 16, /* 66 */ - 87, 87, 98, 98, 46, 11, 11, 11, /* 66 */ - 86, 86, 16, 16, 16, 99, 16, 16, /* 66 */ - 87, 87, 100, 100, 101, 11, 11, 11, /* 66 */ - 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ -102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ - 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ - 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ - 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ - 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ -105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ - 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ - 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ - 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ - 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ - 17, 17, 7, 7, 7, 5, 6, 16, /* 68 */ -107, 107, 107, 107, 107, 107, 107, 107, /* 69 */ -107, 107, 7, 7, 7, 5, 6, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 4, 4, 4, 4, 4, 4, 4, 4, /* 69 */ - 4, 4, 4, 4, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 69 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 60, 60, 60, 60, 60, 60, 60, 60, /* 70 */ - 60, 60, 60, 60, 60, 79, 79, 79, /* 70 */ - 79, 60, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 70 */ - 15, 15, 38, 15, 15, 15, 15, 38, /* 71 */ - 15, 15, 16, 38, 38, 38, 16, 16, /* 71 */ - 38, 38, 38, 16, 15, 38, 15, 15, /* 71 */ - 38, 38, 38, 38, 38, 38, 15, 15, /* 71 */ - 15, 15, 15, 15, 38, 15, 38, 15, /* 71 */ - 38, 15, 38, 38, 38, 38, 16, 16, /* 71 */ - 38, 38, 15, 38, 16, 40, 40, 40, /* 71 */ - 40, 46, 46, 46, 46, 46, 46, 46, /* 71 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 72 */ - 46, 46, 46, 19, 19, 19, 19, 19, /* 72 */ - 19, 19, 19, 19, 19, 19, 19, 108, /* 72 */ -109, 109, 109, 109, 109, 109, 109, 109, /* 72 */ -109, 109, 109, 109, 110, 110, 110, 110, /* 72 */ -111, 111, 111, 111, 111, 111, 111, 111, /* 72 */ -111, 111, 111, 111, 112, 112, 112, 112, /* 72 */ -113, 113, 113, 46, 46, 46, 46, 46, /* 73 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 73 */ - 7, 7, 7, 7, 7, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 73 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 7, 15, 7, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 74 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 74 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 74 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 75 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 7, 7, 7, 7, 7, 7, /* 76 */ - 7, 7, 46, 46, 46, 46, 46, 46, /* 76 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 76 */ - 15, 46, 15, 15, 15, 15, 15, 15, /* 77 */ - 7, 7, 7, 7, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 7, 7, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 5, 6, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 77 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 78 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 78 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 79 */ - 15, 15, 15, 15, 15, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 79 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 80 */ - 15, 15, 15, 46, 46, 46, 46, 46, /* 80 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 80 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 80 */ -114, 114, 114, 114, 82, 82, 82, 82, /* 80 */ - 82, 82, 82, 82, 82, 82, 82, 82, /* 80 */ - 82, 82, 82, 82, 82, 82, 82, 82, /* 81 */ -115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ -115, 115, 115, 115, 115, 115, 115, 115, /* 81 */ -115, 115, 115, 115, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 81 */ - 15, 15, 15, 15, 15, 15, 116, 116, /* 81 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 81 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ -116, 116, 116, 116, 116, 116, 116, 116, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 117, 117, 117, 117, 117, 117, /* 82 */ -117, 117, 118, 46, 46, 46, 46, 46, /* 82 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 82 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 83 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 46, 46, /* 84 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 84 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 85 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 85 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 86 */ - 46, 46, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 86 */ - 46, 15, 15, 15, 15, 46, 15, 15, /* 87 */ - 15, 15, 46, 46, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 87 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 88 */ - 15, 15, 15, 15, 46, 15, 46, 15, /* 88 */ - 15, 15, 15, 46, 46, 46, 15, 46, /* 88 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 88 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 88 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 88 */ - 46, 46, 46, 46, 46, 46, 119, 119, /* 88 */ -119, 119, 119, 119, 119, 119, 119, 119, /* 88 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 89 */ -114, 114, 83, 83, 83, 83, 83, 83, /* 89 */ - 83, 83, 83, 83, 15, 46, 46, 46, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 46, 15, 15, 15, 15, 15, 15, 15, /* 89 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 89 */ - 2, 3, 3, 3, 15, 59, 3, 120, /* 90 */ - 5, 6, 5, 6, 5, 6, 5, 6, /* 90 */ - 5, 6, 15, 15, 5, 6, 5, 6, /* 90 */ - 5, 6, 5, 6, 8, 5, 6, 5, /* 90 */ - 15, 121, 121, 121, 121, 121, 121, 121, /* 90 */ -121, 121, 60, 60, 60, 60, 60, 60, /* 90 */ - 8, 59, 59, 59, 59, 59, 15, 15, /* 90 */ - 46, 46, 46, 46, 46, 46, 46, 15, /* 90 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 91 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 46, 46, 46, /* 92 */ - 46, 60, 60, 59, 59, 59, 59, 46, /* 92 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 92 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 93 */ - 40, 40, 40, 3, 59, 59, 59, 46, /* 93 */ - 46, 46, 46, 46, 46, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 46, 46, 46, /* 94 */ - 46, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 94 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 95 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 95 */ - 15, 15, 85, 85, 85, 85, 15, 15, /* 95 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 95 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 46, 46, 46, /* 96 */ - 85, 85, 85, 85, 85, 85, 85, 85, /* 96 */ - 85, 85, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 96 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 97 */ - 15, 15, 15, 15, 46, 46, 46, 15, /* 97 */ -114, 114, 114, 114, 114, 114, 114, 114, /* 98 */ -114, 114, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 98 */ - 15, 46, 46, 46, 46, 46, 46, 46, /* 98 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 98 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 46, 46, 46, 46, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 99 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 100 */ - 46, 46, 46, 15, 15, 15, 15, 15, /* 100 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 46, 46, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 15, /* 101 */ - 15, 15, 15, 15, 15, 15, 15, 46, /* 101 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 102 */ - 40, 40, 40, 40, 40, 40, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 102 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 103 */ - 40, 40, 40, 40, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 103 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -122, 122, 122, 122, 122, 122, 122, 122, /* 104 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ -123, 123, 123, 123, 123, 123, 123, 123, /* 105 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 106 */ - 40, 40, 40, 40, 40, 40, 46, 46, /* 106 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 106 */ - 16, 16, 16, 16, 16, 16, 16, 46, /* 107 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 107 */ - 46, 46, 46, 16, 16, 16, 16, 16, /* 107 */ - 46, 46, 46, 46, 46, 46, 60, 40, /* 107 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 107 */ - 40, 7, 40, 40, 40, 40, 40, 40, /* 107 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 107 */ - 40, 40, 40, 40, 40, 46, 40, 46, /* 107 */ - 40, 40, 46, 40, 40, 46, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 108 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 109 */ - 40, 40, 46, 46, 46, 46, 46, 46, /* 109 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 109 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 110 */ - 46, 46, 46, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 110 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 111 */ - 40, 40, 40, 40, 40, 40, 5, 6, /* 111 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 112 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 113 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 114 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 114 */ - 40, 40, 40, 40, 46, 46, 46, 46, /* 114 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 60, 60, 60, 60, 46, 46, 46, 46, /* 115 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 115 */ - 3, 8, 8, 12, 12, 5, 6, 5, /* 115 */ - 6, 5, 6, 5, 6, 5, 6, 5, /* 115 */ - 6, 5, 6, 5, 6, 46, 46, 46, /* 116 */ - 46, 3, 3, 3, 3, 12, 12, 12, /* 116 */ - 3, 3, 3, 46, 3, 3, 3, 3, /* 116 */ - 8, 5, 6, 5, 6, 5, 6, 3, /* 116 */ - 3, 3, 7, 8, 7, 7, 7, 46, /* 116 */ - 3, 4, 3, 3, 46, 46, 46, 46, /* 116 */ - 40, 40, 40, 46, 40, 46, 40, 40, /* 116 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 116 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 117 */ - 40, 40, 40, 40, 40, 46, 46, 104, /* 117 */ - 46, 3, 3, 3, 4, 3, 3, 3, /* 118 */ - 5, 6, 3, 7, 3, 8, 3, 3, /* 118 */ - 9, 9, 9, 9, 9, 9, 9, 9, /* 118 */ - 9, 9, 3, 3, 7, 7, 7, 3, /* 118 */ - 3, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 10, 10, 10, 10, 10, /* 118 */ - 10, 10, 10, 5, 3, 6, 11, 12, /* 118 */ - 11, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 13, 13, 13, 13, 13, /* 119 */ - 13, 13, 13, 5, 7, 6, 7, 46, /* 119 */ - 46, 3, 5, 6, 3, 3, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 59, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 119 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 59, 59, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 40, /* 120 */ - 40, 40, 40, 40, 40, 40, 40, 46, /* 120 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 40, 40, 40, /* 121 */ - 46, 46, 40, 40, 40, 46, 46, 46, /* 121 */ - 4, 4, 7, 11, 15, 4, 4, 46, /* 121 */ - 7, 7, 7, 7, 7, 15, 15, 46, /* 121 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 121 */ - 46, 46, 46, 46, 46, 15, 46, 46 /* 121 */ -}; - -/* The A table has 124 entries for a total of 496 bytes. */ - -const uint32 js_A[] = { -0x0001000F, /* 0 Cc, ignorable */ -0x0004000F, /* 1 Cc, whitespace */ -0x0004000C, /* 2 Zs, whitespace */ -0x00000018, /* 3 Po */ -0x0006001A, /* 4 Sc, currency */ -0x00000015, /* 5 Ps */ -0x00000016, /* 6 Pe */ -0x00000019, /* 7 Sm */ -0x00000014, /* 8 Pd */ -0x00036089, /* 9 Nd, identifier part, decimal 16 */ -0x0827FF81, /* 10 Lu, hasLower (add 32), identifier start, supradecimal 31 */ -0x0000001B, /* 11 Sk */ -0x00050017, /* 12 Pc, underscore */ -0x0817FF82, /* 13 Ll, hasUpper (subtract 32), identifier start, supradecimal 31 */ -0x0000000C, /* 14 Zs */ -0x0000001C, /* 15 So */ -0x00070182, /* 16 Ll, identifier start */ -0x0000600B, /* 17 No, decimal 16 */ -0x0000500B, /* 18 No, decimal 8 */ -0x0000800B, /* 19 No, strange */ -0x08270181, /* 20 Lu, hasLower (add 32), identifier start */ -0x08170182, /* 21 Ll, hasUpper (subtract 32), identifier start */ -0xE1D70182, /* 22 Ll, hasUpper (subtract -121), identifier start */ -0x00670181, /* 23 Lu, hasLower (add 1), identifier start */ -0x00570182, /* 24 Ll, hasUpper (subtract 1), identifier start */ -0xCE670181, /* 25 Lu, hasLower (add -199), identifier start */ -0x3A170182, /* 26 Ll, hasUpper (subtract 232), identifier start */ -0xE1E70181, /* 27 Lu, hasLower (add -121), identifier start */ -0x4B170182, /* 28 Ll, hasUpper (subtract 300), identifier start */ -0x34A70181, /* 29 Lu, hasLower (add 210), identifier start */ -0x33A70181, /* 30 Lu, hasLower (add 206), identifier start */ -0x33670181, /* 31 Lu, hasLower (add 205), identifier start */ -0x32A70181, /* 32 Lu, hasLower (add 202), identifier start */ -0x32E70181, /* 33 Lu, hasLower (add 203), identifier start */ -0x33E70181, /* 34 Lu, hasLower (add 207), identifier start */ -0x34E70181, /* 35 Lu, hasLower (add 211), identifier start */ -0x34670181, /* 36 Lu, hasLower (add 209), identifier start */ -0x35670181, /* 37 Lu, hasLower (add 213), identifier start */ -0x00070181, /* 38 Lu, identifier start */ -0x36A70181, /* 39 Lu, hasLower (add 218), identifier start */ -0x00070185, /* 40 Lo, identifier start */ -0x36670181, /* 41 Lu, hasLower (add 217), identifier start */ -0x36E70181, /* 42 Lu, hasLower (add 219), identifier start */ -0x00AF0181, /* 43 Lu, hasLower (add 2), hasTitle, identifier start */ -0x007F0183, /* 44 Lt, hasUpper (subtract 1), hasLower (add 1), hasTitle, identifier start */ -0x009F0182, /* 45 Ll, hasUpper (subtract 2), hasTitle, identifier start */ -0x00000000, /* 46 unassigned */ -0x34970182, /* 47 Ll, hasUpper (subtract 210), identifier start */ -0x33970182, /* 48 Ll, hasUpper (subtract 206), identifier start */ -0x33570182, /* 49 Ll, hasUpper (subtract 205), identifier start */ -0x32970182, /* 50 Ll, hasUpper (subtract 202), identifier start */ -0x32D70182, /* 51 Ll, hasUpper (subtract 203), identifier start */ -0x33D70182, /* 52 Ll, hasUpper (subtract 207), identifier start */ -0x34570182, /* 53 Ll, hasUpper (subtract 209), identifier start */ -0x34D70182, /* 54 Ll, hasUpper (subtract 211), identifier start */ -0x35570182, /* 55 Ll, hasUpper (subtract 213), identifier start */ -0x36970182, /* 56 Ll, hasUpper (subtract 218), identifier start */ -0x36570182, /* 57 Ll, hasUpper (subtract 217), identifier start */ -0x36D70182, /* 58 Ll, hasUpper (subtract 219), identifier start */ -0x00070084, /* 59 Lm, identifier start */ -0x00030086, /* 60 Mn, identifier part */ -0x09A70181, /* 61 Lu, hasLower (add 38), identifier start */ -0x09670181, /* 62 Lu, hasLower (add 37), identifier start */ -0x10270181, /* 63 Lu, hasLower (add 64), identifier start */ -0x0FE70181, /* 64 Lu, hasLower (add 63), identifier start */ -0x09970182, /* 65 Ll, hasUpper (subtract 38), identifier start */ -0x09570182, /* 66 Ll, hasUpper (subtract 37), identifier start */ -0x10170182, /* 67 Ll, hasUpper (subtract 64), identifier start */ -0x0FD70182, /* 68 Ll, hasUpper (subtract 63), identifier start */ -0x0F970182, /* 69 Ll, hasUpper (subtract 62), identifier start */ -0x0E570182, /* 70 Ll, hasUpper (subtract 57), identifier start */ -0x0BD70182, /* 71 Ll, hasUpper (subtract 47), identifier start */ -0x0D970182, /* 72 Ll, hasUpper (subtract 54), identifier start */ -0x15970182, /* 73 Ll, hasUpper (subtract 86), identifier start */ -0x14170182, /* 74 Ll, hasUpper (subtract 80), identifier start */ -0x14270181, /* 75 Lu, hasLower (add 80), identifier start */ -0x0C270181, /* 76 Lu, hasLower (add 48), identifier start */ -0x0C170182, /* 77 Ll, hasUpper (subtract 48), identifier start */ -0x00034089, /* 78 Nd, identifier part, decimal 0 */ -0x00000087, /* 79 Me */ -0x00030088, /* 80 Mc, identifier part */ -0x00037489, /* 81 Nd, identifier part, decimal 26 */ -0x00005A0B, /* 82 No, decimal 13 */ -0x00006E0B, /* 83 No, decimal 23 */ -0x0000740B, /* 84 No, decimal 26 */ -0x0000000B, /* 85 No */ -0xFE170182, /* 86 Ll, hasUpper (subtract -8), identifier start */ -0xFE270181, /* 87 Lu, hasLower (add -8), identifier start */ -0xED970182, /* 88 Ll, hasUpper (subtract -74), identifier start */ -0xEA970182, /* 89 Ll, hasUpper (subtract -86), identifier start */ -0xE7170182, /* 90 Ll, hasUpper (subtract -100), identifier start */ -0xE0170182, /* 91 Ll, hasUpper (subtract -128), identifier start */ -0xE4170182, /* 92 Ll, hasUpper (subtract -112), identifier start */ -0xE0970182, /* 93 Ll, hasUpper (subtract -126), identifier start */ -0xFDD70182, /* 94 Ll, hasUpper (subtract -9), identifier start */ -0xEDA70181, /* 95 Lu, hasLower (add -74), identifier start */ -0xFDE70181, /* 96 Lu, hasLower (add -9), identifier start */ -0xEAA70181, /* 97 Lu, hasLower (add -86), identifier start */ -0xE7270181, /* 98 Lu, hasLower (add -100), identifier start */ -0xFE570182, /* 99 Ll, hasUpper (subtract -7), identifier start */ -0xE4270181, /* 100 Lu, hasLower (add -112), identifier start */ -0xFE670181, /* 101 Lu, hasLower (add -7), identifier start */ -0xE0270181, /* 102 Lu, hasLower (add -128), identifier start */ -0xE0A70181, /* 103 Lu, hasLower (add -126), identifier start */ -0x00010010, /* 104 Cf, ignorable */ -0x0004000D, /* 105 Zl, whitespace */ -0x0004000E, /* 106 Zp, whitespace */ -0x0000400B, /* 107 No, decimal 0 */ -0x0000440B, /* 108 No, decimal 2 */ -0x0427438A, /* 109 Nl, hasLower (add 16), identifier start, decimal 1 */ -0x0427818A, /* 110 Nl, hasLower (add 16), identifier start, strange */ -0x0417638A, /* 111 Nl, hasUpper (subtract 16), identifier start, decimal 17 */ -0x0417818A, /* 112 Nl, hasUpper (subtract 16), identifier start, strange */ -0x0007818A, /* 113 Nl, identifier start, strange */ -0x0000420B, /* 114 No, decimal 1 */ -0x0000720B, /* 115 No, decimal 25 */ -0x06A0001C, /* 116 So, hasLower (add 26) */ -0x0690001C, /* 117 So, hasUpper (subtract 26) */ -0x00006C0B, /* 118 No, decimal 22 */ -0x0000560B, /* 119 No, decimal 11 */ -0x0007738A, /* 120 Nl, identifier start, decimal 25 */ -0x0007418A, /* 121 Nl, identifier start, decimal 0 */ -0x00000013, /* 122 Cs */ -0x00000012 /* 123 Co */ -}; - -const jschar js_uriReservedPlusPound_ucstr[] = - {';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#', 0}; -const jschar js_uriUnescaped_ucstr[] = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '-', '_', '.', '!', '~', '*', '\'', '(', ')', 0}; - -#define URI_CHUNK 64U - -/* Concatenate jschars onto an unshared/newborn JSString. */ -static JSBool -AddCharsToURI(JSContext *cx, JSString *str, const jschar *chars, size_t length) -{ - size_t total; - - JS_ASSERT(!JSSTRING_IS_DEPENDENT(str)); - total = str->length + length + 1; - if (!str->chars || - JS_HOWMANY(total, URI_CHUNK) > JS_HOWMANY(str->length + 1, URI_CHUNK)) { - total = JS_ROUNDUP(total, URI_CHUNK); - str->chars = JS_realloc(cx, str->chars, total * sizeof(jschar)); - if (!str->chars) - return JS_FALSE; - } - js_strncpy(str->chars + str->length, chars, length); - str->length += length; - str->chars[str->length] = 0; - return JS_TRUE; -} - -/* - * ECMA 3, 15.1.3 URI Handling Function Properties - * - * The following are implementations of the algorithms - * given in the ECMA specification for the hidden functions - * 'Encode' and 'Decode'. - */ -static JSBool -Encode(JSContext *cx, JSString *str, const jschar *unescapedSet, - const jschar *unescapedSet2, jsval *rval) -{ - size_t length, j, k, L; - jschar *chars, c, c2; - uint32 v; - uint8 utf8buf[6]; - jschar hexBuf[4]; - static const char HexDigits[] = "0123456789ABCDEF"; /* NB: uppercase */ - JSString *R; - - length = JSSTRING_LENGTH(str); - if (length == 0) { - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - R = js_NewString(cx, NULL, 0, 0); - if (!R) - return JS_FALSE; - - hexBuf[0] = '%'; - hexBuf[3] = 0; - chars = JSSTRING_CHARS(str); - for (k = 0; k < length; k++) { - c = chars[k]; - if (js_strchr(unescapedSet, c) || - (unescapedSet2 && js_strchr(unescapedSet2, c))) { - if (!AddCharsToURI(cx, R, &c, 1)) - return JS_FALSE; - } else { - if ((c >= 0xDC00) && (c <= 0xDFFF)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_URI, NULL); - return JS_FALSE; - } - if (c < 0xD800 || c > 0xDBFF) { - v = c; - } else { - k++; - if (k == length) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_URI, NULL); - return JS_FALSE; - } - c2 = chars[k]; - if ((c2 < 0xDC00) || (c2 > 0xDFFF)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_URI, NULL); - return JS_FALSE; - } - v = ((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000; - } - L = js_OneUcs4ToUtf8Char(utf8buf, v); - for (j = 0; j < L; j++) { - hexBuf[1] = HexDigits[utf8buf[j] >> 4]; - hexBuf[2] = HexDigits[utf8buf[j] & 0xf]; - if (!AddCharsToURI(cx, R, hexBuf, 3)) - return JS_FALSE; - } - } - } - - /* - * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we - * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 - * more jschars than it needs. - */ - chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); - if (chars) - R->chars = chars; - *rval = STRING_TO_JSVAL(R); - return JS_TRUE; -} - -static JSBool -Decode(JSContext *cx, JSString *str, const jschar *reservedSet, jsval *rval) -{ - size_t length, start, k; - jschar *chars, c, H; - uint32 v; - jsuint B; - uint8 octets[6]; - JSString *R; - intN j, n; - - length = JSSTRING_LENGTH(str); - if (length == 0) { - *rval = STRING_TO_JSVAL(cx->runtime->emptyString); - return JS_TRUE; - } - - R = js_NewString(cx, NULL, 0, 0); - if (!R) - return JS_FALSE; - - chars = JSSTRING_CHARS(str); - for (k = 0; k < length; k++) { - c = chars[k]; - if (c == '%') { - start = k; - if ((k + 2) >= length) - goto bad; - if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) - goto bad; - B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); - k += 2; - if (!(B & 0x80)) { - c = (jschar)B; - } else { - n = 1; - while (B & (0x80 >> n)) - n++; - if (n == 1 || n > 6) - goto bad; - octets[0] = (uint8)B; - if (k + 3 * (n - 1) >= length) - goto bad; - for (j = 1; j < n; j++) { - k++; - if (chars[k] != '%') - goto bad; - if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) - goto bad; - B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); - if ((B & 0xC0) != 0x80) - goto bad; - k += 2; - octets[j] = (char)B; - } - v = Utf8ToOneUcs4Char(octets, n); - if (v >= 0x10000) { - v -= 0x10000; - if (v > 0xFFFFF) - goto bad; - c = (jschar)((v & 0x3FF) + 0xDC00); - H = (jschar)((v >> 10) + 0xD800); - if (!AddCharsToURI(cx, R, &H, 1)) - return JS_FALSE; - } else { - c = (jschar)v; - } - } - if (js_strchr(reservedSet, c)) { - if (!AddCharsToURI(cx, R, &chars[start], (k - start + 1))) - return JS_FALSE; - } else { - if (!AddCharsToURI(cx, R, &c, 1)) - return JS_FALSE; - } - } else { - if (!AddCharsToURI(cx, R, &c, 1)) - return JS_FALSE; - } - } - - /* - * Shrinking realloc can fail (e.g., with a BSD-style allocator), but we - * don't worry about that case here. Worst case, R hangs onto URI_CHUNK-1 - * more jschars than it needs. - */ - chars = (jschar *) JS_realloc(cx, R->chars, (R->length+1) * sizeof(jschar)); - if (chars) - R->chars = chars; - *rval = STRING_TO_JSVAL(R); - return JS_TRUE; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_URI); - return JS_FALSE; -} - -static JSBool -str_decodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Decode(cx, str, js_uriReservedPlusPound_ucstr, rval); -} - -static JSBool -str_decodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Decode(cx, str, js_empty_ucstr, rval); -} - -static JSBool -str_encodeURI(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Encode(cx, str, js_uriReservedPlusPound_ucstr, js_uriUnescaped_ucstr, - rval); -} - -static JSBool -str_encodeURI_Component(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = js_ValueToString(cx, argv[0]); - if (!str) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(str); - return Encode(cx, str, js_uriUnescaped_ucstr, NULL, rval); -} - -/* - * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at - * least 6 bytes long. Return the number of UTF-8 bytes of data written. - */ -int -js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char) -{ - int utf8Length = 1; - - JS_ASSERT(ucs4Char <= 0x7FFFFFFF); - if (ucs4Char < 0x80) { - *utf8Buffer = (uint8)ucs4Char; - } else { - int i; - uint32 a = ucs4Char >> 11; - utf8Length = 2; - while (a) { - a >>= 5; - utf8Length++; - } - i = utf8Length; - while (--i) { - utf8Buffer[i] = (uint8)((ucs4Char & 0x3F) | 0x80); - ucs4Char >>= 6; - } - *utf8Buffer = (uint8)(0x100 - (1 << (8-utf8Length)) + ucs4Char); - } - return utf8Length; -} - -/* - * Convert a utf8 character sequence into a UCS-4 character and return that - * character. It is assumed that the caller already checked that the sequence - * is valid. - */ -static uint32 -Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length) -{ - uint32 ucs4Char; - uint32 minucs4Char; - /* from Unicode 3.1, non-shortest form is illegal */ - static const uint32 minucs4Table[] = { - 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000 - }; - - JS_ASSERT(utf8Length >= 1 && utf8Length <= 6); - if (utf8Length == 1) { - ucs4Char = *utf8Buffer; - JS_ASSERT(!(ucs4Char & 0x80)); - } else { - JS_ASSERT((*utf8Buffer & (0x100 - (1 << (7-utf8Length)))) == - (0x100 - (1 << (8-utf8Length)))); - ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1); - minucs4Char = minucs4Table[utf8Length-2]; - while (--utf8Length) { - JS_ASSERT((*utf8Buffer & 0xC0) == 0x80); - ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F); - } - if (ucs4Char < minucs4Char || - ucs4Char == 0xFFFE || ucs4Char == 0xFFFF) { - ucs4Char = 0xFFFD; - } - } - return ucs4Char; -} diff --git a/spidermonkey/libjs/jsstr.h b/spidermonkey/libjs/jsstr.h deleted file mode 100644 index 708c69a..0000000 --- a/spidermonkey/libjs/jsstr.h +++ /dev/null @@ -1,500 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsstr_h___ -#define jsstr_h___ -/* - * JS string type implementation. - * - * A JS string is a counted array of unicode characters. To support handoff - * of API client memory, the chars are allocated separately from the length, - * necessitating a pointer after the count, to form a separately allocated - * string descriptor. String descriptors are GC'ed, while their chars are - * allocated from the malloc heap. - * - * When a string is treated as an object (by following it with . or []), the - * runtime wraps it with a JSObject whose valueOf method returns the unwrapped - * string descriptor. - */ -#include -#include "jspubtd.h" -#include "jsprvtd.h" -#include "jshash.h" - -JS_BEGIN_EXTERN_C - -/* - * The original GC-thing "string" type, a flat character string owned by its - * GC-thing descriptor. The chars member points to a vector having byte size - * (length + 1) * sizeof(jschar), terminated at index length by a zero jschar. - * The terminator is purely a backstop, in case the chars pointer flows out to - * native code that requires \u0000 termination. - * - * NB: Always use the JSSTRING_LENGTH and JSSTRING_CHARS accessor macros, - * unless you guard str->member uses with !JSSTRING_IS_DEPENDENT(str). - */ -struct JSString { - size_t length; - jschar *chars; -}; - -/* - * Overlay structure for a string that depends on another string's characters. - * Distinguished by the JSSTRFLAG_DEPENDENT bit being set in length. The base - * member may point to another dependent string if JSSTRING_CHARS has not been - * called yet. The length chars in a dependent string are stored starting at - * base->chars + start, and are not necessarily zero-terminated. If start is - * 0, it is not stored, length is a full size_t (minus the JSSTRFLAG_* bits in - * the high two positions), and the JSSTRFLAG_PREFIX flag is set. - */ -struct JSDependentString { - size_t length; - JSString *base; -}; - -/* Definitions for flags stored in the high order bits of JSString.length. */ -#define JSSTRFLAG_BITS 2 -#define JSSTRFLAG_SHIFT(flg) ((size_t)(flg) << JSSTRING_LENGTH_BITS) -#define JSSTRFLAG_MASK JSSTRFLAG_SHIFT(JS_BITMASK(JSSTRFLAG_BITS)) -#define JSSTRFLAG_DEPENDENT JSSTRFLAG_SHIFT(1) -#define JSSTRFLAG_PREFIX JSSTRFLAG_SHIFT(2) - -/* Universal JSString type inquiry and accessor macros. */ -#define JSSTRING_BIT(n) ((size_t)1 << (n)) -#define JSSTRING_BITMASK(n) (JSSTRING_BIT(n) - 1) -#define JSSTRING_HAS_FLAG(str,flg) ((str)->length & (flg)) -#define JSSTRING_IS_DEPENDENT(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_DEPENDENT) -#define JSSTRING_IS_PREFIX(str) JSSTRING_HAS_FLAG(str, JSSTRFLAG_PREFIX) -#define JSSTRING_CHARS(str) (JSSTRING_IS_DEPENDENT(str) \ - ? JSSTRDEP_CHARS(str) \ - : (str)->chars) -#define JSSTRING_LENGTH(str) (JSSTRING_IS_DEPENDENT(str) \ - ? JSSTRDEP_LENGTH(str) \ - : (str)->length) -#define JSSTRING_LENGTH_BITS (sizeof(size_t) * JS_BITS_PER_BYTE \ - - JSSTRFLAG_BITS) -#define JSSTRING_LENGTH_MASK JSSTRING_BITMASK(JSSTRING_LENGTH_BITS) - -/* Specific JSDependentString shift/mask accessor and mutator macros. */ -#define JSSTRDEP_START_BITS (JSSTRING_LENGTH_BITS-JSSTRDEP_LENGTH_BITS) -#define JSSTRDEP_START_SHIFT JSSTRDEP_LENGTH_BITS -#define JSSTRDEP_START_MASK JSSTRING_BITMASK(JSSTRDEP_START_BITS) -#define JSSTRDEP_LENGTH_BITS (JSSTRING_LENGTH_BITS / 2) -#define JSSTRDEP_LENGTH_MASK JSSTRING_BITMASK(JSSTRDEP_LENGTH_BITS) - -#define JSSTRDEP(str) ((JSDependentString *)(str)) -#define JSSTRDEP_START(str) (JSSTRING_IS_PREFIX(str) ? 0 \ - : ((JSSTRDEP(str)->length \ - >> JSSTRDEP_START_SHIFT) \ - & JSSTRDEP_START_MASK)) -#define JSSTRDEP_LENGTH(str) (JSSTRDEP(str)->length \ - & (JSSTRING_IS_PREFIX(str) \ - ? JSSTRING_LENGTH_MASK \ - : JSSTRDEP_LENGTH_MASK)) - -#define JSSTRDEP_SET_START_AND_LENGTH(str,off,len) \ - (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT \ - | ((off) << JSSTRDEP_START_SHIFT) \ - | (len)) -#define JSPREFIX_SET_LENGTH(str,len) \ - (JSSTRDEP(str)->length = JSSTRFLAG_DEPENDENT | JSSTRFLAG_PREFIX | (len)) - -#define JSSTRDEP_BASE(str) (JSSTRDEP(str)->base) -#define JSSTRDEP_SET_BASE(str,bstr) (JSSTRDEP(str)->base = (bstr)) -#define JSPREFIX_BASE(str) JSSTRDEP_BASE(str) -#define JSPREFIX_SET_BASE(str,bstr) JSSTRDEP_SET_BASE(str,bstr) - -#define JSSTRDEP_CHARS(str) \ - (JSSTRING_IS_DEPENDENT(JSSTRDEP_BASE(str)) \ - ? js_GetDependentStringChars(str) \ - : JSSTRDEP_BASE(str)->chars + JSSTRDEP_START(str)) - -extern size_t -js_MinimizeDependentStrings(JSString *str, int level, JSString **basep); - -extern jschar * -js_GetDependentStringChars(JSString *str); - -extern jschar * -js_GetStringChars(JSString *str); - -extern JSString * -js_ConcatStrings(JSContext *cx, JSString *left, JSString *right); - -extern const jschar * -js_UndependString(JSContext *cx, JSString *str); - -struct JSSubString { - size_t length; - const jschar *chars; -}; - -extern jschar js_empty_ucstr[]; -extern JSSubString js_EmptySubString; - -/* Unicode character attribute lookup tables. */ -extern const uint8 js_X[]; -extern const uint8 js_Y[]; -extern const uint32 js_A[]; - -/* Enumerated Unicode general category types. */ -typedef enum JSCharType { - JSCT_UNASSIGNED = 0, - JSCT_UPPERCASE_LETTER = 1, - JSCT_LOWERCASE_LETTER = 2, - JSCT_TITLECASE_LETTER = 3, - JSCT_MODIFIER_LETTER = 4, - JSCT_OTHER_LETTER = 5, - JSCT_NON_SPACING_MARK = 6, - JSCT_ENCLOSING_MARK = 7, - JSCT_COMBINING_SPACING_MARK = 8, - JSCT_DECIMAL_DIGIT_NUMBER = 9, - JSCT_LETTER_NUMBER = 10, - JSCT_OTHER_NUMBER = 11, - JSCT_SPACE_SEPARATOR = 12, - JSCT_LINE_SEPARATOR = 13, - JSCT_PARAGRAPH_SEPARATOR = 14, - JSCT_CONTROL = 15, - JSCT_FORMAT = 16, - JSCT_PRIVATE_USE = 18, - JSCT_SURROGATE = 19, - JSCT_DASH_PUNCTUATION = 20, - JSCT_START_PUNCTUATION = 21, - JSCT_END_PUNCTUATION = 22, - JSCT_CONNECTOR_PUNCTUATION = 23, - JSCT_OTHER_PUNCTUATION = 24, - JSCT_MATH_SYMBOL = 25, - JSCT_CURRENCY_SYMBOL = 26, - JSCT_MODIFIER_SYMBOL = 27, - JSCT_OTHER_SYMBOL = 28 -} JSCharType; - -/* Character classifying and mapping macros, based on java.lang.Character. */ -#define JS_CCODE(c) (js_A[js_Y[(js_X[(uint16)(c)>>6]<<6)|((c)&0x3F)]]) -#define JS_CTYPE(c) (JS_CCODE(c) & 0x1F) - -#define JS_ISALPHA(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER)) \ - >> JS_CTYPE(c)) & 1) - -#define JS_ISALNUM(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_DECIMAL_DIGIT_NUMBER)) \ - >> JS_CTYPE(c)) & 1) - -/* A unicode letter, suitable for use in an identifier. */ -#define JS_ISLETTER(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER)) \ - >> JS_CTYPE(c)) & 1) - -/* - * 'IdentifierPart' from ECMA grammar, is Unicode letter or combining mark or - * digit or connector punctuation. - */ -#define JS_ISIDPART(c) ((((1 << JSCT_UPPERCASE_LETTER) | \ - (1 << JSCT_LOWERCASE_LETTER) | \ - (1 << JSCT_TITLECASE_LETTER) | \ - (1 << JSCT_MODIFIER_LETTER) | \ - (1 << JSCT_OTHER_LETTER) | \ - (1 << JSCT_LETTER_NUMBER) | \ - (1 << JSCT_NON_SPACING_MARK) | \ - (1 << JSCT_COMBINING_SPACING_MARK) | \ - (1 << JSCT_DECIMAL_DIGIT_NUMBER) | \ - (1 << JSCT_CONNECTOR_PUNCTUATION)) \ - >> JS_CTYPE(c)) & 1) - -/* Unicode control-format characters, ignored in input */ -#define JS_ISFORMAT(c) (((1 << JSCT_FORMAT) >> JS_CTYPE(c)) & 1) - -/* - * Per ECMA-262 15.10.2.6, these characters are the only ones that make up a - * "word", as far as a RegExp is concerned. If we want a Unicode-friendlier - * definition of "word", we should rename this macro to something regexp-y. - */ -#define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) - -#define JS_ISIDSTART(c) (JS_ISLETTER(c) || (c) == '_' || (c) == '$') -#define JS_ISIDENT(c) (JS_ISIDPART(c) || (c) == '_' || (c) == '$') - -#define JS_ISXMLSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || \ - (c) == '\n') -#define JS_ISXMLNSSTART(c) ((JS_CCODE(c) & 0x00000100) || (c) == '_') -#define JS_ISXMLNS(c) ((JS_CCODE(c) & 0x00000080) || (c) == '.' || \ - (c) == '-' || (c) == '_') -#define JS_ISXMLNAMESTART(c) (JS_ISXMLNSSTART(c) || (c) == ':') -#define JS_ISXMLNAME(c) (JS_ISXMLNS(c) || (c) == ':') - -#define JS_ISDIGIT(c) (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER) - -/* XXXbe unify on A/X/Y tbls, avoid ctype.h? */ -/* XXXbe fs, etc. ? */ -#define JS_ISSPACE(c) ((JS_CCODE(c) & 0x00070000) == 0x00040000) -#define JS_ISPRINT(c) ((c) < 128 && isprint(c)) - -#define JS_ISUPPER(c) (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER) -#define JS_ISLOWER(c) (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER) - -#define JS_TOUPPER(c) ((jschar) ((JS_CCODE(c) & 0x00100000) \ - ? (c) - ((int32)JS_CCODE(c) >> 22) \ - : (c))) -#define JS_TOLOWER(c) ((jschar) ((JS_CCODE(c) & 0x00200000) \ - ? (c) + ((int32)JS_CCODE(c) >> 22) \ - : (c))) - -/* - * Shorthands for ASCII (7-bit) decimal and hex conversion. - * Manually inline isdigit for performance; MSVC doesn't do this for us. - */ -#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) -#define JS7_UNDEC(c) ((c) - '0') -#define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) -#define JS7_UNHEX(c) (uintN)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') -#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) - -/* Initialize per-runtime string state for the first context in the runtime. */ -extern JSBool -js_InitRuntimeStringState(JSContext *cx); - -extern void -js_FinishRuntimeStringState(JSContext *cx); - -extern void -js_FinishDeflatedStringCache(JSRuntime *rt); - -/* Initialize the String class, returning its prototype object. */ -extern JSClass js_StringClass; - -extern JSObject * -js_InitStringClass(JSContext *cx, JSObject *obj); - -extern const char js_escape_str[]; -extern const char js_unescape_str[]; -extern const char js_uneval_str[]; -extern const char js_decodeURI_str[]; -extern const char js_encodeURI_str[]; -extern const char js_decodeURIComponent_str[]; -extern const char js_encodeURIComponent_str[]; - -/* GC-allocate a string descriptor for the given malloc-allocated chars. */ -extern JSString * -js_NewString(JSContext *cx, jschar *chars, size_t length, uintN gcflag); - -extern JSString * -js_NewDependentString(JSContext *cx, JSString *base, size_t start, - size_t length, uintN gcflag); - -/* Copy a counted string and GC-allocate a descriptor for it. */ -extern JSString * -js_NewStringCopyN(JSContext *cx, const jschar *s, size_t n, uintN gcflag); - -/* Copy a C string and GC-allocate a descriptor for it. */ -extern JSString * -js_NewStringCopyZ(JSContext *cx, const jschar *s, uintN gcflag); - -/* Free the chars held by str when it is finalized by the GC. */ -extern void -js_FinalizeString(JSContext *cx, JSString *str); - -extern void -js_FinalizeStringRT(JSRuntime *rt, JSString *str); - -/* Wrap a string value in a String object. */ -extern JSObject * -js_StringToObject(JSContext *cx, JSString *str); - -/* - * Convert a value to a printable C string. - */ -typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v); - -extern JS_FRIEND_API(const char *) -js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun); - -#define js_ValueToPrintableString(cx,v) \ - js_ValueToPrintable(cx, v, js_ValueToString) - -#define js_ValueToPrintableSource(cx,v) \ - js_ValueToPrintable(cx, v, js_ValueToSource) - -/* - * Convert a value to a string, returning null after reporting an error, - * otherwise returning a new string reference. - */ -extern JS_FRIEND_API(JSString *) -js_ValueToString(JSContext *cx, jsval v); - -/* - * Convert a value to its source expression, returning null after reporting - * an error, otherwise returning a new string reference. - */ -extern JS_FRIEND_API(JSString *) -js_ValueToSource(JSContext *cx, jsval v); - -#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */ -/* - * Compute a hash function from str. - */ -extern JSHashNumber -js_HashString(JSString *str); -#endif - -/* - * Return less than, equal to, or greater than zero depending on whether - * str1 is less than, equal to, or greater than str2. - */ -extern intN -js_CompareStrings(JSString *str1, JSString *str2); - -/* - * Test if strings are equal. - */ -extern JSBool -js_EqualStrings(JSString *str1, JSString *str2); - -/* - * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. - * The patlen argument must be positive and no greater than BMH_PATLEN_MAX. - * The start argument tells where in text to begin the search. - * - * Return the index of pat in text, or -1 if not found. - */ -#define BMH_CHARSET_SIZE 256 /* ISO-Latin-1 */ -#define BMH_PATLEN_MAX 255 /* skip table element is uint8 */ - -#define BMH_BAD_PATTERN (-2) /* return value if pat is not ISO-Latin-1 */ - -extern jsint -js_BoyerMooreHorspool(const jschar *text, jsint textlen, - const jschar *pat, jsint patlen, - jsint start); - -extern size_t -js_strlen(const jschar *s); - -extern jschar * -js_strchr(const jschar *s, jschar c); - -extern jschar * -js_strchr_limit(const jschar *s, jschar c, const jschar *limit); - -#define js_strncpy(t, s, n) memcpy((t), (s), (n) * sizeof(jschar)) - -/* - * Return s advanced past any Unicode white space characters. - */ -extern const jschar * -js_SkipWhiteSpace(const jschar *s); - -/* - * Inflate bytes to JS chars and vice versa. Report out of memory via cx - * and return null on error, otherwise return the jschar or byte vector that - * was JS_malloc'ed. length is updated with the length of the new string in jschars. - */ -extern jschar * -js_InflateString(JSContext *cx, const char *bytes, size_t *length); - -extern char * -js_DeflateString(JSContext *cx, const jschar *chars, size_t length); - -/* - * Inflate bytes to JS chars into a buffer. - * 'chars' must be large enough for 'length' jschars. - * The buffer is NOT null-terminated. - * cx may be NULL, which means no errors are thrown. - * The destination length needs to be initialized with the buffer size, takes - * the number of chars moved. - */ -extern JSBool -js_InflateStringToBuffer(JSContext* cx, const char *bytes, size_t length, - jschar *chars, size_t* charsLength); - -/* - * Deflate JS chars to bytes into a buffer. - * 'bytes' must be large enough for 'length chars. - * The buffer is NOT null-terminated. - * cx may be NULL, which means no errors are thrown. - * The destination length needs to be initialized with the buffer size, takes - * the number of bytes moved. - */ -extern JSBool -js_DeflateStringToBuffer(JSContext* cx, const jschar *chars, - size_t charsLength, char *bytes, size_t* length); - -/* - * Associate bytes with str in the deflated string cache, returning true on - * successful association, false on out of memory. - */ -extern JSBool -js_SetStringBytes(JSRuntime *rt, JSString *str, char *bytes, size_t length); - -/* - * Find or create a deflated string cache entry for str that contains its - * characters chopped from Unicode code points into bytes. - */ -extern char * -js_GetStringBytes(JSRuntime *rt, JSString *str); - -/* Remove a deflated string cache entry associated with str if any. */ -extern void -js_PurgeDeflatedStringCache(JSRuntime *rt, JSString *str); - -JSBool -js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval); - -/* - * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at - * least 6 bytes long. Return the number of UTF-8 bytes of data written. - */ -extern int -js_OneUcs4ToUtf8Char(uint8 *utf8Buffer, uint32 ucs4Char); - -JS_END_EXTERN_C - -#endif /* jsstr_h___ */ diff --git a/spidermonkey/libjs/jstypes.h b/spidermonkey/libjs/jstypes.h deleted file mode 100644 index 8aca929..0000000 --- a/spidermonkey/libjs/jstypes.h +++ /dev/null @@ -1,464 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* -** File: jstypes.h -** Description: Definitions of NSPR's basic types -** -** Prototypes and macros used to make up for deficiencies in ANSI environments -** that we have found. -** -** Since we do not wrap and all the other standard headers, authors -** of portable code will not know in general that they need these definitions. -** Instead of requiring these authors to find the dependent uses in their code -** and take the following steps only in those C files, we take steps once here -** for all C files. -**/ - -#ifndef jstypes_h___ -#define jstypes_h___ - -#include - -/*********************************************************************** -** MACROS: JS_EXTERN_API -** JS_EXPORT_API -** DESCRIPTION: -** These are only for externally visible routines and globals. For -** internal routines, just use "extern" for type checking and that -** will not export internal cross-file or forward-declared symbols. -** Define a macro for declaring procedures return types. We use this to -** deal with windoze specific type hackery for DLL definitions. Use -** JS_EXTERN_API when the prototype for the method is declared. Use -** JS_EXPORT_API for the implementation of the method. -** -** Example: -** in dowhim.h -** JS_EXTERN_API( void ) DoWhatIMean( void ); -** in dowhim.c -** JS_EXPORT_API( void ) DoWhatIMean( void ) { return; } -** -** -***********************************************************************/ -#ifdef WIN32 -/* These also work for __MWERKS__ */ -#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_API(__type) __declspec(dllexport) __type -#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - -#elif defined(XP_OS2) && defined(__declspec) - -#define JS_EXTERN_API(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_API(__type) __declspec(dllexport) __type -#define JS_EXTERN_DATA(__type) extern __declspec(dllexport) __type -#define JS_EXPORT_DATA(__type) __declspec(dllexport) __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - -#elif defined(WIN16) - -#ifdef _WINDLL -#define JS_EXTERN_API(__type) extern __type _cdecl _export _loadds -#define JS_EXPORT_API(__type) __type _cdecl _export _loadds -#define JS_EXTERN_DATA(__type) extern __type _export -#define JS_EXPORT_DATA(__type) __type _export - -#define JS_DLL_CALLBACK __cdecl __loadds -#define JS_STATIC_DLL_CALLBACK(__x) static __x CALLBACK - -#else /* this must be .EXE */ -#define JS_EXTERN_API(__type) extern __type _cdecl _export -#define JS_EXPORT_API(__type) __type _cdecl _export -#define JS_EXTERN_DATA(__type) extern __type _export -#define JS_EXPORT_DATA(__type) __type _export - -#define JS_DLL_CALLBACK __cdecl __loadds -#define JS_STATIC_DLL_CALLBACK(__x) __x JS_DLL_CALLBACK -#endif /* _WINDLL */ - -#else /* Unix */ - -#ifdef HAVE_VISIBILITY_ATTRIBUTE -#define JS_EXTERNAL_VIS __attribute__((visibility ("default"))) -#else -#define JS_EXTERNAL_VIS -#endif - -#define JS_EXTERN_API(__type) extern JS_EXTERNAL_VIS __type -#define JS_EXPORT_API(__type) JS_EXTERNAL_VIS __type -#define JS_EXTERN_DATA(__type) extern JS_EXTERNAL_VIS __type -#define JS_EXPORT_DATA(__type) JS_EXTERNAL_VIS __type - -#define JS_DLL_CALLBACK -#define JS_STATIC_DLL_CALLBACK(__x) static __x - -#endif - -#ifdef _WIN32 -# if defined(__MWERKS__) || defined(__GNUC__) -# define JS_IMPORT_API(__x) __x -# else -# define JS_IMPORT_API(__x) __declspec(dllimport) __x -# endif -#elif defined(XP_OS2) && defined(__declspec) -# define JS_IMPORT_API(__x) __declspec(dllimport) __x -#else -# define JS_IMPORT_API(__x) JS_EXPORT_API (__x) -#endif - -#if defined(_WIN32) && !defined(__MWERKS__) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#elif defined(XP_OS2) && defined(__declspec) -# define JS_IMPORT_DATA(__x) __declspec(dllimport) __x -#else -# define JS_IMPORT_DATA(__x) JS_EXPORT_DATA (__x) -#endif - -/* - * The linkage of JS API functions differs depending on whether the file is - * used within the JS library or not. Any source file within the JS - * interpreter should define EXPORT_JS_API whereas any client of the library - * should not. - */ -#ifdef EXPORT_JS_API -#define JS_PUBLIC_API(t) JS_EXPORT_API(t) -#define JS_PUBLIC_DATA(t) JS_EXPORT_DATA(t) -#else -#define JS_PUBLIC_API(t) JS_IMPORT_API(t) -#define JS_PUBLIC_DATA(t) JS_IMPORT_DATA(t) -#endif - -#define JS_FRIEND_API(t) JS_PUBLIC_API(t) -#define JS_FRIEND_DATA(t) JS_PUBLIC_DATA(t) - -#ifdef _WIN32 -# define JS_INLINE __inline -#elif defined(__GNUC__) -# define JS_INLINE -#else -# define JS_INLINE -#endif - -/*********************************************************************** -** MACROS: JS_BEGIN_MACRO -** JS_END_MACRO -** DESCRIPTION: -** Macro body brackets so that macros with compound statement definitions -** behave syntactically more like functions when called. -***********************************************************************/ -#define JS_BEGIN_MACRO do { -#define JS_END_MACRO } while (0) - -/*********************************************************************** -** MACROS: JS_BEGIN_EXTERN_C -** JS_END_EXTERN_C -** DESCRIPTION: -** Macro shorthands for conditional C++ extern block delimiters. -***********************************************************************/ -#ifdef __cplusplus -#define JS_BEGIN_EXTERN_C extern "C" { -#define JS_END_EXTERN_C } -#else -#define JS_BEGIN_EXTERN_C -#define JS_END_EXTERN_C -#endif - -/*********************************************************************** -** MACROS: JS_BIT -** JS_BITMASK -** DESCRIPTION: -** Bit masking macros. XXX n must be <= 31 to be portable -***********************************************************************/ -#define JS_BIT(n) ((JSUint32)1 << (n)) -#define JS_BITMASK(n) (JS_BIT(n) - 1) - -/*********************************************************************** -** MACROS: JS_PTR_TO_INT32 -** JS_PTR_TO_UINT32 -** JS_INT32_TO_PTR -** JS_UINT32_TO_PTR -** DESCRIPTION: -** Integer to pointer and pointer to integer conversion macros. -***********************************************************************/ -#define JS_PTR_TO_INT32(x) ((jsint)((char *)(x) - (char *)0)) -#define JS_PTR_TO_UINT32(x) ((jsuint)((char *)(x) - (char *)0)) -#define JS_INT32_TO_PTR(x) ((void *)((char *)0 + (jsint)(x))) -#define JS_UINT32_TO_PTR(x) ((void *)((char *)0 + (jsuint)(x))) - -/*********************************************************************** -** MACROS: JS_HOWMANY -** JS_ROUNDUP -** JS_MIN -** JS_MAX -** DESCRIPTION: -** Commonly used macros for operations on compatible types. -***********************************************************************/ -#define JS_HOWMANY(x,y) (((x)+(y)-1)/(y)) -#define JS_ROUNDUP(x,y) (JS_HOWMANY(x,y)*(y)) -#define JS_MIN(x,y) ((x)<(y)?(x):(y)) -#define JS_MAX(x,y) ((x)>(y)?(x):(y)) - -#if (defined(XP_WIN) && !defined(CROSS_COMPILE)) || defined (WINCE) -# include "jscpucfg.h" /* Use standard Mac or Windows configuration */ -#elif defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_OS2) || defined(CROSS_COMPILE) -# include "jsautocfg.h" /* Use auto-detected configuration */ -# include "jsosdep.h" /* ...and platform-specific flags */ -#else -# error "Must define one of XP_BEOS, XP_OS2, XP_WIN or XP_UNIX" -#endif - -JS_BEGIN_EXTERN_C - -/************************************************************************ -** TYPES: JSUint8 -** JSInt8 -** DESCRIPTION: -** The int8 types are known to be 8 bits each. There is no type that -** is equivalent to a plain "char". -************************************************************************/ -#if JS_BYTES_PER_BYTE == 1 -typedef unsigned char JSUint8; -typedef signed char JSInt8; -#else -#error No suitable type for JSInt8/JSUint8 -#endif - -/************************************************************************ -** TYPES: JSUint16 -** JSInt16 -** DESCRIPTION: -** The int16 types are known to be 16 bits each. -************************************************************************/ -#if JS_BYTES_PER_SHORT == 2 -typedef unsigned short JSUint16; -typedef short JSInt16; -#else -#error No suitable type for JSInt16/JSUint16 -#endif - -/************************************************************************ -** TYPES: JSUint32 -** JSInt32 -** DESCRIPTION: -** The int32 types are known to be 32 bits each. -************************************************************************/ -#if JS_BYTES_PER_INT == 4 -typedef unsigned int JSUint32; -typedef int JSInt32; -#define JS_INT32(x) x -#define JS_UINT32(x) x ## U -#elif JS_BYTES_PER_LONG == 4 -typedef unsigned long JSUint32; -typedef long JSInt32; -#define JS_INT32(x) x ## L -#define JS_UINT32(x) x ## UL -#else -#error No suitable type for JSInt32/JSUint32 -#endif - -/************************************************************************ -** TYPES: JSUint64 -** JSInt64 -** DESCRIPTION: -** The int64 types are known to be 64 bits each. Care must be used when -** declaring variables of type JSUint64 or JSInt64. Different hardware -** architectures and even different compilers have varying support for -** 64 bit values. The only guaranteed portability requires the use of -** the JSLL_ macros (see jslong.h). -************************************************************************/ -#ifdef JS_HAVE_LONG_LONG -#if JS_BYTES_PER_LONG == 8 -typedef long JSInt64; -typedef unsigned long JSUint64; -#elif defined(WIN16) -typedef __int64 JSInt64; -typedef unsigned __int64 JSUint64; -#elif defined(WIN32) && !defined(__GNUC__) -typedef __int64 JSInt64; -typedef unsigned __int64 JSUint64; -#else -typedef long long JSInt64; -typedef unsigned long long JSUint64; -#endif /* JS_BYTES_PER_LONG == 8 */ -#else /* !JS_HAVE_LONG_LONG */ -typedef struct { -#ifdef IS_LITTLE_ENDIAN - JSUint32 lo, hi; -#else - JSUint32 hi, lo; -#endif -} JSInt64; -typedef JSInt64 JSUint64; -#endif /* !JS_HAVE_LONG_LONG */ - -/************************************************************************ -** TYPES: JSUintn -** JSIntn -** DESCRIPTION: -** The JSIntn types are most appropriate for automatic variables. They are -** guaranteed to be at least 16 bits, though various architectures may -** define them to be wider (e.g., 32 or even 64 bits). These types are -** never valid for fields of a structure. -************************************************************************/ -#if JS_BYTES_PER_INT >= 2 -typedef int JSIntn; -typedef unsigned int JSUintn; -#else -#error 'sizeof(int)' not sufficient for platform use -#endif - -/************************************************************************ -** TYPES: JSFloat64 -** DESCRIPTION: -** NSPR's floating point type is always 64 bits. -************************************************************************/ -typedef double JSFloat64; - -/************************************************************************ -** TYPES: JSSize -** DESCRIPTION: -** A type for representing the size of objects. -************************************************************************/ -typedef size_t JSSize; - -/************************************************************************ -** TYPES: JSPtrDiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -typedef ptrdiff_t JSPtrdiff; - -/************************************************************************ -** TYPES: JSUptrdiff -** DESCRIPTION: -** A type for pointer difference. Variables of this type are suitable -** for storing a pointer or pointer sutraction. -************************************************************************/ -#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 -typedef JSUint64 JSUptrdiff; -#else -typedef unsigned long JSUptrdiff; -#endif - -/************************************************************************ -** TYPES: JSBool -** DESCRIPTION: -** Use JSBool for variables and parameter types. Use JS_FALSE and JS_TRUE -** for clarity of target type in assignments and actual arguments. Use -** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans -** just as you would C int-valued conditions. -************************************************************************/ -typedef JSIntn JSBool; -#define JS_TRUE (JSIntn)1 -#define JS_FALSE (JSIntn)0 - -/************************************************************************ -** TYPES: JSPackedBool -** DESCRIPTION: -** Use JSPackedBool within structs where bitfields are not desireable -** but minimum and consistent overhead matters. -************************************************************************/ -typedef JSUint8 JSPackedBool; - -/* -** A JSWord is an integer that is the same size as a void* -*/ -#if JS_BYTES_PER_WORD == 8 && JS_BYTES_PER_LONG != 8 -typedef JSInt64 JSWord; -typedef JSUint64 JSUword; -#else -typedef long JSWord; -typedef unsigned long JSUword; -#endif - -#include "jsotypes.h" - -/*********************************************************************** -** MACROS: JS_LIKELY -** JS_UNLIKELY -** DESCRIPTION: -** These macros allow you to give a hint to the compiler about branch -** probability so that it can better optimize. Use them like this: -** -** if (JS_LIKELY(v == 1)) { -** ... expected code path ... -** } -** -** if (JS_UNLIKELY(v == 0)) { -** ... non-expected code path ... -** } -** -***********************************************************************/ -#if defined(__GNUC__) && (__GNUC__ > 2) -#define JS_LIKELY(x) (__builtin_expect((x), 1)) -#define JS_UNLIKELY(x) (__builtin_expect((x), 0)) -#else -#define JS_LIKELY(x) (x) -#define JS_UNLIKELY(x) (x) -#endif - -/*********************************************************************** -** MACROS: JS_ARRAY_LENGTH -** JS_ARRAY_END -** DESCRIPTION: -** Macros to get the number of elements and the pointer to one past the -** last element of a C array. Use them like this: -** -** jschar buf[10], *s; -** JSString *str; -** ... -** for (s = buf; s != JS_ARRAY_END(buf); ++s) *s = ...; -** ... -** str = JS_NewStringCopyN(cx, buf, JS_ARRAY_LENGTH(buf)); -** ... -** -***********************************************************************/ - -#define JS_ARRAY_LENGTH(array) (sizeof (array) / sizeof (array)[0]) -#define JS_ARRAY_END(array) ((array) + JS_ARRAY_LENGTH(array)) - -JS_END_EXTERN_C - -#endif /* jstypes_h___ */ diff --git a/spidermonkey/libjs/jsutil.c b/spidermonkey/libjs/jsutil.c deleted file mode 100644 index 1bb9f93..0000000 --- a/spidermonkey/libjs/jsutil.c +++ /dev/null @@ -1,198 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * IBM Corp. - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * PR assertion checker. - */ -#include "jsstddef.h" -#include -#include -#include "jstypes.h" -#include "jsutil.h" - -#ifdef WIN32 -# include -#endif - -JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) -{ - fprintf(stderr, "Assertion failure: %s, at %s:%d\n", s, file, ln); -#if defined(WIN32) - DebugBreak(); - exit(3); -#elif defined(XP_OS2) || (defined(__GNUC__) && defined(__i386)) - asm("int $3"); -#endif - abort(); -} - -#if defined DEBUG_notme && defined XP_UNIX - -#define __USE_GNU 1 -#include -#include -#include "jshash.h" -#include "jsprf.h" - -JSCallsite js_calltree_root = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL}; - -static JSCallsite * -CallTree(void **bp) -{ - void **bpup, **bpdown, *pc; - JSCallsite *parent, *site, **csp; - Dl_info info; - int ok, offset; - const char *symbol; - char *method; - - /* Reverse the stack frame list to avoid recursion. */ - bpup = NULL; - for (;;) { - bpdown = (void**) bp[0]; - bp[0] = (void*) bpup; - if ((void**) bpdown[0] < bpdown) - break; - bpup = bp; - bp = bpdown; - } - - /* Reverse the stack again, finding and building a path in the tree. */ - parent = &js_calltree_root; - do { - bpup = (void**) bp[0]; - bp[0] = (void*) bpdown; - pc = bp[1]; - - csp = &parent->kids; - while ((site = *csp) != NULL) { - if (site->pc == pc) { - /* Put the most recently used site at the front of siblings. */ - *csp = site->siblings; - site->siblings = parent->kids; - parent->kids = site; - - /* Site already built -- go up the stack. */ - goto upward; - } - csp = &site->siblings; - } - - /* Check for recursion: see if pc is on our ancestor line. */ - for (site = parent; site; site = site->parent) { - if (site->pc == pc) - goto upward; - } - - /* - * Not in tree at all: let's find our symbolic callsite info. - * XXX static syms are masked by nearest lower global - */ - info.dli_fname = info.dli_sname = NULL; - ok = dladdr(pc, &info); - if (ok < 0) { - fprintf(stderr, "dladdr failed!\n"); - return NULL; - } - -/* XXXbe sub 0x08040000? or something, see dbaron bug with tenthumbs comment */ - symbol = info.dli_sname; - offset = (char*)pc - (char*)info.dli_fbase; - method = symbol - ? strdup(symbol) - : JS_smprintf("%s+%X", - info.dli_fname ? info.dli_fname : "main", - offset); - if (!method) - return NULL; - - /* Create a new callsite record. */ - site = (JSCallsite *) malloc(sizeof(JSCallsite)); - if (!site) - return NULL; - - /* Insert the new site into the tree. */ - site->pc = pc; - site->name = method; - site->library = info.dli_fname; - site->offset = offset; - site->parent = parent; - site->siblings = parent->kids; - parent->kids = site; - site->kids = NULL; - - upward: - parent = site; - bpdown = bp; - bp = bpup; - } while (bp); - - return site; -} - -JSCallsite * -JS_Backtrace(int skip) -{ - void **bp, **bpdown; - - /* Stack walking code adapted from Kipp's "leaky". */ -#if defined(__i386) - __asm__( "movl %%ebp, %0" : "=g"(bp)); -#elif defined(__x86_64__) - __asm__( "movq %%rbp, %0" : "=g"(bp)); -#else - /* - * It would be nice if this worked uniformly, but at least on i386 and - * x86_64, it stopped working with gcc 4.1, because it points to the - * end of the saved registers instead of the start. - */ - bp = (void**) __builtin_frame_address(0); -#endif - while (--skip >= 0) { - bpdown = (void**) *bp++; - if (bpdown < bp) - break; - bp = bpdown; - } - - return CallTree(bp); -} - -#endif /* DEBUG_notme && XP_UNIX */ diff --git a/spidermonkey/libjs/jsutil.h b/spidermonkey/libjs/jsutil.h deleted file mode 100644 index efcb614..0000000 --- a/spidermonkey/libjs/jsutil.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * PR assertion checker. - */ - -#ifndef jsutil_h___ -#define jsutil_h___ - -JS_BEGIN_EXTERN_C - -#ifdef DEBUG - -extern JS_PUBLIC_API(void) -JS_Assert(const char *s, const char *file, JSIntn ln); -#define JS_ASSERT(_expr) \ - ((_expr)?((void)0):JS_Assert(# _expr,__FILE__,__LINE__)) - -#define JS_NOT_REACHED(_reasonStr) \ - JS_Assert(_reasonStr,__FILE__,__LINE__) - -#else - -#define JS_ASSERT(expr) ((void) 0) -#define JS_NOT_REACHED(reasonStr) - -#endif /* defined(DEBUG) */ - -/* - * Compile-time assert. "condition" must be a constant expression. - * The macro should be used only once per source line in places where - * a "typedef" declaration is allowed. - */ -#define JS_STATIC_ASSERT(condition) \ - JS_STATIC_ASSERT_IMPL(condition, __LINE__) -#define JS_STATIC_ASSERT_IMPL(condition, line) \ - JS_STATIC_ASSERT_IMPL2(condition, line) -#define JS_STATIC_ASSERT_IMPL2(condition, line) \ - typedef int js_static_assert_line_##line[(condition) ? 1 : -1] - -/* -** Abort the process in a non-graceful manner. This will cause a core file, -** call to the debugger or other moral equivalent as well as causing the -** entire process to stop. -*/ -extern JS_PUBLIC_API(void) JS_Abort(void); - -#ifdef XP_UNIX - -typedef struct JSCallsite JSCallsite; - -struct JSCallsite { - uint32 pc; - char *name; - const char *library; - int offset; - JSCallsite *parent; - JSCallsite *siblings; - JSCallsite *kids; - void *handy; -}; - -extern JSCallsite *JS_Backtrace(int skip); - -#endif - -JS_END_EXTERN_C - -#endif /* jsutil_h___ */ diff --git a/spidermonkey/libjs/jsxdrapi.c b/spidermonkey/libjs/jsxdrapi.c deleted file mode 100644 index 2855c60..0000000 --- a/spidermonkey/libjs/jsxdrapi.c +++ /dev/null @@ -1,835 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ -#include "jsstddef.h" -#include "jsconfig.h" - -#if JS_HAS_XDR - -#include -#include "jstypes.h" -#include "jsutil.h" /* Added by JSIFY */ -#include "jsdhash.h" -#include "jsprf.h" -#include "jsapi.h" -#include "jscntxt.h" -#include "jsnum.h" -#include "jsobj.h" /* js_XDRObject */ -#include "jsscript.h" /* js_XDRScript */ -#include "jsstr.h" -#include "jsxdrapi.h" - -#ifdef DEBUG -#define DBG(x) x -#else -#define DBG(x) ((void)0) -#endif - -typedef struct JSXDRMemState { - JSXDRState state; - char *base; - uint32 count; - uint32 limit; -} JSXDRMemState; - -#define MEM_BLOCK 8192 -#define MEM_PRIV(xdr) ((JSXDRMemState *)(xdr)) - -#define MEM_BASE(xdr) (MEM_PRIV(xdr)->base) -#define MEM_COUNT(xdr) (MEM_PRIV(xdr)->count) -#define MEM_LIMIT(xdr) (MEM_PRIV(xdr)->limit) - -#define MEM_LEFT(xdr, bytes) \ - JS_BEGIN_MACRO \ - if ((xdr)->mode == JSXDR_DECODE && \ - MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - JS_ReportErrorNumber((xdr)->cx, js_GetErrorMessage, NULL, \ - JSMSG_END_OF_DATA); \ - return 0; \ - } \ - JS_END_MACRO - -#define MEM_NEED(xdr, bytes) \ - JS_BEGIN_MACRO \ - if ((xdr)->mode == JSXDR_ENCODE) { \ - if (MEM_LIMIT(xdr) && \ - MEM_COUNT(xdr) + bytes > MEM_LIMIT(xdr)) { \ - uint32 limit_ = JS_ROUNDUP(MEM_COUNT(xdr) + bytes, MEM_BLOCK);\ - void *data_ = JS_realloc((xdr)->cx, MEM_BASE(xdr), limit_); \ - if (!data_) \ - return 0; \ - MEM_BASE(xdr) = data_; \ - MEM_LIMIT(xdr) = limit_; \ - } \ - } else { \ - MEM_LEFT(xdr, bytes); \ - } \ - JS_END_MACRO - -#define MEM_DATA(xdr) ((void *)(MEM_BASE(xdr) + MEM_COUNT(xdr))) -#define MEM_INCR(xdr,bytes) (MEM_COUNT(xdr) += (bytes)) - -static JSBool -mem_get32(JSXDRState *xdr, uint32 *lp) -{ - MEM_LEFT(xdr, 4); - *lp = *(uint32 *)MEM_DATA(xdr); - MEM_INCR(xdr, 4); - return JS_TRUE; -} - -static JSBool -mem_set32(JSXDRState *xdr, uint32 *lp) -{ - MEM_NEED(xdr, 4); - *(uint32 *)MEM_DATA(xdr) = *lp; - MEM_INCR(xdr, 4); - return JS_TRUE; -} - -static JSBool -mem_getbytes(JSXDRState *xdr, char *bytes, uint32 len) -{ - MEM_LEFT(xdr, len); - memcpy(bytes, MEM_DATA(xdr), len); - MEM_INCR(xdr, len); - return JS_TRUE; -} - -static JSBool -mem_setbytes(JSXDRState *xdr, char *bytes, uint32 len) -{ - MEM_NEED(xdr, len); - memcpy(MEM_DATA(xdr), bytes, len); - MEM_INCR(xdr, len); - return JS_TRUE; -} - -static void * -mem_raw(JSXDRState *xdr, uint32 len) -{ - void *data; - if (xdr->mode == JSXDR_ENCODE) { - MEM_NEED(xdr, len); - } else if (xdr->mode == JSXDR_DECODE) { - MEM_LEFT(xdr, len); - } - data = MEM_DATA(xdr); - MEM_INCR(xdr, len); - return data; -} - -static JSBool -mem_seek(JSXDRState *xdr, int32 offset, JSXDRWhence whence) -{ - switch (whence) { - case JSXDR_SEEK_CUR: - if ((int32)MEM_COUNT(xdr) + offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_START); - return JS_FALSE; - } - if (offset > 0) - MEM_NEED(xdr, offset); - MEM_COUNT(xdr) += offset; - return JS_TRUE; - case JSXDR_SEEK_SET: - if (offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_START); - return JS_FALSE; - } - if (xdr->mode == JSXDR_ENCODE) { - if ((uint32)offset > MEM_COUNT(xdr)) - MEM_NEED(xdr, offset - MEM_COUNT(xdr)); - MEM_COUNT(xdr) = offset; - } else { - if ((uint32)offset > MEM_LIMIT(xdr)) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_SEEK_BEYOND_END); - return JS_FALSE; - } - MEM_COUNT(xdr) = offset; - } - return JS_TRUE; - case JSXDR_SEEK_END: - if (offset >= 0 || - xdr->mode == JSXDR_ENCODE || - (int32)MEM_LIMIT(xdr) + offset < 0) { - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_END_SEEK); - return JS_FALSE; - } - MEM_COUNT(xdr) = MEM_LIMIT(xdr) + offset; - return JS_TRUE; - default: { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%d", whence); - JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL, - JSMSG_WHITHER_WHENCE, numBuf); - return JS_FALSE; - } - } -} - -static uint32 -mem_tell(JSXDRState *xdr) -{ - return MEM_COUNT(xdr); -} - -static void -mem_finalize(JSXDRState *xdr) -{ - JS_free(xdr->cx, MEM_BASE(xdr)); -} - -static JSXDROps xdrmem_ops = { - mem_get32, mem_set32, mem_getbytes, mem_setbytes, - mem_raw, mem_seek, mem_tell, mem_finalize -}; - -JS_PUBLIC_API(void) -JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx) -{ - xdr->mode = mode; - xdr->cx = cx; - xdr->registry = NULL; - xdr->numclasses = xdr->maxclasses = 0; - xdr->reghash = NULL; - xdr->userdata = NULL; - xdr->script = NULL; -} - -JS_PUBLIC_API(JSXDRState *) -JS_XDRNewMem(JSContext *cx, JSXDRMode mode) -{ - JSXDRState *xdr = (JSXDRState *) JS_malloc(cx, sizeof(JSXDRMemState)); - if (!xdr) - return NULL; - JS_XDRInitBase(xdr, mode, cx); - if (mode == JSXDR_ENCODE) { - if (!(MEM_BASE(xdr) = JS_malloc(cx, MEM_BLOCK))) { - JS_free(cx, xdr); - return NULL; - } - } else { - /* XXXbe ok, so better not deref MEM_BASE(xdr) if not ENCODE */ - MEM_BASE(xdr) = NULL; - } - xdr->ops = &xdrmem_ops; - MEM_COUNT(xdr) = 0; - MEM_LIMIT(xdr) = MEM_BLOCK; - return xdr; -} - -JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp) -{ - if (xdr->ops != &xdrmem_ops) - return NULL; - *lp = MEM_COUNT(xdr); - return MEM_BASE(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len) -{ - if (xdr->ops != &xdrmem_ops) - return; - MEM_LIMIT(xdr) = len; - MEM_BASE(xdr) = data; - MEM_COUNT(xdr) = 0; -} - -JS_PUBLIC_API(uint32) -JS_XDRMemDataLeft(JSXDRState *xdr) -{ - if (xdr->ops != &xdrmem_ops) - return 0; - return MEM_LIMIT(xdr) - MEM_COUNT(xdr); -} - -JS_PUBLIC_API(void) -JS_XDRMemResetData(JSXDRState *xdr) -{ - if (xdr->ops != &xdrmem_ops) - return; - MEM_COUNT(xdr) = 0; -} - -JS_PUBLIC_API(void) -JS_XDRDestroy(JSXDRState *xdr) -{ - JSContext *cx = xdr->cx; - xdr->ops->finalize(xdr); - if (xdr->registry) { - JS_free(cx, xdr->registry); - if (xdr->reghash) - JS_DHashTableDestroy(xdr->reghash); - } - JS_free(cx, xdr); -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8 *b) -{ - uint32 l = *b; - if (!JS_XDRUint32(xdr, &l)) - return JS_FALSE; - *b = (uint8) l; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16 *s) -{ - uint32 l = *s; - if (!JS_XDRUint32(xdr, &l)) - return JS_FALSE; - *s = (uint16) l; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32 *lp) -{ - JSBool ok = JS_TRUE; - if (xdr->mode == JSXDR_ENCODE) { - uint32 xl = JSXDR_SWAB32(*lp); - ok = xdr->ops->set32(xdr, &xl); - } else if (xdr->mode == JSXDR_DECODE) { - ok = xdr->ops->get32(xdr, lp); - *lp = JSXDR_SWAB32(*lp); - } - return ok; -} - -JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len) -{ - uint32 padlen; - static char padbuf[JSXDR_ALIGN-1]; - - if (xdr->mode == JSXDR_ENCODE) { - if (!xdr->ops->setbytes(xdr, bytes, len)) - return JS_FALSE; - } else { - if (!xdr->ops->getbytes(xdr, bytes, len)) - return JS_FALSE; - } - len = xdr->ops->tell(xdr); - if (len % JSXDR_ALIGN) { - padlen = JSXDR_ALIGN - (len % JSXDR_ALIGN); - if (xdr->mode == JSXDR_ENCODE) { - if (!xdr->ops->setbytes(xdr, padbuf, padlen)) - return JS_FALSE; - } else { - if (!xdr->ops->seek(xdr, padlen, JSXDR_SEEK_CUR)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -/** - * Convert between a C string and the XDR representation: - * leading 32-bit count, then counted vector of chars, - * then possibly \0 padding to multiple of 4. - */ -JS_PUBLIC_API(JSBool) -JS_XDRCString(JSXDRState *xdr, char **sp) -{ - uint32 len; - - if (xdr->mode == JSXDR_ENCODE) - len = strlen(*sp); - JS_XDRUint32(xdr, &len); - if (xdr->mode == JSXDR_DECODE) { - if (!(*sp = (char *) JS_malloc(xdr->cx, len + 1))) - return JS_FALSE; - } - if (!JS_XDRBytes(xdr, *sp, len)) { - if (xdr->mode == JSXDR_DECODE) - JS_free(xdr->cx, *sp); - return JS_FALSE; - } - if (xdr->mode == JSXDR_DECODE) { - (*sp)[len] = '\0'; - } else if (xdr->mode == JSXDR_FREE) { - JS_free(xdr->cx, *sp); - *sp = NULL; - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRCStringOrNull(JSXDRState *xdr, char **sp) -{ - uint32 null = (*sp == NULL); - if (!JS_XDRUint32(xdr, &null)) - return JS_FALSE; - if (null) { - *sp = NULL; - return JS_TRUE; - } - return JS_XDRCString(xdr, sp); -} - -static JSBool -XDRChars(JSXDRState *xdr, jschar *chars, uint32 nchars) -{ - uint32 i, padlen, nbytes; - jschar *raw; - - nbytes = nchars * sizeof(jschar); - padlen = nbytes % JSXDR_ALIGN; - if (padlen) { - padlen = JSXDR_ALIGN - padlen; - nbytes += padlen; - } - if (!(raw = (jschar *) xdr->ops->raw(xdr, nbytes))) - return JS_FALSE; - if (xdr->mode == JSXDR_ENCODE) { - for (i = 0; i != nchars; i++) - raw[i] = JSXDR_SWAB16(chars[i]); - if (padlen) - memset((char *)raw + nbytes - padlen, 0, padlen); - } else if (xdr->mode == JSXDR_DECODE) { - for (i = 0; i != nchars; i++) - chars[i] = JSXDR_SWAB16(raw[i]); - } - return JS_TRUE; -} - -/* - * Convert between a JS (Unicode) string and the XDR representation. - */ -JS_PUBLIC_API(JSBool) -JS_XDRString(JSXDRState *xdr, JSString **strp) -{ - uint32 nchars; - jschar *chars; - - if (xdr->mode == JSXDR_ENCODE) - nchars = JSSTRING_LENGTH(*strp); - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - - if (xdr->mode == JSXDR_DECODE) { - chars = (jschar *) JS_malloc(xdr->cx, (nchars + 1) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - } else { - chars = JSSTRING_CHARS(*strp); - } - - if (!XDRChars(xdr, chars, nchars)) - goto bad; - if (xdr->mode == JSXDR_DECODE) { - chars[nchars] = 0; - *strp = JS_NewUCString(xdr->cx, chars, nchars); - if (!*strp) - goto bad; - } - return JS_TRUE; - -bad: - if (xdr->mode == JSXDR_DECODE) - JS_free(xdr->cx, chars); - return JS_FALSE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp) -{ - uint32 null = (*strp == NULL); - if (!JS_XDRUint32(xdr, &null)) - return JS_FALSE; - if (null) { - *strp = NULL; - return JS_TRUE; - } - return JS_XDRString(xdr, strp); -} - -static JSBool -XDRDoubleValue(JSXDRState *xdr, jsdouble *dp) -{ - jsdpun u; - - if (xdr->mode == JSXDR_ENCODE) - u.d = *dp; - if (!JS_XDRUint32(xdr, &u.s.lo) || !JS_XDRUint32(xdr, &u.s.hi)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *dp = u.d; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, jsdouble **dpp) -{ - jsdouble d; - - if (xdr->mode == JSXDR_ENCODE) - d = **dpp; - if (!XDRDoubleValue(xdr, &d)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) { - *dpp = JS_NewDouble(xdr->cx, d); - if (!*dpp) - return JS_FALSE; - } - return JS_TRUE; -} - -/* These are magic pseudo-tags: see jsapi.h, near the top, for real tags. */ -#define JSVAL_XDRNULL 0x8 -#define JSVAL_XDRVOID 0xA - -static JSBool -XDRValueBody(JSXDRState *xdr, uint32 type, jsval *vp) -{ - switch (type) { - case JSVAL_XDRNULL: - *vp = JSVAL_NULL; - break; - case JSVAL_XDRVOID: - *vp = JSVAL_VOID; - break; - case JSVAL_STRING: { - JSString *str; - if (xdr->mode == JSXDR_ENCODE) - str = JSVAL_TO_STRING(*vp); - if (!JS_XDRString(xdr, &str)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = STRING_TO_JSVAL(str); - break; - } - case JSVAL_DOUBLE: { - jsdouble *dp; - if (xdr->mode == JSXDR_ENCODE) - dp = JSVAL_TO_DOUBLE(*vp); - if (!JS_XDRDouble(xdr, &dp)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = DOUBLE_TO_JSVAL(dp); - break; - } - case JSVAL_OBJECT: { - JSObject *obj; - if (xdr->mode == JSXDR_ENCODE) - obj = JSVAL_TO_OBJECT(*vp); - if (!js_XDRObject(xdr, &obj)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = OBJECT_TO_JSVAL(obj); - break; - } - case JSVAL_BOOLEAN: { - uint32 b; - if (xdr->mode == JSXDR_ENCODE) - b = (uint32) JSVAL_TO_BOOLEAN(*vp); - if (!JS_XDRUint32(xdr, &b)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = BOOLEAN_TO_JSVAL((JSBool) b); - break; - } - default: { - uint32 i; - - JS_ASSERT(type & JSVAL_INT); - if (xdr->mode == JSXDR_ENCODE) - i = (uint32) JSVAL_TO_INT(*vp); - if (!JS_XDRUint32(xdr, &i)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - *vp = INT_TO_JSVAL((int32) i); - break; - } - } - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRValue(JSXDRState *xdr, jsval *vp) -{ - uint32 type; - - if (xdr->mode == JSXDR_ENCODE) { - if (JSVAL_IS_NULL(*vp)) - type = JSVAL_XDRNULL; - else if (JSVAL_IS_VOID(*vp)) - type = JSVAL_XDRVOID; - else - type = JSVAL_TAG(*vp); - } - return JS_XDRUint32(xdr, &type) && XDRValueBody(xdr, type, vp); -} - -JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp) -{ - jsval v; - uint32 type; - jsdouble d; - JSAtom *atom; - - if (xdr->mode == JSXDR_ENCODE) { - v = ATOM_KEY(*atomp); - return JS_XDRValue(xdr, &v); - } - - /* - * Inline JS_XDRValue when decoding to avoid ceation of GC things when - * then corresponding atom already exists. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &type)) - return JS_FALSE; - if (type == JSVAL_STRING) - return js_XDRStringAtom(xdr, atomp); - - if (type == JSVAL_DOUBLE) { - if (!XDRDoubleValue(xdr, &d)) - return JS_FALSE; - atom = js_AtomizeDouble(xdr->cx, d, 0); - } else { - if (!XDRValueBody(xdr, type, &v)) - return JS_FALSE; - atom = js_AtomizeValue(xdr->cx, v, 0); - } - - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -extern JSBool -js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp) -{ - JSString *str; - uint32 nchars; - JSAtom *atom; - JSContext *cx; - void *mark; - jschar *chars; - - if (xdr->mode == JSXDR_ENCODE) { - JS_ASSERT(ATOM_IS_STRING(*atomp)); - str = ATOM_TO_STRING(*atomp); - return JS_XDRString(xdr, &str); - } - - /* - * Inline JS_XDRString when decoding to avoid JSString allocation - * for already existing atoms. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nchars)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(chars, jschar *, &cx->tempPool, - nchars * sizeof(jschar)); - if (!chars) - JS_ReportOutOfMemory(cx); - else if (XDRChars(xdr, chars, nchars)) - atom = js_AtomizeChars(cx, chars, nchars, 0); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -/* - * FIXME: This performs lossy conversion and we need to switch to - * js_XDRStringAtom while allowing to read older XDR files. See bug 325202. - */ -JSBool -js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp) -{ - char *bytes; - uint32 nbytes; - JSAtom *atom; - JSContext *cx; - void *mark; - - if (xdr->mode == JSXDR_ENCODE) { - JS_ASSERT(ATOM_IS_STRING(*atomp)); - bytes = JS_GetStringBytes(ATOM_TO_STRING(*atomp)); - return JS_XDRCString(xdr, &bytes); - } - - /* - * Inline JS_XDRCString when decoding not to malloc temporary buffer - * just to free it after atomization. See bug 321985. - */ - if (!JS_XDRUint32(xdr, &nbytes)) - return JS_FALSE; - atom = NULL; - cx = xdr->cx; - mark = JS_ARENA_MARK(&cx->tempPool); - JS_ARENA_ALLOCATE_CAST(bytes, char *, &cx->tempPool, - nbytes * sizeof *bytes); - if (!bytes) - JS_ReportOutOfMemory(cx); - else if (JS_XDRBytes(xdr, bytes, nbytes)) - atom = js_Atomize(cx, bytes, nbytes, 0); - JS_ARENA_RELEASE(&cx->tempPool, mark); - if (!atom) - return JS_FALSE; - *atomp = atom; - return JS_TRUE; -} - -JS_PUBLIC_API(JSBool) -JS_XDRScript(JSXDRState *xdr, JSScript **scriptp) -{ - if (!js_XDRScript(xdr, scriptp, NULL)) - return JS_FALSE; - if (xdr->mode == JSXDR_DECODE) - js_CallNewScriptHook(xdr->cx, *scriptp, NULL); - return JS_TRUE; -} - -#define CLASS_REGISTRY_MIN 8 -#define CLASS_INDEX_TO_ID(i) ((i)+1) -#define CLASS_ID_TO_INDEX(id) ((id)-1) - -typedef struct JSRegHashEntry { - JSDHashEntryHdr hdr; - const char *name; - uint32 index; -} JSRegHashEntry; - -JS_PUBLIC_API(JSBool) -JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *idp) -{ - uintN numclasses, maxclasses; - JSClass **registry; - - numclasses = xdr->numclasses; - maxclasses = xdr->maxclasses; - if (numclasses == maxclasses) { - maxclasses = (maxclasses == 0) ? CLASS_REGISTRY_MIN : maxclasses << 1; - registry = (JSClass **) - JS_realloc(xdr->cx, xdr->registry, maxclasses * sizeof(JSClass *)); - if (!registry) - return JS_FALSE; - xdr->registry = registry; - xdr->maxclasses = maxclasses; - } else { - JS_ASSERT(numclasses && numclasses < maxclasses); - registry = xdr->registry; - } - - registry[numclasses] = clasp; - if (xdr->reghash) { - JSRegHashEntry *entry = (JSRegHashEntry *) - JS_DHashTableOperate(xdr->reghash, clasp->name, JS_DHASH_ADD); - if (!entry) { - JS_ReportOutOfMemory(xdr->cx); - return JS_FALSE; - } - entry->name = clasp->name; - entry->index = numclasses; - } - *idp = CLASS_INDEX_TO_ID(numclasses); - xdr->numclasses = ++numclasses; - return JS_TRUE; -} - -JS_PUBLIC_API(uint32) -JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name) -{ - uintN i, numclasses; - - numclasses = xdr->numclasses; - if (numclasses >= 10) { - JSRegHashEntry *entry; - - /* Bootstrap reghash from registry on first overpopulated Find. */ - if (!xdr->reghash) { - xdr->reghash = JS_NewDHashTable(JS_DHashGetStubOps(), NULL, - sizeof(JSRegHashEntry), - numclasses); - if (xdr->reghash) { - for (i = 0; i < numclasses; i++) { - JSClass *clasp = xdr->registry[i]; - entry = (JSRegHashEntry *) - JS_DHashTableOperate(xdr->reghash, clasp->name, - JS_DHASH_ADD); - entry->name = clasp->name; - entry->index = i; - } - } - } - - /* If we managed to create reghash, use it for O(1) Find. */ - if (xdr->reghash) { - entry = (JSRegHashEntry *) - JS_DHashTableOperate(xdr->reghash, name, JS_DHASH_LOOKUP); - if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) - return CLASS_INDEX_TO_ID(entry->index); - } - } - - /* Only a few classes, or we couldn't malloc reghash: use linear search. */ - for (i = 0; i < numclasses; i++) { - if (!strcmp(name, xdr->registry[i]->name)) - return CLASS_INDEX_TO_ID(i); - } - return 0; -} - -JS_PUBLIC_API(JSClass *) -JS_XDRFindClassById(JSXDRState *xdr, uint32 id) -{ - uintN i = CLASS_ID_TO_INDEX(id); - - if (i >= xdr->numclasses) - return NULL; - return xdr->registry[i]; -} - -#endif /* JS_HAS_XDR */ diff --git a/spidermonkey/libjs/jsxdrapi.h b/spidermonkey/libjs/jsxdrapi.h deleted file mode 100644 index 35d9918..0000000 --- a/spidermonkey/libjs/jsxdrapi.h +++ /dev/null @@ -1,223 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=8 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsxdrapi_h___ -#define jsxdrapi_h___ - -/* - * JS external data representation interface API. - * - * The XDR system is comprised of three major parts: - * - * - the state serialization/deserialization APIs, which allow consumers - * of the API to serialize JS runtime state (script bytecodes, atom maps, - * object graphs, etc.) for later restoration. These portions - * are implemented in various appropriate files, such as jsscript.c - * for the script portions and jsobj.c for object state. - * - the callback APIs through which the runtime requests an opaque - * representation of a native object, and through which the runtime - * constructs a live native object from an opaque representation. These - * portions are the responsibility of the native object implementor. - * - utility functions for en/decoding of primitive types, such as - * JSStrings. This portion is implemented in jsxdrapi.c. - * - * Spiritually guided by Sun's XDR, where appropriate. - */ - -#include "jspubtd.h" -#include "jsprvtd.h" - -JS_BEGIN_EXTERN_C - -/* We use little-endian byteorder for all encoded data */ - -#if defined IS_LITTLE_ENDIAN -#define JSXDR_SWAB32(x) x -#define JSXDR_SWAB16(x) x -#elif defined IS_BIG_ENDIAN -#define JSXDR_SWAB32(x) (((uint32)(x) >> 24) | \ - (((uint32)(x) >> 8) & 0xff00) | \ - (((uint32)(x) << 8) & 0xff0000) | \ - ((uint32)(x) << 24)) -#define JSXDR_SWAB16(x) (((uint16)(x) >> 8) | ((uint16)(x) << 8)) -#else -#error "unknown byte order" -#endif - -#define JSXDR_ALIGN 4 - -typedef enum JSXDRMode { - JSXDR_ENCODE, - JSXDR_DECODE, - JSXDR_FREE -} JSXDRMode; - -typedef enum JSXDRWhence { - JSXDR_SEEK_SET, - JSXDR_SEEK_CUR, - JSXDR_SEEK_END -} JSXDRWhence; - -typedef struct JSXDROps { - JSBool (*get32)(JSXDRState *, uint32 *); - JSBool (*set32)(JSXDRState *, uint32 *); - JSBool (*getbytes)(JSXDRState *, char *, uint32); - JSBool (*setbytes)(JSXDRState *, char *, uint32); - void * (*raw)(JSXDRState *, uint32); - JSBool (*seek)(JSXDRState *, int32, JSXDRWhence); - uint32 (*tell)(JSXDRState *); - void (*finalize)(JSXDRState *); -} JSXDROps; - -struct JSXDRState { - JSXDRMode mode; - JSXDROps *ops; - JSContext *cx; - JSClass **registry; - uintN numclasses; - uintN maxclasses; - void *reghash; - void *userdata; - JSScript *script; -}; - -extern JS_PUBLIC_API(void) -JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx); - -extern JS_PUBLIC_API(JSXDRState *) -JS_XDRNewMem(JSContext *cx, JSXDRMode mode); - -extern JS_PUBLIC_API(void *) -JS_XDRMemGetData(JSXDRState *xdr, uint32 *lp); - -extern JS_PUBLIC_API(void) -JS_XDRMemSetData(JSXDRState *xdr, void *data, uint32 len); - -extern JS_PUBLIC_API(uint32) -JS_XDRMemDataLeft(JSXDRState *xdr); - -extern JS_PUBLIC_API(void) -JS_XDRMemResetData(JSXDRState *xdr); - -extern JS_PUBLIC_API(void) -JS_XDRDestroy(JSXDRState *xdr); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint8(JSXDRState *xdr, uint8 *b); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint16(JSXDRState *xdr, uint16 *s); - -extern JS_PUBLIC_API(JSBool) -JS_XDRUint32(JSXDRState *xdr, uint32 *lp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32 len); - -extern JS_PUBLIC_API(JSBool) -JS_XDRCString(JSXDRState *xdr, char **sp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRCStringOrNull(JSXDRState *xdr, char **sp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRString(JSXDRState *xdr, JSString **strp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRStringOrNull(JSXDRState *xdr, JSString **strp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRDouble(JSXDRState *xdr, jsdouble **dp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRValue(JSXDRState *xdr, jsval *vp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRScript(JSXDRState *xdr, JSScript **scriptp); - -extern JS_PUBLIC_API(JSBool) -JS_XDRRegisterClass(JSXDRState *xdr, JSClass *clasp, uint32 *lp); - -extern JS_PUBLIC_API(uint32) -JS_XDRFindClassIdByName(JSXDRState *xdr, const char *name); - -extern JS_PUBLIC_API(JSClass *) -JS_XDRFindClassById(JSXDRState *xdr, uint32 id); - -/* - * Magic numbers. - */ -#define JSXDR_MAGIC_SCRIPT_1 0xdead0001 -#define JSXDR_MAGIC_SCRIPT_2 0xdead0002 -#define JSXDR_MAGIC_SCRIPT_3 0xdead0003 -#define JSXDR_MAGIC_SCRIPT_4 0xdead0004 -#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 -#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 - -/* - * Bytecode version number. Decrement the second term whenever JS bytecode - * changes incompatibly. - * - * This version number should be XDR'ed once near the front of any file or - * larger storage unit containing XDR'ed bytecode and other data, and checked - * before deserialization of bytecode. If the saved version does not match - * the current version, abort deserialization and invalidate the file. - */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 16) - -/* - * Library-private functions. - */ -extern JSBool -js_XDRAtom(JSXDRState *xdr, JSAtom **atomp); - -extern JSBool -js_XDRStringAtom(JSXDRState *xdr, JSAtom **atomp); - -/* - * FIXME: This is non-unicode version of js_XDRStringAtom that performs lossy - * conversion. Do not use it in the new code! See bug 325202. - */ -extern JSBool -js_XDRCStringAtom(JSXDRState *xdr, JSAtom **atomp); - -JS_END_EXTERN_C - -#endif /* ! jsxdrapi_h___ */ diff --git a/spidermonkey/libjs/jsxml.c b/spidermonkey/libjs/jsxml.c deleted file mode 100644 index 1266255..0000000 --- a/spidermonkey/libjs/jsxml.c +++ /dev/null @@ -1,8357 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=78: - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey E4X code, released August, 2004. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "jsstddef.h" -#include "jsconfig.h" - -#if JS_HAS_XML_SUPPORT - -#include -#include -#include -#include "jstypes.h" -#include "jsbit.h" -#include "jsprf.h" -#include "jsutil.h" -#include "jsapi.h" -#include "jsarray.h" -#include "jsatom.h" -#include "jsbool.h" -#include "jscntxt.h" -#include "jsfun.h" -#include "jsgc.h" -#include "jsinterp.h" -#include "jslock.h" -#include "jsnum.h" -#include "jsobj.h" -#include "jsopcode.h" -#include "jsparse.h" -#include "jsscan.h" -#include "jsscope.h" -#include "jsscript.h" -#include "jsstr.h" -#include "jsxml.h" - -#ifdef DEBUG -#include /* for #ifdef DEBUG memset calls */ -#endif - -/* - * NOTES - * - in the js shell, you must use the -x command line option, or call - * options('xml') before compiling anything that uses XML literals - * - * TODO - * - XXXbe patrol - * - Fuse objects and their JSXML* private data into single GC-things - * - fix function::foo vs. x.(foo == 42) collision using proper namespacing - * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants - * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM! - * - JS_TypeOfValue sure could use a cleaner interface to "types" - */ - -#ifdef DEBUG_brendan -#define METERING 1 -#endif - -#ifdef METERING -static struct { - jsrefcount qname; - jsrefcount qnameobj; - jsrefcount liveqname; - jsrefcount liveqnameobj; - jsrefcount namespace; - jsrefcount namespaceobj; - jsrefcount livenamespace; - jsrefcount livenamespaceobj; - jsrefcount xml; - jsrefcount xmlobj; - jsrefcount livexml; - jsrefcount livexmlobj; -} xml_stats; - -#define METER(x) JS_ATOMIC_INCREMENT(&(x)) -#define UNMETER(x) JS_ATOMIC_DECREMENT(&(x)) -#else -#define METER(x) /* nothing */ -#define UNMETER(x) /* nothing */ -#endif - -/* - * Random utilities and global functions. - */ -const char js_isXMLName_str[] = "isXMLName"; -const char js_XMLList_str[] = "XMLList"; -const char js_localName_str[] = "localName"; -const char js_xml_parent_str[] = "parent"; -const char js_prefix_str[] = "prefix"; -const char js_toXMLString_str[] = "toXMLString"; -const char js_uri_str[] = "uri"; - -const char js_amp_entity_str[] = "&"; -const char js_gt_entity_str[] = ">"; -const char js_lt_entity_str[] = "<"; -const char js_quot_entity_str[] = """; - -#define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0) -#define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*') - -static JSBool -xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0])); - return JS_TRUE; -} - -/* - * Namespace class and library functions. - */ -enum namespace_tinyid { - NAMESPACE_PREFIX = -1, - NAMESPACE_URI = -2 -}; - -static JSBool -namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXMLNamespace *ns; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - ns = (JSXMLNamespace *) - JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, NULL); - if (!ns) - return JS_TRUE; - - switch (JSVAL_TO_INT(id)) { - case NAMESPACE_PREFIX: - *vp = ns->prefix ? STRING_TO_JSVAL(ns->prefix) : JSVAL_VOID; - break; - case NAMESPACE_URI: - *vp = STRING_TO_JSVAL(ns->uri); - break; - } - return JS_TRUE; -} - -static void -namespace_finalize(JSContext *cx, JSObject *obj) -{ - JSXMLNamespace *ns; - JSRuntime *rt; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - if (!ns) - return; - JS_ASSERT(ns->object == obj); - ns->object = NULL; - UNMETER(xml_stats.livenamespaceobj); - - rt = cx->runtime; - if (rt->functionNamespaceObject == obj) - rt->functionNamespaceObject = NULL; -} - -static void -namespace_mark_vector(JSContext *cx, JSXMLNamespace **vec, uint32 len) -{ - uint32 i; - JSXMLNamespace *ns; - - for (i = 0; i < len; i++) { - ns = vec[i]; - { -#ifdef GC_MARK_DEBUG - char buf[100]; - - JS_snprintf(buf, sizeof buf, "%s=%s", - ns->prefix ? JS_GetStringBytes(ns->prefix) : "", - JS_GetStringBytes(ns->uri)); -#endif - GC_MARK(cx, ns, buf); - } - } -} - -static uint32 -namespace_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - GC_MARK(cx, ns, "private"); - return 0; -} - -static JSBool -namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXMLNamespace *ns, *ns2; - JSObject *obj2; - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, obj); - JS_ASSERT(JSVAL_IS_OBJECT(v)); - obj2 = JSVAL_TO_OBJECT(v); - if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base) { - *bp = JS_FALSE; - } else { - ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, obj2); - *bp = js_EqualStrings(ns->uri, ns2->uri); - } - return JS_TRUE; -} - -JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = { - { "Namespace", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | - JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace), - JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, namespace_mark, NULL }, - namespace_equality,NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -#define NAMESPACE_ATTRS \ - (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) - -static JSPropertySpec namespace_props[] = { - {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0}, - {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0}, - {0,0,0,0,0} -}; - -static JSBool -namespace_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) - JS_GetInstancePrivate(cx, obj, &js_NamespaceClass.base, argv); - if (!ns) - return JS_FALSE; - - *rval = STRING_TO_JSVAL(ns->uri); - return JS_TRUE; -} - -static JSFunctionSpec namespace_methods[] = { - {js_toString_str, namespace_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSXMLNamespace * -js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared) -{ - JSXMLNamespace *ns; - - ns = (JSXMLNamespace *) - js_NewGCThing(cx, GCX_NAMESPACE, sizeof(JSXMLNamespace)); - if (!ns) - return NULL; - ns->object = NULL; - ns->prefix = prefix; - ns->uri = uri; - ns->declared = declared; - METER(xml_stats.namespace); - METER(xml_stats.livenamespace); - return ns; -} - -void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns) -{ - GC_MARK(cx, ns->object, "object"); - GC_MARK(cx, ns->prefix, "prefix"); - GC_MARK(cx, ns->uri, "uri"); -} - -void -js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns) -{ - UNMETER(xml_stats.livenamespace); -} - -JSObject * -js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared) -{ - JSXMLNamespace *ns; - - ns = js_NewXMLNamespace(cx, prefix, uri, declared); - if (!ns) - return NULL; - return js_GetXMLNamespaceObject(cx, ns); -} - -JSObject * -js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns) -{ - JSObject *obj; - - obj = ns->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == ns); - return obj; - } - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, ns)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - ns->object = obj; - METER(xml_stats.namespaceobj); - METER(xml_stats.livenamespaceobj); - return obj; -} - -/* - * QName class and library functions. - */ -enum qname_tinyid { - QNAME_URI = -1, - QNAME_LOCALNAME = -2 -}; - -static JSBool -qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXMLQName *qn; - - if (!JSVAL_IS_INT(id)) - return JS_TRUE; - - qn = (JSXMLQName *) - JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, NULL); - if (!qn) - return JS_TRUE; - - switch (JSVAL_TO_INT(id)) { - case QNAME_URI: - *vp = qn->uri ? STRING_TO_JSVAL(qn->uri) : JSVAL_NULL; - break; - case QNAME_LOCALNAME: - *vp = STRING_TO_JSVAL(qn->localName); - break; - } - return JS_TRUE; -} - -static void -qname_finalize(JSContext *cx, JSObject *obj) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - if (!qn) - return; - JS_ASSERT(qn->object == obj); - qn->object = NULL; - UNMETER(xml_stats.liveqnameobj); -} - -static void -anyname_finalize(JSContext* cx, JSObject* obj) -{ - JSRuntime *rt; - - /* Make sure the next call to js_GetAnyName doesn't try to use obj. */ - rt = cx->runtime; - if (rt->anynameObject == obj) - rt->anynameObject = NULL; - - qname_finalize(cx, obj); -} - -static uint32 -qname_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - GC_MARK(cx, qn, "private"); - return 0; -} - -static JSBool -qname_identity(JSXMLQName *qna, JSXMLQName *qnb) -{ - if (!qna->uri ^ !qnb->uri) - return JS_FALSE; - if (qna->uri && !js_EqualStrings(qna->uri, qnb->uri)) - return JS_FALSE; - return js_EqualStrings(qna->localName, qnb->localName); -} - -static JSBool -qname_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXMLQName *qn, *qn2; - JSObject *obj2; - - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - JS_ASSERT(JSVAL_IS_OBJECT(v)); - obj2 = JSVAL_TO_OBJECT(v); - if (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base) { - *bp = JS_FALSE; - } else { - qn2 = (JSXMLQName *) JS_GetPrivate(cx, obj2); - *bp = qname_identity(qn, qn2); - } - return JS_TRUE; -} - -JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = { - { "QName", - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED | - JSCLASS_HAS_CACHED_PROTO(JSProto_QName), - JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL }, - qname_equality, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -/* - * Classes for the ECMA-357-internal types AttributeName and AnyName, which - * are like QName, except that they have no property getters. They share the - * qname_toString method, and therefore are exposed as constructable objects - * in this implementation. - */ -JS_FRIEND_DATA(JSClass) js_AttributeNameClass = { - js_AttributeName_str, - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | - JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, qname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL -}; - -JS_FRIEND_DATA(JSClass) js_AnyNameClass = { - js_AnyName_str, - JSCLASS_HAS_PRIVATE | JSCLASS_CONSTRUCT_PROTOTYPE | - JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize, - NULL, NULL, NULL, NULL, - NULL, NULL, qname_mark, NULL -}; - -#define QNAME_ATTRS \ - (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED) - -static JSPropertySpec qname_props[] = { - {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0}, - {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0}, - {0,0,0,0,0} -}; - -static JSBool -qname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSClass *clasp; - JSXMLQName *qn; - JSString *str, *qualstr; - size_t length; - jschar *chars; - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass || clasp == &js_AnyNameClass) { - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - } else { - qn = (JSXMLQName *) - JS_GetInstancePrivate(cx, obj, &js_QNameClass.base, argv); - if (!qn) - return JS_FALSE; - } - - if (!qn->uri) { - /* No uri means wildcard qualifier. */ - str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom); - } else if (IS_EMPTY(qn->uri)) { - /* Empty string for uri means localName is in no namespace. */ - str = cx->runtime->emptyString; - } else { - qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom); - str = js_ConcatStrings(cx, qn->uri, qualstr); - if (!str) - return JS_FALSE; - } - str = js_ConcatStrings(cx, str, qn->localName); - if (!str) - return JS_FALSE; - - if (str && clasp == &js_AttributeNameClass) { - length = JSSTRING_LENGTH(str); - chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar)); - if (!chars) - return JS_FALSE; - *chars = '@'; - js_strncpy(chars + 1, JSSTRING_CHARS(str), length); - chars[++length] = 0; - str = js_NewString(cx, chars, length, 0); - if (!str) { - JS_free(cx, chars); - return JS_FALSE; - } - } - - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSFunctionSpec qname_methods[] = { - {js_toString_str, qname_toString, 0,0,0}, - {0,0,0,0,0} -}; - -JSXMLQName * -js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName) -{ - JSXMLQName *qn; - - qn = (JSXMLQName *) js_NewGCThing(cx, GCX_QNAME, sizeof(JSXMLQName)); - if (!qn) - return NULL; - qn->object = NULL; - qn->uri = uri; - qn->prefix = prefix; - qn->localName = localName; - METER(xml_stats.qname); - METER(xml_stats.liveqname); - return qn; -} - -void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn) -{ - GC_MARK(cx, qn->object, "object"); - GC_MARK(cx, qn->uri, "uri"); - GC_MARK(cx, qn->prefix, "prefix"); - GC_MARK(cx, qn->localName, "localName"); -} - -void -js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn) -{ - UNMETER(xml_stats.liveqname); -} - -JSObject * -js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName) -{ - JSXMLQName *qn; - - qn = js_NewXMLQName(cx, uri, prefix, localName); - if (!qn) - return NULL; - return js_GetXMLQNameObject(cx, qn); -} - -JSObject * -js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn) -{ - JSObject *obj; - - obj = qn->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == qn); - return obj; - } - obj = js_NewObject(cx, &js_QNameClass.base, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - return obj; -} - -JSObject * -js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn) -{ - JSObject *obj; - - obj = qn->object; - if (obj) { - if (OBJ_GET_CLASS(cx, obj) == &js_AttributeNameClass) - return obj; - qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); - if (!qn) - return NULL; - } - - obj = js_NewObject(cx, &js_AttributeNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - return obj; -} - -JSObject * -js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval) -{ - jsval argv[2]; - - /* - * ECMA-357 11.1.2, - * The _QualifiedIdentifier : PropertySelector :: PropertySelector_ - * production, step 2. - */ - if (!JSVAL_IS_PRIMITIVE(nsval) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) { - nsval = JSVAL_NULL; - } - - argv[0] = nsval; - argv[1] = lnval; - return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv); -} - -static JSBool -IsXMLName(const jschar *cp, size_t n) -{ - JSBool rv; - jschar c; - - rv = JS_FALSE; - if (n != 0 && JS_ISXMLNSSTART(*cp)) { - while (--n != 0) { - c = *++cp; - if (!JS_ISXMLNS(c)) - return rv; - } - rv = JS_TRUE; - } - return rv; -} - -JSBool -js_IsXMLName(JSContext *cx, jsval v) -{ - JSClass *clasp; - JSXMLQName *qn; - JSString *name; - JSErrorReporter older; - - /* - * Inline specialization of the QName constructor called with v passed as - * the only argument, to compute the localName for the constructed qname, - * without actually allocating the object or computing its uri and prefix. - * See ECMA-357 13.1.2.1 step 1 and 13.3.2. - */ - if (!JSVAL_IS_PRIMITIVE(v) && - (clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)), - clasp == &js_QNameClass.base || - clasp == &js_AttributeNameClass || - clasp == &js_AnyNameClass)) { - qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - name = qn->localName; - } else { - older = JS_SetErrorReporter(cx, NULL); - name = js_ValueToString(cx, v); - JS_SetErrorReporter(cx, older); - if (!name) { - JS_ClearPendingException(cx); - return JS_FALSE; - } - } - - return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name)); -} - -static JSBool -Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval urival, prefixval; - JSObject *uriobj; - JSBool isNamespace, isQName; - JSClass *clasp; - JSString *empty, *prefix; - JSXMLNamespace *ns, *ns2; - JSXMLQName *qn; - - urival = argv[argc > 1]; - isNamespace = isQName = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(urival)) { - uriobj = JSVAL_TO_OBJECT(urival); - clasp = OBJ_GET_CLASS(cx, uriobj); - isNamespace = (clasp == &js_NamespaceClass.base); - isQName = (clasp == &js_QNameClass.base); - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else uriobj = NULL; -#endif - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* Namespace called as function. */ - if (argc == 1 && isNamespace) { - /* Namespace called with one Namespace argument is identity. */ - *rval = urival; - return JS_TRUE; - } - - /* Create and return a new QName object exactly as if constructed. */ - obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - METER(xml_stats.namespaceobj); - METER(xml_stats.livenamespaceobj); - - /* - * Create and connect private data to rooted obj early, so we don't have - * to worry about rooting string newborns hanging off of the private data - * further below. - */ - empty = cx->runtime->emptyString; - ns = js_NewXMLNamespace(cx, empty, empty, JS_FALSE); - if (!ns) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, ns)) - return JS_FALSE; - ns->object = obj; - - if (argc == 1) { - if (isNamespace) { - ns2 = (JSXMLNamespace *) JS_GetPrivate(cx, uriobj); - ns->uri = ns2->uri; - ns->prefix = ns2->prefix; - } else if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { - ns->uri = qn->uri; - ns->prefix = qn->prefix; - } else { - ns->uri = js_ValueToString(cx, urival); - if (!ns->uri) - return JS_FALSE; - - /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ - if (!IS_EMPTY(ns->uri)) - ns->prefix = NULL; - } - } else if (argc == 2) { - if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, uriobj))->uri) { - ns->uri = qn->uri; - } else { - ns->uri = js_ValueToString(cx, urival); - if (!ns->uri) - return JS_FALSE; - } - - prefixval = argv[0]; - if (IS_EMPTY(ns->uri)) { - if (!JSVAL_IS_VOID(prefixval)) { - prefix = js_ValueToString(cx, prefixval); - if (!prefix) - return JS_FALSE; - if (!IS_EMPTY(prefix)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAMESPACE, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(prefix))); - return JS_FALSE; - } - } - } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) { - /* NULL here represents *undefined* in ECMA-357 13.2.2 4(d) etc. */ - ns->prefix = NULL; - } else { - prefix = js_ValueToString(cx, prefixval); - if (!prefix) - return JS_FALSE; - ns->prefix = prefix; - } - } - - return JS_TRUE; -} - -static JSBool -QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval nameval, nsval; - JSBool isQName, isNamespace; - JSXMLQName *qn; - JSString *uri, *prefix, *name; - JSObject *nsobj; - JSClass *clasp; - JSXMLNamespace *ns; - - nameval = argv[argc > 1]; - isQName = - !JSVAL_IS_PRIMITIVE(nameval) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base; - - if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { - /* QName called as function. */ - if (argc == 1 && isQName) { - /* QName called with one QName argument is identity. */ - *rval = nameval; - return JS_TRUE; - } - - /* - * Create and return a new QName object exactly as if constructed. - * Use the constructor's clasp so we can be shared by AttributeName - * (see below after this function). - */ - obj = js_NewObject(cx, - JS_ValueToFunction(cx, argv[-2])->clasp, - NULL, NULL); - if (!obj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - } - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - if (isQName) { - /* If namespace is not specified and name is a QName, clone it. */ - qn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nameval)); - if (argc == 1) { - uri = qn->uri; - prefix = qn->prefix; - name = qn->localName; - goto out; - } - - /* Namespace and qname were passed -- use the qname's localName. */ - nameval = STRING_TO_JSVAL(qn->localName); - } - - if (argc == 0) { - name = cx->runtime->emptyString; - } else { - name = js_ValueToString(cx, nameval); - if (!name) - return JS_FALSE; - - /* Use argv[1] as a local root for name, even if it was not passed. */ - argv[1] = STRING_TO_JSVAL(name); - } - - nsval = argv[0]; - if (argc == 1 || JSVAL_IS_VOID(nsval)) { - if (IS_STAR(name)) { - nsval = JSVAL_NULL; - } else { - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return JS_FALSE; - } - } - - if (JSVAL_IS_NULL(nsval)) { - /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */ - uri = prefix = NULL; - } else { - /* - * Inline specialization of the Namespace constructor called with - * nsval passed as the only argument, to compute the uri and prefix - * for the constructed namespace, without actually allocating the - * object or computing other members. See ECMA-357 13.3.2 6(a) and - * 13.2.2. - */ - isNamespace = isQName = JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(nsval)) { - nsobj = JSVAL_TO_OBJECT(nsval); - clasp = OBJ_GET_CLASS(cx, nsobj); - isNamespace = (clasp == &js_NamespaceClass.base); - isQName = (clasp == &js_QNameClass.base); - } -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - else nsobj = NULL; -#endif - - if (isNamespace) { - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - uri = ns->uri; - prefix = ns->prefix; - } else if (isQName && - (qn = (JSXMLQName *) JS_GetPrivate(cx, nsobj))->uri) { - uri = qn->uri; - prefix = qn->prefix; - } else { - uri = js_ValueToString(cx, nsval); - if (!uri) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(uri); /* local root */ - - /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */ - prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; - } - } - -out: - qn = js_NewXMLQName(cx, uri, prefix, name); - if (!qn) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, qn)) - return JS_FALSE; - qn->object = obj; - return JS_TRUE; -} - -static JSBool -AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - /* - * Since js_AttributeNameClass was initialized, obj will have that as its - * class, not js_QNameClass. - */ - return QName(cx, obj, argc, argv, rval); -} - -/* - * XMLArray library functions. - */ -static JSBool -namespace_identity(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsa->prefix && nsb->prefix) { - if (!js_EqualStrings(nsa->prefix, nsb->prefix)) - return JS_FALSE; - } else { - if (nsa->prefix || nsb->prefix) - return JS_FALSE; - } - return js_EqualStrings(nsa->uri, nsb->uri); -} - -static JSBool -attr_identity(const void *a, const void *b) -{ - const JSXML *xmla = (const JSXML *) a; - const JSXML *xmlb = (const JSXML *) b; - - return qname_identity(xmla->name, xmlb->name); -} - -static void -XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array) -{ - JSXMLArrayCursor *next; - - cursor->array = array; - cursor->index = 0; - next = cursor->next = array->cursors; - if (next) - next->prevp = &cursor->next; - cursor->prevp = &array->cursors; - array->cursors = cursor; - cursor->root = NULL; -} - -static void -XMLArrayCursorFinish(JSXMLArrayCursor *cursor) -{ - JSXMLArrayCursor *next; - - if (!cursor->array) - return; - next = cursor->next; - if (next) - next->prevp = cursor->prevp; - *cursor->prevp = next; - cursor->array = NULL; -} - -static void * -XMLArrayCursorNext(JSXMLArrayCursor *cursor) -{ - JSXMLArray *array; - - array = cursor->array; - if (!array || cursor->index >= array->length) - return NULL; - return cursor->root = array->vector[cursor->index++]; -} - -static void * -XMLArrayCursorItem(JSXMLArrayCursor *cursor) -{ - JSXMLArray *array; - - array = cursor->array; - if (!array || cursor->index >= array->length) - return NULL; - return cursor->root = array->vector[cursor->index]; -} - -static void -XMLArrayCursorMark(JSContext *cx, JSXMLArrayCursor *cursor) -{ - while (cursor) { - GC_MARK(cx, cursor->root, "cursor->root"); - cursor = cursor->next; - } -} - -/* NB: called with null cx from the GC, via xml_mark => XMLArrayTrim. */ -static JSBool -XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity) -{ - void **vector; - - if (capacity == 0) { - /* We could let realloc(p, 0) free this, but purify gets confused. */ - if (array->vector) - free(array->vector); - vector = NULL; - } else { - if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || - !(vector = (void **) - realloc(array->vector, capacity * sizeof(void *)))) { - if (cx) - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - } - array->capacity = JSXML_PRESET_CAPACITY | capacity; - array->vector = vector; - return JS_TRUE; -} - -static void -XMLArrayTrim(JSXMLArray *array) -{ - if (array->capacity & JSXML_PRESET_CAPACITY) - return; - if (array->length < array->capacity) - XMLArraySetCapacity(NULL, array, array->length); -} - -static JSBool -XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity) -{ - array->length = array->capacity = 0; - array->vector = NULL; - array->cursors = NULL; - return capacity == 0 || XMLArraySetCapacity(cx, array, capacity); -} - -static void -XMLArrayFinish(JSContext *cx, JSXMLArray *array) -{ - JSXMLArrayCursor *cursor; - - JS_free(cx, array->vector); - - while ((cursor = array->cursors) != NULL) - XMLArrayCursorFinish(cursor); - -#ifdef DEBUG - memset(array, 0xd5, sizeof *array); -#endif -} - -#define XML_NOT_FOUND ((uint32) -1) - -static uint32 -XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity) -{ - void **vector; - uint32 i, n; - - /* The identity op must not reallocate array->vector. */ - vector = array->vector; - if (identity) { - for (i = 0, n = array->length; i < n; i++) { - if (identity(vector[i], elt)) - return i; - } - } else { - for (i = 0, n = array->length; i < n; i++) { - if (vector[i] == elt) - return i; - } - } - return XML_NOT_FOUND; -} - -/* - * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after - * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold - * should be greater than increment. - */ -#define LINEAR_THRESHOLD 256 -#define LINEAR_INCREMENT 32 - -static JSBool -XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt) -{ - uint32 capacity, i; - int log2; - void **vector; - - if (index >= array->length) { - if (index >= JSXML_CAPACITY(array)) { - /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */ - capacity = index + 1; - if (index >= LINEAR_THRESHOLD) { - capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT); - } else { - JS_CEILING_LOG2(log2, capacity); - capacity = JS_BIT(log2); - } - if ((size_t)capacity > ~(size_t)0 / sizeof(void *) || - !(vector = (void **) - realloc(array->vector, capacity * sizeof(void *)))) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - array->capacity = capacity; - array->vector = vector; - for (i = array->length; i < index; i++) - vector[i] = NULL; - } - array->length = index + 1; - } - - array->vector[index] = elt; - return JS_TRUE; -} - -static JSBool -XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n) -{ - uint32 j; - JSXMLArrayCursor *cursor; - - j = array->length; - JS_ASSERT(i <= j); - if (!XMLArraySetCapacity(cx, array, j + n)) - return JS_FALSE; - - array->length = j + n; - JS_ASSERT(n != (uint32)-1); - while (j != i) { - --j; - array->vector[j + n] = array->vector[j]; - } - - for (cursor = array->cursors; cursor; cursor = cursor->next) { - if (cursor->index > i) - cursor->index += n; - } - return JS_TRUE; -} - -static void * -XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress) -{ - uint32 length; - void **vector, *elt; - JSXMLArrayCursor *cursor; - - length = array->length; - if (index >= length) - return NULL; - - vector = array->vector; - elt = vector[index]; - if (compress) { - while (++index < length) - vector[index-1] = vector[index]; - array->length = length - 1; - array->capacity = JSXML_CAPACITY(array); - } else { - vector[index] = NULL; - } - - for (cursor = array->cursors; cursor; cursor = cursor->next) { - if (cursor->index > index) - --cursor->index; - } - return elt; -} - -static void -XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length) -{ - void **vector; - - JS_ASSERT(!array->cursors); - if (length >= array->length) - return; - - if (length == 0) { - if (array->vector) - free(array->vector); - vector = NULL; - } else { - vector = realloc(array->vector, length * sizeof(void *)); - if (!vector) - return; - } - - if (array->length > length) - array->length = length; - array->capacity = length; - array->vector = vector; -} - -#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f) -#define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \ - XML_NOT_FOUND) -#define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \ - ? (t *) (a)->vector[i] \ - : NULL) -#define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \ - if ((a)->length <= (i)) \ - (a)->length = (i) + 1; \ - ((a)->vector[i] = (void *)(e)); \ - JS_END_MACRO -#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e)) -#define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n) -#define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e)) -#define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c)) -#define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n) - -/* - * Define XML setting property strings and constants early, so everyone can - * use the same names and their magic numbers (tinyids, flags). - */ -static const char js_ignoreComments_str[] = "ignoreComments"; -static const char js_ignoreProcessingInstructions_str[] - = "ignoreProcessingInstructions"; -static const char js_ignoreWhitespace_str[] = "ignoreWhitespace"; -static const char js_prettyPrinting_str[] = "prettyPrinting"; -static const char js_prettyIndent_str[] = "prettyIndent"; - -/* - * NB: These XML static property tinyids must - * (a) not collide with the generic negative tinyids at the top of jsfun.c; - * (b) index their corresponding xml_static_props array elements. - * Don't change 'em! - */ -enum xml_static_tinyid { - XML_IGNORE_COMMENTS, - XML_IGNORE_PROCESSING_INSTRUCTIONS, - XML_IGNORE_WHITESPACE, - XML_PRETTY_PRINTING, - XML_PRETTY_INDENT -}; - -static JSBool -xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - return JS_TRUE; -} - -static JSBool -xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool b; - uint8 flag; - - JS_ASSERT(JSVAL_IS_INT(id)); - if (!js_ValueToBoolean(cx, *vp, &b)) - return JS_FALSE; - - flag = JS_BIT(JSVAL_TO_INT(id)); - if (b) - cx->xmlSettingFlags |= flag; - else - cx->xmlSettingFlags &= ~flag; - return JS_TRUE; -} - -static JSPropertySpec xml_static_props[] = { - {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_ignoreProcessingInstructions_str, - XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT, - xml_setting_getter, xml_setting_setter}, - {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT, - xml_setting_getter, NULL}, - {0,0,0,0,0} -}; - -/* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */ -#define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS) -#define XSF_IGNORE_PROCESSING_INSTRUCTIONS \ - JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS) -#define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE) -#define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING) -#define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT) - -/* - * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML. - * This flag means a couple of things: - * - * - The top JSXML created for a parse tree must have an object owning it. - * - * - That the default namespace normally inherited from the temporary - * tag that wraps a runtime-concatenated XML source - * string must, in the case of a precompiled XML object tree, inherit via - * ad-hoc code in ParseNodeToXML. - * - * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT. - */ -#define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1) - -/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */ -#define IS_XML(str) \ - (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str))) - -#define IS_XMLNS(str) \ - (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str))) - -#define IS_XML_CHARS(chars) \ - (JS_TOLOWER((chars)[0]) == 'x' && \ - JS_TOLOWER((chars)[1]) == 'm' && \ - JS_TOLOWER((chars)[2]) == 'l') - -#define HAS_NS_AFTER_XML(chars) \ - (JS_TOLOWER((chars)[3]) == 'n' && \ - JS_TOLOWER((chars)[4]) == 's') - -#define IS_XMLNS_CHARS(chars) \ - (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars)) - -#define STARTS_WITH_XML(chars,length) \ - (length >= 3 && IS_XML_CHARS(chars)) - -static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; -static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; - -static JSXMLQName * -ParseNodeToQName(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, - JSBool isAttributeName) -{ - JSString *str, *uri, *prefix, *localName; - size_t length, offset; - const jschar *start, *limit, *colon; - uint32 n; - JSXMLNamespace *ns; - - JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = ATOM_TO_STRING(pn->pn_atom); - length = JSSTRING_LENGTH(str); - start = JSSTRING_CHARS(str); - JS_ASSERT(length != 0 && *start != '@'); - JS_ASSERT(length != 1 || *start != '*'); - - uri = cx->runtime->emptyString; - limit = start + length; - colon = js_strchr_limit(start, ':', limit); - if (colon) { - offset = PTRDIFF(colon, start, jschar); - prefix = js_NewDependentString(cx, str, 0, offset, 0); - if (!prefix) - return NULL; - - if (STARTS_WITH_XML(start, offset)) { - if (offset == 3) { - uri = JS_InternString(cx, xml_namespace_str); - if (!uri) - return NULL; - } else if (offset == 5 && HAS_NS_AFTER_XML(start)) { - uri = JS_InternString(cx, xmlns_namespace_str); - if (!uri) - return NULL; - } else { - uri = NULL; - } - } else { - uri = NULL; - n = inScopeNSes->length; - while (n != 0) { - --n; - ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (ns->prefix && js_EqualStrings(ns->prefix, prefix)) { - uri = ns->uri; - break; - } - } - } - - if (!uri) { - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_XML_NAMESPACE, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(prefix))); - return NULL; - } - - localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1), 0); - if (!localName) - return NULL; - } else { - if (isAttributeName) { - /* - * An unprefixed attribute is not in any namespace, so set prefix - * as well as uri to the empty string. - */ - prefix = uri; - } else { - /* - * Loop from back to front looking for the closest declared default - * namespace. - */ - n = inScopeNSes->length; - while (n != 0) { - --n; - ns = XMLARRAY_MEMBER(inScopeNSes, n, JSXMLNamespace); - if (!ns->prefix || IS_EMPTY(ns->prefix)) { - uri = ns->uri; - break; - } - } - prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL; - } - localName = str; - } - - return js_NewXMLQName(cx, uri, prefix, localName); -} - -static JSString * -ChompXMLWhitespace(JSContext *cx, JSString *str) -{ - size_t length, newlength, offset; - const jschar *cp, *start, *end; - jschar c; - - length = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (!JS_ISXMLSPACE(c)) - break; - } - while (end > cp) { - c = end[-1]; - if (!JS_ISXMLSPACE(c)) - break; - --end; - } - newlength = PTRDIFF(end, cp, jschar); - if (newlength == length) - return str; - offset = PTRDIFF(cp, start, jschar); - return js_NewDependentString(cx, str, offset, newlength, 0); -} - -static JSXML * -ParseNodeToXML(JSContext *cx, JSParseNode *pn, JSXMLArray *inScopeNSes, - uintN flags) -{ - JSXML *xml, *kid, *attr, *attrj; - JSString *str; - uint32 length, n, i, j; - JSParseNode *pn2, *pn3, *head, **pnp; - JSXMLNamespace *ns; - JSXMLQName *qn, *attrjqn; - JSXMLClass xml_class; - int stackDummy; - - if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_OVER_RECURSED); - return NULL; - } - -#define PN2X_SKIP_CHILD ((JSXML *) 1) - - /* - * Cases return early to avoid common code that gets an outermost xml's - * object, which protects GC-things owned by xml and its descendants from - * garbage collection. - */ - xml = NULL; - if (!js_EnterLocalRootScope(cx)) - return NULL; - switch (pn->pn_type) { - case TOK_XMLELEM: - length = inScopeNSes->length; - pn2 = pn->pn_head; - xml = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (!xml) - goto fail; - - flags &= ~XSF_PRECOMPILED_ROOT; - n = pn->pn_count; - JS_ASSERT(n >= 2); - n -= 2; - if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) - goto fail; - - i = 0; - while ((pn2 = pn2->pn_next) != NULL) { - if (!pn2->pn_next) { - /* Don't append the end tag! */ - JS_ASSERT(pn2->pn_type == TOK_XMLETAGO); - break; - } - - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && pn2->pn_type == TOK_XMLSPACE) { - --n; - continue; - } - - kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (kid == PN2X_SKIP_CHILD) { - --n; - continue; - } - - if (!kid) - goto fail; - - /* Store kid in xml right away, to protect it from GC. */ - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); - kid->parent = xml; - ++i; - - /* XXX where is this documented in an XML spec, or in E4X? */ - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && kid->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid->xml_value); - if (!str) - goto fail; - kid->xml_value = str; - } - } - - JS_ASSERT(i == n); - if (n < pn->pn_count - 2) - XMLArrayTrim(&xml->xml_kids); - XMLARRAY_TRUNCATE(cx, inScopeNSes, length); - break; - - case TOK_XMLLIST: - xml = js_NewXML(cx, JSXML_CLASS_LIST); - if (!xml) - goto fail; - - n = pn->pn_count; - if (!XMLArraySetCapacity(cx, &xml->xml_kids, n)) - goto fail; - - i = 0; - for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) { - /* - * Always ignore insignificant whitespace in lists -- we shouldn't - * condition this on an XML.ignoreWhitespace setting when the list - * constructor is XMLList (note XML/XMLList unification hazard). - */ - if (pn2->pn_type == TOK_XMLSPACE) { - --n; - continue; - } - - kid = ParseNodeToXML(cx, pn2, inScopeNSes, flags); - if (kid == PN2X_SKIP_CHILD) { - --n; - continue; - } - - if (!kid) - goto fail; - - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid); - ++i; - } - - if (n < pn->pn_count) - XMLArrayTrim(&xml->xml_kids); - break; - - case TOK_XMLSTAGO: - case TOK_XMLPTAGC: - length = inScopeNSes->length; - pn2 = pn->pn_head; - JS_ASSERT(pn2->pn_type == TOK_XMLNAME); - if (pn2->pn_arity == PN_LIST) - goto syntax; - - xml = js_NewXML(cx, JSXML_CLASS_ELEMENT); - if (!xml) - goto fail; - - /* First pass: check syntax and process namespace declarations. */ - JS_ASSERT(pn->pn_count >= 1); - n = pn->pn_count - 1; - pnp = &pn2->pn_next; - head = *pnp; - while ((pn2 = *pnp) != NULL) { - size_t length; - const jschar *chars; - - if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY) - goto syntax; - - /* Enforce "Well-formedness constraint: Unique Att Spec". */ - for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { - if (pn3->pn_atom == pn2->pn_atom) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_DUPLICATE_XML_ATTR, - js_ValueToPrintableString(cx, - ATOM_KEY(pn2->pn_atom))); - goto fail; - } - } - - str = ATOM_TO_STRING(pn2->pn_atom); - pn2 = pn2->pn_next; - JS_ASSERT(pn2); - if (pn2->pn_type != TOK_XMLATTR) - goto syntax; - - length = JSSTRING_LENGTH(str); - chars = JSSTRING_CHARS(str); - if (length >= 5 && - IS_XMLNS_CHARS(chars) && - (length == 5 || chars[5] == ':')) { - JSString *uri, *prefix; - - uri = ATOM_TO_STRING(pn2->pn_atom); - if (length == 5) { - /* 10.3.2.1. Step 6(h)(i)(1)(a). */ - prefix = cx->runtime->emptyString; - } else { - prefix = js_NewStringCopyN(cx, chars + 6, length - 6, 0); - if (!prefix) - goto fail; - } - - /* - * Once the new ns is appended to xml->xml_namespaces, it is - * protected from GC by the object that owns xml -- which is - * either xml->object if outermost, or the object owning xml's - * oldest ancestor if !outermost. - */ - ns = js_NewXMLNamespace(cx, prefix, uri, JS_TRUE); - if (!ns) - goto fail; - - /* - * Don't add a namespace that's already in scope. If someone - * extracts a child property from its parent via [[Get]], then - * we enforce the invariant, noted many times in ECMA-357, that - * the child's namespaces form a possibly-improper superset of - * its ancestors' namespaces. - */ - if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) { - if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) || - !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) { - goto fail; - } - } - - JS_ASSERT(n >= 2); - n -= 2; - *pnp = pn2->pn_next; - /* XXXbe recycle pn2 */ - continue; - } - - pnp = &pn2->pn_next; - } - - /* - * If called from js_ParseNodeToXMLObject, emulate the effect of the - * ... wrapping done by "ToXML Applied to - * the String Type" (ECMA-357 10.3.1). - */ - if (flags & XSF_PRECOMPILED_ROOT) { - JS_ASSERT(length >= 1); - ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSXMLNamespace); - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns, - namespace_identity)); - ns = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_FALSE); - if (!ns) - goto fail; - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - goto fail; - } - XMLArrayTrim(&xml->xml_namespaces); - - /* Second pass: process tag name and attributes, using namespaces. */ - pn2 = pn->pn_head; - qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_FALSE); - if (!qn) - goto fail; - xml->name = qn; - - JS_ASSERT((n & 1) == 0); - n >>= 1; - if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n)) - goto fail; - - for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { - qn = ParseNodeToQName(cx, pn2, inScopeNSes, JS_TRUE); - if (!qn) { - xml->xml_attrs.length = i; - goto fail; - } - - /* - * Enforce "Well-formedness constraint: Unique Att Spec", part 2: - * this time checking local name and namespace URI. - */ - for (j = 0; j < i; j++) { - attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML); - attrjqn = attrj->name; - if (js_EqualStrings(attrjqn->uri, qn->uri) && - js_EqualStrings(attrjqn->localName, qn->localName)) { - js_ReportCompileErrorNumber(cx, pn2, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_DUPLICATE_XML_ATTR, - js_ValueToPrintableString(cx, - ATOM_KEY(pn2->pn_atom))); - goto fail; - } - } - - pn2 = pn2->pn_next; - JS_ASSERT(pn2); - JS_ASSERT(pn2->pn_type == TOK_XMLATTR); - - attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - if (!attr) - goto fail; - - XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr); - attr->parent = xml; - attr->name = qn; - attr->xml_value = ATOM_TO_STRING(pn2->pn_atom); - } - - /* Point tag closes its own namespace scope. */ - if (pn->pn_type == TOK_XMLPTAGC) - XMLARRAY_TRUNCATE(cx, inScopeNSes, length); - break; - - case TOK_XMLSPACE: - case TOK_XMLTEXT: - case TOK_XMLCDATA: - case TOK_XMLCOMMENT: - case TOK_XMLPI: - str = ATOM_TO_STRING(pn->pn_atom); - qn = NULL; - if (pn->pn_type == TOK_XMLCOMMENT) { - if (flags & XSF_IGNORE_COMMENTS) - goto skip_child; - xml_class = JSXML_CLASS_COMMENT; - } else if (pn->pn_type == TOK_XMLPI) { - if (IS_XML(str)) { - js_ReportCompileErrorNumber(cx, pn, - JSREPORT_PN | JSREPORT_ERROR, - JSMSG_RESERVED_ID, - js_ValueToPrintableString(cx, - STRING_TO_JSVAL(str))); - goto fail; - } - - if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) - goto skip_child; - - qn = ParseNodeToQName(cx, pn, inScopeNSes, JS_FALSE); - if (!qn) - goto fail; - - str = pn->pn_atom2 - ? ATOM_TO_STRING(pn->pn_atom2) - : cx->runtime->emptyString; - xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; - } else { - /* CDATA section content, or element text. */ - xml_class = JSXML_CLASS_TEXT; - } - - xml = js_NewXML(cx, xml_class); - if (!xml) - goto fail; - xml->name = qn; - if (pn->pn_type == TOK_XMLSPACE) - xml->xml_flags |= XMLF_WHITESPACE_TEXT; - xml->xml_value = str; - break; - - default: - goto syntax; - } - - js_LeaveLocalRootScopeWithResult(cx, (jsval) xml); - if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml)) - return NULL; - return xml; - -skip_child: - js_LeaveLocalRootScope(cx); - return PN2X_SKIP_CHILD; - -#undef PN2X_SKIP_CHILD - -syntax: - js_ReportCompileErrorNumber(cx, pn, JSREPORT_PN | JSREPORT_ERROR, - JSMSG_BAD_XML_MARKUP); -fail: - js_LeaveLocalRootScope(cx); - return NULL; -} - -/* - * XML helper, object-ops, and library functions. We start with the helpers, - * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers. - */ -static JSBool -GetXMLSetting(JSContext *cx, const char *name, jsval *vp) -{ - jsval v; - - if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v)) - return JS_FALSE; - if (!VALUE_IS_FUNCTION(cx, v)) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp); -} - -static JSBool -FillSettingsCache(JSContext *cx) -{ - int i; - const char *name; - jsval v; - JSBool isSet; - - /* Note: XML_PRETTY_INDENT is not a boolean setting. */ - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - name = xml_static_props[i].name; - if (!GetXMLSetting(cx, name, &v) || !js_ValueToBoolean(cx, v, &isSet)) - return JS_FALSE; - if (isSet) - cx->xmlSettingFlags |= JS_BIT(i); - else - cx->xmlSettingFlags &= ~JS_BIT(i); - } - - cx->xmlSettingFlags |= XSF_CACHE_VALID; - return JS_TRUE; -} - -static JSBool -GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp) -{ - int i; - - if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx)) - return JS_FALSE; - - for (i = 0; xml_static_props[i].name; i++) { - if (!strcmp(xml_static_props[i].name, name)) { - *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0; - return JS_TRUE; - } - } - *bp = JS_FALSE; - return JS_TRUE; -} - -static JSBool -GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip) -{ - jsval v; - - return GetXMLSetting(cx, name, &v) && js_ValueToECMAUint32(cx, v, uip); -} - -static JSBool -GetXMLSettingFlags(JSContext *cx, uintN *flagsp) -{ - JSBool flag; - - /* Just get the first flag to validate the setting flags cache. */ - if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag)) - return JS_FALSE; - *flagsp = cx->xmlSettingFlags; - return JS_TRUE; -} - -static JSXML * -ParseXMLSource(JSContext *cx, JSString *src) -{ - jsval nsval; - JSXMLNamespace *ns; - size_t urilen, srclen, length, offset, dstlen; - jschar *chars; - const jschar *srcp, *endp; - void *mark; - JSTokenStream *ts; - uintN lineno; - JSStackFrame *fp; - JSOp op; - JSParseNode *pn; - JSXML *xml; - JSXMLArray nsarray; - uintN flags; - - static const char prefix[] = ""; - static const char suffix[] = ""; - -#define constrlen(constr) (sizeof(constr) - 1) - - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return NULL; - ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - - urilen = JSSTRING_LENGTH(ns->uri); - srclen = JSSTRING_LENGTH(src); - length = constrlen(prefix) + urilen + constrlen(middle) + srclen + - constrlen(suffix); - - chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); - if (!chars) - return NULL; - - dstlen = length; - js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen); - offset = dstlen; - js_strncpy(chars + offset, JSSTRING_CHARS(ns->uri), urilen); - offset += urilen; - dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset, - &dstlen); - offset += dstlen; - srcp = JSSTRING_CHARS(src); - js_strncpy(chars + offset, srcp, srclen); - offset += srclen; - dstlen = length - offset + 1; - js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, - &dstlen); - chars [offset + dstlen] = 0; - - mark = JS_ARENA_MARK(&cx->tempPool); - ts = js_NewBufferTokenStream(cx, chars, length); - if (!ts) - return NULL; - for (fp = cx->fp; fp && !fp->pc; fp = fp->down) - continue; - if (fp) { - op = (JSOp) *fp->pc; - if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) { - ts->filename = fp->script->filename; - lineno = js_PCToLineNumber(cx, fp->script, fp->pc); - for (endp = srcp + srclen; srcp < endp; srcp++) - if (*srcp == '\n') - --lineno; - ts->lineno = lineno; - } - } - - JS_KEEP_ATOMS(cx->runtime); - pn = js_ParseXMLTokenStream(cx, cx->fp->scopeChain, ts, JS_FALSE); - xml = NULL; - if (pn && XMLArrayInit(cx, &nsarray, 1)) { - if (GetXMLSettingFlags(cx, &flags)) - xml = ParseNodeToXML(cx, pn, &nsarray, flags); - - XMLArrayFinish(cx, &nsarray); - } - JS_UNKEEP_ATOMS(cx->runtime); - - JS_ARENA_RELEASE(&cx->tempPool, mark); - JS_free(cx, chars); - return xml; - -#undef constrlen -} - -/* - * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least). - * - * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce - * the constraint: - * - * for all x belonging to XML: - * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]] - * - * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here - * (in new sub-step 6(a), renumbering the others to (b) and (c)). - * - * Same goes for 10.4.1 Step 7(a). - * - * In order for XML.prototype.namespaceDeclarations() to work correctly, the - * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be - * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such - * undeclared namespaces associated with x not belonging to ancestorNS. - */ -static JSXML * -OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i) -{ - JSXMLNamespace *ns; - - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSXMLNamespace); - xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!ns || !xml) - return xml; - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - return NULL; - ns->declared = JS_FALSE; - } - xml->parent = NULL; - return xml; -} - -static JSObject * -ToXML(JSContext *cx, jsval v) -{ - JSObject *obj; - JSXML *xml; - JSClass *clasp; - JSString *str; - uint32 length; - - if (JSVAL_IS_PRIMITIVE(v)) { - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - goto bad; - } else { - obj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, obj)) { - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length != 1) - goto bad; - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - return js_GetXMLObject(cx, xml); - } - } - return obj; - } - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { - JS_ASSERT(0); - } - - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { - goto bad; - } - } - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - if (IS_EMPTY(str)) { - length = 0; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - xml = NULL; -#endif - } else { - xml = ParseXMLSource(cx, str); - if (!xml) - return NULL; - length = JSXML_LENGTH(xml); - } - - if (length == 0) { - obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT); - if (!obj) - return NULL; - } else if (length == 1) { - xml = OrphanXMLChild(cx, xml, 0); - if (!xml) - return NULL; - obj = js_GetXMLObject(cx, xml); - if (!obj) - return NULL; - } else { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR); - return NULL; - } - return obj; - -bad: - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_CONVERSION, - JS_GetStringBytes(str)); - } - return NULL; -} - -static JSBool -Append(JSContext *cx, JSXML *list, JSXML *kid); - -static JSObject * -ToXMLList(JSContext *cx, jsval v) -{ - JSObject *obj, *listobj; - JSXML *xml, *list, *kid; - JSClass *clasp; - JSString *str; - uint32 i, length; - - if (JSVAL_IS_PRIMITIVE(v)) { - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - goto bad; - } else { - obj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, obj)) { - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class != JSXML_CLASS_LIST) { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (!Append(cx, list, xml)) - return NULL; - return listobj; - } - return obj; - } - - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) { - JS_ASSERT(0); - } - - if (clasp != &js_StringClass && - clasp != &js_NumberClass && - clasp != &js_BooleanClass) { - goto bad; - } - } - - str = js_ValueToString(cx, v); - if (!str) - return NULL; - if (IS_EMPTY(str)) { - xml = NULL; - length = 0; - } else { - if (!js_EnterLocalRootScope(cx)) - return NULL; - xml = ParseXMLSource(cx, str); - if (!xml) { - js_LeaveLocalRootScope(cx); - return NULL; - } - length = JSXML_LENGTH(xml); - } - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (listobj) { - list = (JSXML *) JS_GetPrivate(cx, listobj); - for (i = 0; i < length; i++) { - kid = OrphanXMLChild(cx, xml, i); - if (!kid || !Append(cx, list, kid)) { - listobj = NULL; - break; - } - } - } - - if (xml) - js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); - return listobj; - -bad: - str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (str) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XMLLIST_CONVERSION, - JS_GetStringBytes(str)); - } - return NULL; -} - -/* - * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString - * and their library-public js_* counterparts. The guts of MakeXMLCDataString, - * MakeXMLCommentString, and MakeXMLPIString are further factored into a common - * MakeXMLSpecialString subroutine. - * - * These functions take ownership of sb->base, if sb is non-null, in all cases - * of success or failure. - */ -static JSString * -MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb, - JSString *str, JSString *str2, - const jschar *prefix, size_t prefixlength, - const jschar *suffix, size_t suffixlength) -{ - JSStringBuffer localSB; - size_t length, length2, newlength; - jschar *bp, *base; - - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - - length = JSSTRING_LENGTH(str); - length2 = str2 ? JSSTRING_LENGTH(str2) : 0; - newlength = STRING_BUFFER_OFFSET(sb) + - prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) + - suffixlength; - bp = base = (jschar *) - JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar)); - if (!bp) { - js_FinishStringBuffer(sb); - return NULL; - } - - bp += STRING_BUFFER_OFFSET(sb); - js_strncpy(bp, prefix, prefixlength); - bp += prefixlength; - js_strncpy(bp, JSSTRING_CHARS(str), length); - bp += length; - if (length2 != 0) { - *bp++ = (jschar) ' '; - js_strncpy(bp, JSSTRING_CHARS(str2), length2); - bp += length2; - } - js_strncpy(bp, suffix, suffixlength); - bp[suffixlength] = 0; - - str = js_NewString(cx, base, newlength, 0); - if (!str) - free(base); - return str; -} - -static JSString * -MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[', - 'C', 'D', 'A', 'T', 'A', - '['}; - static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'}; - - return MakeXMLSpecialString(cx, sb, str, NULL, - cdata_prefix_ucNstr, 9, - cdata_suffix_ucNstr, 3); -} - -static JSString * -MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'}; - static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'}; - - return MakeXMLSpecialString(cx, sb, str, NULL, - comment_prefix_ucNstr, 4, - comment_suffix_ucNstr, 3); -} - -static JSString * -MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name, - JSString *value) -{ - static const jschar pi_prefix_ucNstr[] = {'<', '?'}; - static const jschar pi_suffix_ucNstr[] = {'?', '>'}; - - return MakeXMLSpecialString(cx, sb, name, value, - pi_prefix_ucNstr, 2, - pi_suffix_ucNstr, 2); -} - -/* - * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends - * equals, a double quote, an attribute value, and a closing double quote. - */ -static void -AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr) -{ - js_AppendCString(sb, "=\""); - valstr = js_EscapeAttributeValue(cx, valstr); - if (!valstr) { - free(sb->base); - sb->base = STRING_BUFFER_ERROR_BASE; - return; - } - js_AppendJSString(sb, valstr); - js_AppendChar(sb, '"'); -} - -/* - * ECMA-357 10.2.1.1 EscapeElementValue helper method. - * - * This function takes ownership of sb->base, if sb is non-null, in all cases - * of success or failure. - */ -static JSString * -EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - size_t length, newlength; - const jschar *cp, *start, *end; - jschar c; - - length = newlength = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (c == '<' || c == '>') - newlength += 3; - else if (c == '&') - newlength += 4; - - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { - JSStringBuffer localSB; - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - if (!sb->grow(sb, newlength)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - for (cp = start; cp < end; cp++) { - c = *cp; - if (c == '<') - js_AppendCString(sb, js_lt_entity_str); - else if (c == '>') - js_AppendCString(sb, js_gt_entity_str); - else if (c == '&') - js_AppendCString(sb, js_amp_entity_str); - else - js_AppendChar(sb, c); - } - JS_ASSERT(STRING_BUFFER_OK(sb)); - str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); - if (!str) - js_FinishStringBuffer(sb); - } - return str; -} - -/* - * ECMA-357 10.2.1.2 EscapeAttributeValue helper method. - * This function takes ownership of sb->base, if sb is non-null, in all cases. - */ -static JSString * -EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str) -{ - size_t length, newlength; - const jschar *cp, *start, *end; - jschar c; - - length = newlength = JSSTRING_LENGTH(str); - for (cp = start = JSSTRING_CHARS(str), end = cp + length; cp < end; cp++) { - c = *cp; - if (c == '"') - newlength += 5; - else if (c == '<') - newlength += 3; - else if (c == '&' || c == '\n' || c == '\r' || c == '\t') - newlength += 4; - - if (newlength < length) { - JS_ReportOutOfMemory(cx); - return NULL; - } - } - if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) { - JSStringBuffer localSB; - if (!sb) { - sb = &localSB; - js_InitStringBuffer(sb); - } - if (!sb->grow(sb, newlength)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - for (cp = start; cp < end; cp++) { - c = *cp; - if (c == '"') - js_AppendCString(sb, js_quot_entity_str); - else if (c == '<') - js_AppendCString(sb, js_lt_entity_str); - else if (c == '&') - js_AppendCString(sb, js_amp_entity_str); - else if (c == '\n') - js_AppendCString(sb, " "); - else if (c == '\r') - js_AppendCString(sb, " "); - else if (c == '\t') - js_AppendCString(sb, " "); - else - js_AppendChar(sb, c); - } - JS_ASSERT(STRING_BUFFER_OK(sb)); - str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb), 0); - if (!str) - js_FinishStringBuffer(sb); - } - return str; -} - -/* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */ -static JSXMLNamespace * -GetNamespace(JSContext *cx, JSXMLQName *qn, const JSXMLArray *inScopeNSes) -{ - JSXMLNamespace *match, *ns; - uint32 i, n; - jsval argv[2]; - JSObject *nsobj; - - JS_ASSERT(qn->uri); - if (!qn->uri) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAMESPACE, - qn->prefix - ? js_ValueToPrintableString(cx, - STRING_TO_JSVAL(qn->prefix)) - : js_type_strs[JSTYPE_VOID]); - return NULL; - } - - /* Look for a matching namespace in inScopeNSes, if provided. */ - match = NULL; - if (inScopeNSes) { - for (i = 0, n = inScopeNSes->length; i < n; i++) { - ns = XMLARRAY_MEMBER(inScopeNSes, i, JSXMLNamespace); - if (!ns) - continue; - - /* - * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4: - * If we preserve prefixes, we must match null qn->prefix against - * an empty ns->prefix, in order to avoid generating redundant - * prefixed and default namespaces for cases such as: - * - * x = - * print(x.toXMLString()); - * - * Per 10.3.2.1, the namespace attribute in t has an empty string - * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1): - * - * 1. If the [local name] property of a is "xmlns" - * a. Map ns.prefix to the empty string - * - * But t's name has a null prefix in this implementation, meaning - * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to - * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without - * saying how "no value" maps to an ECMA-357 value -- but it must - * map to the *undefined* prefix value). - * - * Since "" != undefined (or null, in the current implementation) - * the ECMA-357 spec will fail to match in [[GetNamespace]] called - * on t with argument {} U {(prefix="", uri="http://foo.com")}. - * This spec bug leads to ToXMLString results that duplicate the - * declared namespace. - */ - if (js_EqualStrings(ns->uri, qn->uri) && - (ns->prefix == qn->prefix || - ((ns->prefix && qn->prefix) - ? js_EqualStrings(ns->prefix, qn->prefix) - : IS_EMPTY(ns->prefix ? ns->prefix : qn->prefix)))) { - match = ns; - break; - } - } - } - - /* If we didn't match, make a new namespace from qn. */ - if (!match) { - argv[0] = qn->prefix ? STRING_TO_JSVAL(qn->prefix) : JSVAL_VOID; - argv[1] = STRING_TO_JSVAL(qn->uri); - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, - 2, argv); - if (!nsobj) - return NULL; - match = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - } - return match; -} - -static JSString * -GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls) -{ - const jschar *cp, *start, *end; - size_t length, newlength, offset; - uint32 i, n, m, serial; - jschar *bp, *dp; - JSBool done; - JSXMLNamespace *ns; - JSString *prefix; - - JS_ASSERT(!IS_EMPTY(uri)); - - /* - * If there are no *declared* namespaces, skip all collision detection and - * return a short prefix quickly; an example of such a situation: - * - * var x = ; - * var n = new Namespace("http://example.com/"); - * x.@n::att = "val"; - * x.toXMLString(); - * - * This is necessary for various log10 uses below to be valid. - */ - if (decls->length == 0) - return JS_NewStringCopyZ(cx, "a"); - - /* - * Try peeling off the last filename suffix or pathname component till - * we have a valid XML name. This heuristic will prefer "xul" given - * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any - * likely URI of the form ".../xbl2/2005". - */ - start = JSSTRING_CHARS(uri); - cp = end = start + JSSTRING_LENGTH(uri); - while (--cp > start) { - if (*cp == '.' || *cp == '/' || *cp == ':') { - ++cp; - length = PTRDIFF(end, cp, jschar); - if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length)) - break; - end = --cp; - } - } - length = PTRDIFF(end, cp, jschar); - - /* - * If the namespace consisted only of non-XML names or names that begin - * case-insensitively with "xml", arbitrarily create a prefix consisting - * of 'a's of size length (allowing dp-calculating code to work with or - * without this branch executing) plus the space for storing a hyphen and - * the serial number (avoiding reallocation if a collision happens). - */ - bp = (jschar *) cp; - newlength = length; - if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) { - newlength = length + 2 + (size_t) log10(decls->length); - bp = (jschar *) - JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!bp) - return NULL; - - bp[newlength] = 0; - for (i = 0; i < newlength; i++) - bp[i] = 'a'; - } - - /* - * Now search through decls looking for a collision. If we collide with - * an existing prefix, start tacking on a hyphen and a serial number. - */ - serial = 0; - do { - done = JS_TRUE; - for (i = 0, n = decls->length; i < n; i++) { - ns = XMLARRAY_MEMBER(decls, i, JSXMLNamespace); - if (ns && ns->prefix && - JSSTRING_LENGTH(ns->prefix) == newlength && - !memcmp(JSSTRING_CHARS(ns->prefix), bp, - newlength * sizeof(jschar))) { - if (bp == cp) { - newlength = length + 2 + (size_t) log10(n); - bp = (jschar *) - JS_malloc(cx, (newlength + 1) * sizeof(jschar)); - if (!bp) - return NULL; - js_strncpy(bp, cp, length); - } - - ++serial; - JS_ASSERT(serial <= n); - dp = bp + length + 2 + (size_t) log10(serial); - *dp = 0; - for (m = serial; m != 0; m /= 10) - *--dp = (jschar)('0' + m % 10); - *--dp = '-'; - JS_ASSERT(dp == bp + length); - - done = JS_FALSE; - break; - } - } - } while (!done); - - if (bp == cp) { - offset = PTRDIFF(cp, start, jschar); - prefix = js_NewDependentString(cx, uri, offset, length, 0); - } else { - prefix = js_NewString(cx, bp, newlength, 0); - if (!prefix) - JS_free(cx, bp); - } - return prefix; -} - -static JSBool -namespace_match(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsb->prefix) - return nsa->prefix && js_EqualStrings(nsa->prefix, nsb->prefix); - return js_EqualStrings(nsa->uri, nsb->uri); -} - -/* ECMA-357 10.2.1 and 10.2.2 */ -static JSString * -XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes, - uintN indentLevel) -{ - JSBool pretty, indentKids; - JSStringBuffer sb; - JSString *str, *prefix, *kidstr; - JSXMLArrayCursor cursor; - uint32 i, n; - JSXMLArray empty, decls, ancdecls; - JSXMLNamespace *ns, *ns2; - uintN nextIndentLevel; - JSXML *attr, *kid; - - if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty)) - return NULL; - - js_InitStringBuffer(&sb); - if (pretty) - js_RepeatChar(&sb, ' ', indentLevel); - str = NULL; - - switch (xml->xml_class) { - case JSXML_CLASS_TEXT: - /* Step 4. */ - if (pretty) { - str = ChompXMLWhitespace(cx, xml->xml_value); - if (!str) - return NULL; - } else { - str = xml->xml_value; - } - return EscapeElementValue(cx, &sb, str); - - case JSXML_CLASS_ATTRIBUTE: - /* Step 5. */ - return EscapeAttributeValue(cx, &sb, xml->xml_value); - - case JSXML_CLASS_COMMENT: - /* Step 6. */ - return MakeXMLCommentString(cx, &sb, xml->xml_value); - - case JSXML_CLASS_PROCESSING_INSTRUCTION: - /* Step 7. */ - return MakeXMLPIString(cx, &sb, xml->name->localName, xml->xml_value); - - case JSXML_CLASS_LIST: - /* ECMA-357 10.2.2. */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - i = 0; - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (pretty && i != 0) - js_AppendChar(&sb, '\n'); - - kidstr = XMLToXMLString(cx, kid, ancestorNSes, indentLevel); - if (!kidstr) - break; - - js_AppendJSString(&sb, kidstr); - ++i; - } - XMLArrayCursorFinish(&cursor); - if (kid) - goto list_out; - - if (!sb.base) { - if (!STRING_BUFFER_OK(&sb)) { - JS_ReportOutOfMemory(cx); - return NULL; - } - return cx->runtime->emptyString; - } - - str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); - list_out: - if (!str) - js_FinishStringBuffer(&sb); - return str; - - default:; - } - - /* After this point, control must flow through label out: to exit. */ - if (!js_EnterLocalRootScope(cx)) - return NULL; - - /* ECMA-357 10.2.1 step 8 onward: handle ToXMLString on an XML element. */ - if (!ancestorNSes) { - XMLArrayInit(cx, &empty, 0); - ancestorNSes = ∅ - } - XMLArrayInit(cx, &decls, 0); - ancdecls.capacity = 0; - - /* Clone in-scope namespaces not in ancestorNSes into decls. */ - XMLArrayCursorInit(&cursor, &xml->xml_namespaces); - while ((ns = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { - if (!ns->declared) - continue; - if (!XMLARRAY_HAS_MEMBER(ancestorNSes, ns, namespace_identity)) { - /* NOTE: may want to exclude unused namespaces here. */ - ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, JS_TRUE); - if (!ns2 || !XMLARRAY_APPEND(cx, &decls, ns2)) - break; - } - } - XMLArrayCursorFinish(&cursor); - if (ns) - goto out; - - /* - * Union ancestorNSes and decls into ancdecls. Note that ancdecls does - * not own its member references. In the spec, ancdecls has no name, but - * is always written out as (AncestorNamespaces U namespaceDeclarations). - */ - if (!XMLArrayInit(cx, &ancdecls, ancestorNSes->length + decls.length)) - goto out; - for (i = 0, n = ancestorNSes->length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(ancestorNSes, i, JSXMLNamespace); - if (!ns2) - continue; - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&decls, ns2, namespace_identity)); - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) - goto out; - } - for (i = 0, n = decls.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&decls, i, JSXMLNamespace); - if (!ns2) - continue; - JS_ASSERT(!XMLARRAY_HAS_MEMBER(&ancdecls, ns2, namespace_identity)); - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2)) - goto out; - } - - /* Step 11, except we don't clone ns unless its prefix is undefined. */ - ns = GetNamespace(cx, xml->name, &ancdecls); - if (!ns) - goto out; - - /* Step 12 (NULL means *undefined* here), plus the deferred ns cloning. */ - if (!ns->prefix) { - /* - * Create a namespace prefix that isn't used by any member of decls. - * Assign the new prefix to a copy of ns. Flag this namespace as if - * it were declared, for assertion-testing's sake later below. - * - * Erratum: if ns->prefix and xml->name are both null (*undefined* in - * ECMA-357), we know that xml was named using the default namespace - * (proof: see GetNamespace and the Namespace constructor called with - * two arguments). So we ought not generate a new prefix here, when - * we can declare ns as the default namespace for xml. - * - * This helps descendants inherit the namespace instead of redundantly - * redeclaring it with generated prefixes in each descendant. - */ - if (!xml->name->prefix) { - prefix = cx->runtime->emptyString; - } else { - prefix = GeneratePrefix(cx, ns->uri, &ancdecls); - if (!prefix) - goto out; - } - ns = js_NewXMLNamespace(cx, prefix, ns->uri, JS_TRUE); - if (!ns) - goto out; - - /* - * If the xml->name was unprefixed, we must remove any declared default - * namespace from decls before appending ns. How can you get a default - * namespace in decls that doesn't match the one from name? Apparently - * by calling x.setNamespace(ns) where ns has no prefix. The other way - * to fix this is to update x's in-scope namespaces when setNamespace - * is called, but that's not specified by ECMA-357. - * - * Likely Erratum here, depending on whether the lack of update to x's - * in-scope namespace in XML.prototype.setNamespace (13.4.4.36) is an - * erratum or not. Note that changing setNamespace to update the list - * of in-scope namespaces will change x.namespaceDeclarations(). - */ - if (IS_EMPTY(prefix)) { - i = XMLArrayFindMember(&decls, ns, namespace_match); - if (i != XML_NOT_FOUND) - XMLArrayDelete(cx, &decls, i, JS_TRUE); - } - - /* - * In the spec, ancdecls has no name, but is always written out as - * (AncestorNamespaces U namespaceDeclarations). Since we compute - * that union in ancdecls, any time we append a namespace strong - * ref to decls, we must also append a weak ref to ancdecls. Order - * matters here: code at label out: releases strong refs in decls. - */ - if (!XMLARRAY_APPEND(cx, &ancdecls, ns) || - !XMLARRAY_APPEND(cx, &decls, ns)) { - goto out; - } - } - - /* Format the element or point-tag into sb. */ - js_AppendChar(&sb, '<'); - - if (ns->prefix && !IS_EMPTY(ns->prefix)) { - js_AppendJSString(&sb, ns->prefix); - js_AppendChar(&sb, ':'); - } - js_AppendJSString(&sb, xml->name->localName); - - /* - * Step 16 makes a union to avoid writing two loops in step 17, to share - * common attribute value appending spec-code. We prefer two loops for - * faster code and less data overhead. - */ - - /* Step 17(b): append attributes. */ - XMLArrayCursorInit(&cursor, &xml->xml_attrs); - while ((attr = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - js_AppendChar(&sb, ' '); - ns2 = GetNamespace(cx, attr->name, &ancdecls); - if (!ns2) - break; - - /* 17(b)(ii): NULL means *undefined* here. */ - if (!ns2->prefix) { - prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); - if (!prefix) - break; - - /* Again, we avoid copying ns2 until we know it's prefix-less. */ - ns2 = js_NewXMLNamespace(cx, prefix, ns2->uri, JS_TRUE); - if (!ns2) - break; - - /* - * In the spec, ancdecls has no name, but is always written out as - * (AncestorNamespaces U namespaceDeclarations). Since we compute - * that union in ancdecls, any time we append a namespace strong - * ref to decls, we must also append a weak ref to ancdecls. Order - * matters here: code at label out: releases strong refs in decls. - */ - if (!XMLARRAY_APPEND(cx, &ancdecls, ns2) || - !XMLARRAY_APPEND(cx, &decls, ns2)) { - break; - } - } - - /* 17(b)(iii). */ - if (!IS_EMPTY(ns2->prefix)) { - js_AppendJSString(&sb, ns2->prefix); - js_AppendChar(&sb, ':'); - } - - /* 17(b)(iv). */ - js_AppendJSString(&sb, attr->name->localName); - - /* 17(d-g). */ - AppendAttributeValue(cx, &sb, attr->xml_value); - } - XMLArrayCursorFinish(&cursor); - if (attr) - goto out; - - /* Step 17(c): append XML namespace declarations. */ - XMLArrayCursorInit(&cursor, &decls); - while ((ns2 = (JSXMLNamespace *) XMLArrayCursorNext(&cursor)) != NULL) { - JS_ASSERT(ns2->declared); - - js_AppendCString(&sb, " xmlns"); - - /* 17(c)(ii): NULL means *undefined* here. */ - if (!ns2->prefix) { - prefix = GeneratePrefix(cx, ns2->uri, &ancdecls); - if (!prefix) - break; - ns2->prefix = prefix; - } - - /* 17(c)(iii). */ - if (!IS_EMPTY(ns2->prefix)) { - js_AppendChar(&sb, ':'); - js_AppendJSString(&sb, ns2->prefix); - } - - /* 17(d-g). */ - AppendAttributeValue(cx, &sb, ns2->uri); - } - XMLArrayCursorFinish(&cursor); - if (ns2) - goto out; - - /* Step 18: handle point tags. */ - n = xml->xml_kids.length; - if (n == 0) { - js_AppendCString(&sb, "/>"); - } else { - /* Steps 19 through 25: handle element content, and open the end-tag. */ - js_AppendChar(&sb, '>'); - indentKids = n > 1 || - (n == 1 && - (kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML)) && - kid->xml_class != JSXML_CLASS_TEXT); - - if (pretty && indentKids) { - if (!GetUint32XMLSetting(cx, js_prettyIndent_str, &i)) - goto out; - nextIndentLevel = indentLevel + i; - } else { - nextIndentLevel = 0; - } - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (pretty && indentKids) - js_AppendChar(&sb, '\n'); - - kidstr = XMLToXMLString(cx, kid, &ancdecls, nextIndentLevel); - if (!kidstr) - break; - - js_AppendJSString(&sb, kidstr); - } - XMLArrayCursorFinish(&cursor); - if (kid) - goto out; - - if (pretty && indentKids) { - js_AppendChar(&sb, '\n'); - js_RepeatChar(&sb, ' ', indentLevel); - } - js_AppendCString(&sb, "prefix && !IS_EMPTY(ns->prefix)) { - js_AppendJSString(&sb, ns->prefix); - js_AppendChar(&sb, ':'); - } - - /* Step 27. */ - js_AppendJSString(&sb, xml->name->localName); - js_AppendChar(&sb, '>'); - } - - if (!STRING_BUFFER_OK(&sb)) { - JS_ReportOutOfMemory(cx); - goto out; - } - - str = js_NewString(cx, sb.base, STRING_BUFFER_OFFSET(&sb), 0); -out: - js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); - if (!str && STRING_BUFFER_OK(&sb)) - js_FinishStringBuffer(&sb); - XMLArrayFinish(cx, &decls); - if (ancdecls.capacity != 0) - XMLArrayFinish(cx, &ancdecls); - return str; -} - -/* ECMA-357 10.2 */ -static JSString * -ToXMLString(JSContext *cx, jsval v) -{ - JSObject *obj; - JSString *str; - JSXML *xml; - - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_CONVERSION, - js_type_strs[JSVAL_IS_NULL(v) - ? JSTYPE_NULL - : JSTYPE_VOID]); - return NULL; - } - - if (JSVAL_IS_BOOLEAN(v) || JSVAL_IS_NUMBER(v)) - return js_ValueToString(cx, v); - - if (JSVAL_IS_STRING(v)) - return EscapeElementValue(cx, NULL, JSVAL_TO_STRING(v)); - - obj = JSVAL_TO_OBJECT(v); - if (!OBJECT_IS_XML(cx, obj)) { - if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, &v)) - return NULL; - str = js_ValueToString(cx, v); - if (!str) - return NULL; - return EscapeElementValue(cx, NULL, str); - } - - /* Handle non-element cases in this switch, returning from each case. */ - xml = (JSXML *) JS_GetPrivate(cx, obj); - return XMLToXMLString(cx, xml, NULL, 0); -} - -static JSXMLQName * -ToAttributeName(JSContext *cx, jsval v) -{ - JSString *name, *uri, *prefix; - JSObject *obj; - JSClass *clasp; - JSXMLQName *qn; - JSTempValueRooter tvr; - - if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); - uri = prefix = cx->runtime->emptyString; - } else { - if (JSVAL_IS_PRIMITIVE(v)) { - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (name) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_ATTR_NAME, - JS_GetStringBytes(name)); - } - return NULL; - } - - obj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass) - return (JSXMLQName *) JS_GetPrivate(cx, obj); - - if (clasp == &js_QNameClass.base) { - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - uri = qn->uri; - prefix = qn->prefix; - name = qn->localName; - } else { - if (clasp == &js_AnyNameClass) { - name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); - } else { - name = js_ValueToString(cx, v); - if (!name) - return NULL; - } - uri = prefix = cx->runtime->emptyString; - } - } - - qn = js_NewXMLQName(cx, uri, prefix, name); - if (!qn) - return NULL; - - JS_PUSH_TEMP_ROOT_GCTHING(cx, qn, &tvr); - obj = js_GetAttributeNameObject(cx, qn); - JS_POP_TEMP_ROOT(cx, &tvr); - if (!obj) - return NULL; - return qn; -} - -static JSXMLQName * -ToXMLName(JSContext *cx, jsval v, jsid *funidp) -{ - JSString *name; - JSObject *obj; - JSClass *clasp; - uint32 index; - JSXMLQName *qn; - JSAtom *atom; - - if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v); - } else { - if (JSVAL_IS_PRIMITIVE(v)) { - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, v, NULL); - if (name) - goto bad; - return NULL; - } - - obj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, obj); - if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass.base) - goto out; - if (clasp == &js_AnyNameClass) { - name = ATOM_TO_STRING(cx->runtime->atomState.starAtom); - goto construct; - } - name = js_ValueToString(cx, v); - if (!name) - return NULL; - } - - /* - * ECMA-357 10.6.1 step 1 seems to be incorrect. The spec says: - * - * 1. If ToString(ToNumber(P)) == ToString(P), throw a TypeError exception - * - * First, _P_ should be _s_, to refer to the given string. - * - * Second, why does ToXMLName applied to the string type throw TypeError - * only for numeric literals without any leading or trailing whitespace? - * - * If the idea is to reject uint32 property names, then the check needs to - * be stricter, to exclude hexadecimal and floating point literals. - */ - if (js_IdIsIndex(STRING_TO_JSVAL(name), &index)) - goto bad; - - if (*JSSTRING_CHARS(name) == '@') { - name = js_NewDependentString(cx, name, 1, JSSTRING_LENGTH(name) - 1, 0); - if (!name) - return NULL; - *funidp = 0; - return ToAttributeName(cx, STRING_TO_JSVAL(name)); - } - -construct: - v = STRING_TO_JSVAL(name); - obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &v); - if (!obj) - return NULL; - -out: - qn = (JSXMLQName *) JS_GetPrivate(cx, obj); - atom = cx->runtime->atomState.lazy.functionNamespaceURIAtom; - if (qn->uri && atom && - (qn->uri == ATOM_TO_STRING(atom) || - js_EqualStrings(qn->uri, ATOM_TO_STRING(atom)))) { - if (!JS_ValueToId(cx, STRING_TO_JSVAL(qn->localName), funidp)) - return NULL; - } else { - *funidp = 0; - } - return qn; - -bad: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAME, - js_ValueToPrintableString(cx, STRING_TO_JSVAL(name))); - return NULL; -} - -/* ECMA-357 9.1.1.13 XML [[AddInScopeNamespace]]. */ -static JSBool -AddInScopeNamespace(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) -{ - JSXMLNamespace *match, *ns2; - uint32 i, n, m; - - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - /* NULL means *undefined* here -- see ECMA-357 9.1.1.13 step 2. */ - if (!ns->prefix) { - match = NULL; - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && js_EqualStrings(ns2->uri, ns->uri)) { - match = ns2; - break; - } - } - if (!match && !XMLARRAY_ADD_MEMBER(cx, &xml->xml_namespaces, n, ns)) - return JS_FALSE; - } else { - if (IS_EMPTY(ns->prefix) && IS_EMPTY(xml->name->uri)) - return JS_TRUE; - match = NULL; -#ifdef __GNUC__ /* suppress bogus gcc warnings */ - m = XML_NOT_FOUND; -#endif - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns2 = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns2 && ns2->prefix && - js_EqualStrings(ns2->prefix, ns->prefix)) { - match = ns2; - m = i; - break; - } - } - if (match && !js_EqualStrings(match->uri, ns->uri)) { - ns2 = XMLARRAY_DELETE(cx, &xml->xml_namespaces, m, JS_TRUE, - JSXMLNamespace); - JS_ASSERT(ns2 == match); - match->prefix = NULL; - if (!AddInScopeNamespace(cx, xml, match)) - return JS_FALSE; - } - if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) - return JS_FALSE; - } - - /* OPTION: enforce that descendants have superset namespaces. */ - return JS_TRUE; -} - -/* ECMA-357 9.2.1.6 XMLList [[Append]]. */ -static JSBool -Append(JSContext *cx, JSXML *list, JSXML *xml) -{ - uint32 i, j, k, n; - JSXML *kid; - - JS_ASSERT(list->xml_class == JSXML_CLASS_LIST); - i = list->xml_kids.length; - n = 1; - if (xml->xml_class == JSXML_CLASS_LIST) { - list->xml_target = xml->xml_target; - list->xml_targetprop = xml->xml_targetprop; - n = JSXML_LENGTH(xml); - k = i + n; - if (!XMLArraySetCapacity(cx, &list->xml_kids, k)) - return JS_FALSE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, j, JSXML); - if (kid) - XMLARRAY_SET_MEMBER(&list->xml_kids, i + j, kid); - } - return JS_TRUE; - } - - list->xml_target = xml->parent; - if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - list->xml_targetprop = NULL; - else - list->xml_targetprop = xml->name; - if (!XMLARRAY_ADD_MEMBER(cx, &list->xml_kids, i, xml)) - return JS_FALSE; - return JS_TRUE; -} - -/* ECMA-357 9.1.1.7 XML [[DeepCopy]] and 9.2.1.7 XMLList [[DeepCopy]]. */ -static JSXML * -DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags); - -static JSXML * -DeepCopy(JSContext *cx, JSXML *xml, JSObject *obj, uintN flags) -{ - JSXML *copy; - JSBool ok; - - /* Our caller may not be protecting newborns with a local root scope. */ - if (!js_EnterLocalRootScope(cx)) - return NULL; - copy = DeepCopyInLRS(cx, xml, flags); - if (copy) { - if (obj) { - /* Caller provided the object for this copy, hook 'em up. */ - ok = JS_SetPrivate(cx, obj, copy); - if (ok) - copy->object = obj; - } else { - ok = js_GetXMLObject(cx, copy) != NULL; - } - if (!ok) - copy = NULL; - } - js_LeaveLocalRootScopeWithResult(cx, (jsval) copy); - return copy; -} - -/* - * (i) We must be in a local root scope (InLRS). - * (ii) parent must have a rooted object. - * (iii) from's owning object must be locked if not thread-local. - */ -static JSBool -DeepCopySetInLRS(JSContext *cx, JSXMLArray *from, JSXMLArray *to, JSXML *parent, - uintN flags) -{ - uint32 j, n; - JSXMLArrayCursor cursor; - JSBool ok; - JSXML *kid, *kid2; - JSString *str; - - JS_ASSERT(cx->localRootStack); - - n = from->length; - if (!XMLArraySetCapacity(cx, to, n)) - return JS_FALSE; - - XMLArrayCursorInit(&cursor, from); - j = 0; - ok = JS_TRUE; - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if ((flags & XSF_IGNORE_COMMENTS) && - kid->xml_class == JSXML_CLASS_COMMENT) { - continue; - } - if ((flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) && - kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) { - continue; - } - if ((flags & XSF_IGNORE_WHITESPACE) && - (kid->xml_flags & XMLF_WHITESPACE_TEXT)) { - continue; - } - kid2 = DeepCopyInLRS(cx, kid, flags); - if (!kid2) { - to->length = j; - ok = JS_FALSE; - break; - } - - if ((flags & XSF_IGNORE_WHITESPACE) && - n > 1 && kid2->xml_class == JSXML_CLASS_TEXT) { - str = ChompXMLWhitespace(cx, kid2->xml_value); - if (!str) { - to->length = j; - ok = JS_FALSE; - break; - } - kid2->xml_value = str; - } - - XMLARRAY_SET_MEMBER(to, j, kid2); - ++j; - if (parent->xml_class != JSXML_CLASS_LIST) - kid2->parent = parent; - } - XMLArrayCursorFinish(&cursor); - if (!ok) - return JS_FALSE; - - if (j < n) - XMLArrayTrim(to); - return JS_TRUE; -} - -static JSXML * -DeepCopyInLRS(JSContext *cx, JSXML *xml, uintN flags) -{ - JSXML *copy; - JSXMLQName *qn; - JSBool ok; - uint32 i, n; - JSXMLNamespace *ns, *ns2; - - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(cx->localRootStack); - - copy = js_NewXML(cx, xml->xml_class); - if (!copy) - return NULL; - qn = xml->name; - if (qn) { - qn = js_NewXMLQName(cx, qn->uri, qn->prefix, qn->localName); - if (!qn) { - ok = JS_FALSE; - goto out; - } - } - copy->name = qn; - copy->xml_flags = xml->xml_flags; - - if (JSXML_HAS_VALUE(xml)) { - copy->xml_value = xml->xml_value; - ok = JS_TRUE; - } else { - ok = DeepCopySetInLRS(cx, &xml->xml_kids, ©->xml_kids, copy, flags); - if (!ok) - goto out; - - if (xml->xml_class == JSXML_CLASS_LIST) { - copy->xml_target = xml->xml_target; - copy->xml_targetprop = xml->xml_targetprop; - } else { - n = xml->xml_namespaces.length; - ok = XMLArraySetCapacity(cx, ©->xml_namespaces, n); - if (!ok) - goto out; - for (i = 0; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - ns2 = js_NewXMLNamespace(cx, ns->prefix, ns->uri, ns->declared); - if (!ns2) { - copy->xml_namespaces.length = i; - ok = JS_FALSE; - goto out; - } - XMLARRAY_SET_MEMBER(©->xml_namespaces, i, ns2); - } - - ok = DeepCopySetInLRS(cx, &xml->xml_attrs, ©->xml_attrs, copy, - 0); - if (!ok) - goto out; - } - } - -out: - if (!ok) - return NULL; - return copy; -} - -static void -ReportBadXMLName(JSContext *cx, jsval id) -{ - JSString *name; - - name = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, id, NULL); - if (name) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XML_NAME, - JS_GetStringBytes(name)); - } -} - -/* ECMA-357 9.1.1.4 XML [[DeleteByIndex]]. */ -static JSBool -DeleteByIndex(JSContext *cx, JSXML *xml, jsval id, jsval *vp) -{ - uint32 index; - JSXML *kid; - - if (!js_IdIsIndex(id, &index)) { - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - if (JSXML_HAS_KIDS(xml) && index < xml->xml_kids.length) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (kid) - kid->parent = NULL; - XMLArrayDelete(cx, &xml->xml_kids, index, JS_TRUE); - } - - *vp = JSVAL_TRUE; - return JS_TRUE; -} - -typedef JSBool (*JSXMLNameMatcher)(JSXMLQName *nameqn, JSXML *xml); - -static JSBool -MatchAttrName(JSXMLQName *nameqn, JSXML *attr) -{ - JSXMLQName *attrqn = attr->name; - - return (IS_STAR(nameqn->localName) || - js_EqualStrings(attrqn->localName, nameqn->localName)) && - (!nameqn->uri || - js_EqualStrings(attrqn->uri, nameqn->uri)); -} - -static JSBool -MatchElemName(JSXMLQName *nameqn, JSXML *elem) -{ - return (IS_STAR(nameqn->localName) || - (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(elem->name->localName, nameqn->localName))) && - (!nameqn->uri || - (elem->xml_class == JSXML_CLASS_ELEMENT && - js_EqualStrings(elem->name->uri, nameqn->uri))); -} - -/* ECMA-357 9.1.1.8 XML [[Descendants]] and 9.2.1.8 XMLList [[Descendants]]. */ -static JSBool -DescendantsHelper(JSContext *cx, JSXML *xml, JSXMLQName *nameqn, JSXML *list) -{ - uint32 i, n; - JSXML *attr, *kid; - - if (xml->xml_class == JSXML_CLASS_ELEMENT && - OBJ_GET_CLASS(cx, nameqn->object) == &js_AttributeNameClass) { - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (attr && MatchAttrName(nameqn, attr)) { - if (!Append(cx, list, attr)) - return JS_FALSE; - } - } - } - - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - continue; - if (OBJ_GET_CLASS(cx, nameqn->object) != &js_AttributeNameClass && - MatchElemName(nameqn, kid)) { - if (!Append(cx, list, kid)) - return JS_FALSE; - } - if (!DescendantsHelper(cx, kid, nameqn, list)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSXML * -Descendants(JSContext *cx, JSXML *xml, jsval id) -{ - jsid funid; - JSXMLQName *nameqn; - JSObject *listobj; - JSXML *list, *kid; - uint32 i, n; - JSBool ok; - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return NULL; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (funid) - return list; - - /* - * Protect nameqn's object and strings from GC by linking list to it - * temporarily. The cx->newborn[GCX_OBJECT] GC root protects listobj, - * which protects list. Any other object allocations occuring beneath - * DescendantsHelper use local roots. - */ - list->name = nameqn; - if (!js_EnterLocalRootScope(cx)) - return NULL; - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = DescendantsHelper(cx, kid, nameqn, list); - if (!ok) - break; - } - } - } else { - ok = DescendantsHelper(cx, xml, nameqn, list); - } - js_LeaveLocalRootScopeWithResult(cx, (jsval) list); - if (!ok) - return NULL; - list->name = NULL; - return list; -} - -static JSBool -xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp); - -/* Recursive (JSXML *) parameterized version of Equals. */ -static JSBool -XMLEquals(JSContext *cx, JSXML *xml, JSXML *vxml, JSBool *bp) -{ - JSXMLQName *qn, *vqn; - uint32 i, j, n; - JSXMLArrayCursor cursor, vcursor; - JSXML *kid, *vkid, *attr, *vattr; - JSBool ok; - JSObject *xobj, *vobj; - -retry: - if (xml->xml_class != vxml->xml_class) { - if (xml->xml_class == JSXML_CLASS_LIST && xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) - goto retry; - } - if (vxml->xml_class == JSXML_CLASS_LIST && vxml->xml_kids.length == 1) { - vxml = XMLARRAY_MEMBER(&vxml->xml_kids, 0, JSXML); - if (vxml) - goto retry; - } - *bp = JS_FALSE; - return JS_TRUE; - } - - qn = xml->name; - vqn = vxml->name; - if (qn) { - *bp = vqn && - js_EqualStrings(qn->localName, vqn->localName) && - js_EqualStrings(qn->uri, vqn->uri); - } else { - *bp = vqn == NULL; - } - if (!*bp) - return JS_TRUE; - - if (JSXML_HAS_VALUE(xml)) { - *bp = js_EqualStrings(xml->xml_value, vxml->xml_value); - } else if (xml->xml_kids.length != vxml->xml_kids.length) { - *bp = JS_FALSE; - } else { - XMLArrayCursorInit(&cursor, &xml->xml_kids); - XMLArrayCursorInit(&vcursor, &vxml->xml_kids); - for (;;) { - kid = (JSXML *) XMLArrayCursorNext(&cursor); - vkid = (JSXML *) XMLArrayCursorNext(&vcursor); - if (!kid || !vkid) { - *bp = !kid && !vkid; - ok = JS_TRUE; - break; - } - xobj = js_GetXMLObject(cx, kid); - vobj = js_GetXMLObject(cx, vkid); - ok = xobj && vobj && - xml_equality(cx, xobj, OBJECT_TO_JSVAL(vobj), bp); - if (!ok || !*bp) - break; - } - XMLArrayCursorFinish(&vcursor); - XMLArrayCursorFinish(&cursor); - if (!ok) - return JS_FALSE; - - if (*bp && xml->xml_class == JSXML_CLASS_ELEMENT) { - n = xml->xml_attrs.length; - if (n != vxml->xml_attrs.length) - *bp = JS_FALSE; - for (i = 0; *bp && i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - j = XMLARRAY_FIND_MEMBER(&vxml->xml_attrs, attr, attr_identity); - if (j == XML_NOT_FOUND) { - *bp = JS_FALSE; - break; - } - vattr = XMLARRAY_MEMBER(&vxml->xml_attrs, j, JSXML); - if (!vattr) - continue; - *bp = js_EqualStrings(attr->xml_value, vattr->xml_value); - } - } - } - - return JS_TRUE; -} - -/* ECMA-357 9.1.1.9 XML [[Equals]] and 9.2.1.9 XMLList [[Equals]]. */ -static JSBool -Equals(JSContext *cx, JSXML *xml, jsval v, JSBool *bp) -{ - JSObject *vobj; - JSXML *vxml; - - if (JSVAL_IS_PRIMITIVE(v)) { - *bp = JS_FALSE; - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_kids.length == 1) { - vxml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!vxml) - return JS_TRUE; - vobj = js_GetXMLObject(cx, vxml); - if (!vobj) - return JS_FALSE; - return js_XMLObjectOps.equality(cx, vobj, v, bp); - } - if (JSVAL_IS_VOID(v) && xml->xml_kids.length == 0) - *bp = JS_TRUE; - } - } else { - vobj = JSVAL_TO_OBJECT(v); - if (!OBJECT_IS_XML(cx, vobj)) { - *bp = JS_FALSE; - } else { - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - if (!XMLEquals(cx, xml, vxml, bp)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -CheckCycle(JSContext *cx, JSXML *xml, JSXML *kid) -{ - JS_ASSERT(kid->xml_class != JSXML_CLASS_LIST); - - do { - if (xml == kid) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CYCLIC_VALUE, js_XML_str); - return JS_FALSE; - } - } while ((xml = xml->parent) != NULL); - - return JS_TRUE; -} - -/* ECMA-357 9.1.1.11 XML [[Insert]]. */ -static JSBool -Insert(JSContext *cx, JSXML *xml, uint32 i, jsval v) -{ - uint32 j, n; - JSXML *vxml, *kid; - JSObject *vobj; - JSString *str; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - n = 1; - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) { - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - if (vxml->xml_class == JSXML_CLASS_LIST) { - n = vxml->xml_kids.length; - if (n == 0) - return JS_TRUE; - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); - if (!kid) - continue; - if (!CheckCycle(cx, xml, kid)) - return JS_FALSE; - } - } else if (vxml->xml_class == JSXML_CLASS_ELEMENT) { - /* OPTION: enforce that descendants have superset namespaces. */ - if (!CheckCycle(cx, xml, vxml)) - return JS_FALSE; - } - } - } - if (!vxml) { - str = js_ValueToString(cx, v); - if (!str) - return JS_FALSE; - - vxml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!vxml) - return JS_FALSE; - vxml->xml_value = str; - } - - if (i > xml->xml_kids.length) - i = xml->xml_kids.length; - - if (!XMLArrayInsert(cx, &xml->xml_kids, i, n)) - return JS_FALSE; - - if (vxml->xml_class == JSXML_CLASS_LIST) { - for (j = 0; j < n; j++) { - kid = XMLARRAY_MEMBER(&vxml->xml_kids, j, JSXML); - if (!kid) - continue; - kid->parent = xml; - XMLARRAY_SET_MEMBER(&xml->xml_kids, i + j, kid); - - /* OPTION: enforce that descendants have superset namespaces. */ - } - } else { - vxml->parent = xml; - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); - } - return JS_TRUE; -} - -static JSBool -IndexToIdVal(JSContext *cx, uint32 index, jsval *idvp) -{ - JSString *str; - - if (index <= JSVAL_INT_MAX) { - *idvp = INT_TO_JSVAL(index); - } else { - str = js_NumberToString(cx, (jsdouble) index); - if (!str) - return JS_FALSE; - *idvp = STRING_TO_JSVAL(str); - } - return JS_TRUE; -} - -/* ECMA-357 9.1.1.12 XML [[Replace]]. */ -static JSBool -Replace(JSContext *cx, JSXML *xml, jsval id, jsval v) -{ - uint32 i, n; - JSXML *vxml, *kid; - JSObject *vobj; - jsval junk; - JSString *str; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - if (!js_IdIsIndex(id, &i)) { - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - /* - * 9.1.1.12 - * [[Replace]] handles _i >= x.[[Length]]_ by incrementing _x.[[Length]_. - * It should therefore constrain callers to pass in _i <= x.[[Length]]_. - */ - n = xml->xml_kids.length; - if (i >= n) { - if (!IndexToIdVal(cx, n, &id)) - return JS_FALSE; - i = n; - } - - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - switch (vxml ? vxml->xml_class : JSXML_CLASS_LIMIT) { - case JSXML_CLASS_ELEMENT: - /* OPTION: enforce that descendants have superset namespaces. */ - if (!CheckCycle(cx, xml, vxml)) - return JS_FALSE; - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - case JSXML_CLASS_TEXT: - goto do_replace; - - case JSXML_CLASS_LIST: - if (i < n && !DeleteByIndex(cx, xml, id, &junk)) - return JS_FALSE; - if (!Insert(cx, xml, i, v)) - return JS_FALSE; - break; - - default: - str = js_ValueToString(cx, v); - if (!str) - return JS_FALSE; - - vxml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!vxml) - return JS_FALSE; - vxml->xml_value = str; - - do_replace: - vxml->parent = xml; - if (i < n) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid) - kid->parent = NULL; - } - if (!XMLARRAY_ADD_MEMBER(cx, &xml->xml_kids, i, vxml)) - return JS_FALSE; - break; - } - - return JS_TRUE; -} - -/* Forward declared -- its implementation uses other statics that call it. */ -static JSBool -ResolveValue(JSContext *cx, JSXML *list, JSXML **result); - -/* ECMA-357 9.1.1.3 XML [[Delete]], 9.2.1.3 XML [[Delete]]. */ -static JSBool -DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *kid, *parent; - JSBool isIndex; - JSXMLArray *array; - uint32 length, index, kidIndex, deleteCount; - JSXMLQName *nameqn; - jsid funid; - JSObject *nameobj, *kidobj; - JSXMLNameMatcher matcher; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - isIndex = js_IdIsIndex(id, &index); - if (JSXML_HAS_KIDS(xml)) { - array = &xml->xml_kids; - length = array->length; - } else { - array = NULL; - length = 0; - } - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.3. */ - if (isIndex && index < length) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (!kid) - goto out; - parent = kid->parent; - if (parent) { - JS_ASSERT(parent != xml); - JS_ASSERT(JSXML_HAS_KIDS(parent)); - - if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { - nameqn = kid->name; - nameobj = js_GetAttributeNameObject(cx, nameqn); - if (!nameobj || !js_GetXMLObject(cx, parent)) - return JS_FALSE; - - id = OBJECT_TO_JSVAL(nameobj); - if (!DeleteProperty(cx, parent->object, id, vp)) - return JS_FALSE; - } else { - kidIndex = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, - NULL); - JS_ASSERT(kidIndex != XML_NOT_FOUND); - if (!IndexToIdVal(cx, kidIndex, &id)) - return JS_FALSE; - if (!DeleteByIndex(cx, parent, id, vp)) - return JS_FALSE; - } - } - - XMLArrayDelete(cx, array, index, JS_TRUE); - } else { - for (index = 0; index < length; index++) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !DeleteProperty(cx, kidobj, id, vp)) - return JS_FALSE; - } - } - } - } else { - /* ECMA-357 9.1.1.3. */ - if (isIndex) { - /* See NOTE in spec: this variation is reserved for future use. */ - ReportBadXMLName(cx, id); - return JS_FALSE; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - goto out; - nameobj = nameqn->object; - - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - if (xml->xml_class != JSXML_CLASS_ELEMENT) - goto out; - array = &xml->xml_attrs; - length = array->length; - matcher = MatchAttrName; - } else { - matcher = MatchElemName; - } - if (length != 0) { - deleteCount = 0; - for (index = 0; index < length; index++) { - kid = XMLARRAY_MEMBER(array, index, JSXML); - if (kid && matcher(nameqn, kid)) { - kid->parent = NULL; - XMLArrayDelete(cx, array, index, JS_FALSE); - ++deleteCount; - } else if (deleteCount != 0) { - XMLARRAY_SET_MEMBER(array, - index - deleteCount, - array->vector[index]); - } - } - array->length -= deleteCount; - } - } - -out: - *vp = JSVAL_TRUE; - return JS_TRUE; -} - -static JSBool -SyncInScopeNamespaces(JSContext *cx, JSXML *xml) -{ - JSXMLArray *nsarray; - uint32 i, n; - JSXMLNamespace *ns; - - nsarray = &xml->xml_namespaces; - while ((xml = xml->parent) != NULL) { - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (ns && !XMLARRAY_HAS_MEMBER(nsarray, ns, namespace_identity)) { - if (!XMLARRAY_APPEND(cx, nsarray, ns)) - return JS_FALSE; - } - } - } - return JS_TRUE; -} - -static JSBool -GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, - JSBool attributes, JSXML *list) -{ - JSXMLArray *array; - JSXMLNameMatcher matcher; - JSXMLArrayCursor cursor; - JSXML *kid; - JSBool ok; - - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - if (attributes) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - - XMLArrayCursorInit(&cursor, array); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (matcher(nameqn, kid)) { - if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = SyncInScopeNamespaces(cx, kid); - if (!ok) - goto out; - } - ok = Append(cx, list, kid); - if (!ok) - goto out; - } - } - ok = JS_TRUE; - - out: - XMLArrayCursorFinish(&cursor); - return ok; -} - -/* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ -static JSBool -GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *list, *kid; - uint32 index; - JSObject *kidobj, *listobj; - JSXMLQName *nameqn; - jsid funid; - jsval roots[2]; - JSTempValueRooter tvr; - JSBool attributes; - JSXMLArrayCursor cursor; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) - return JS_TRUE; - - if (js_IdIsIndex(id, &index)) { - if (xml->xml_class != JSXML_CLASS_LIST) { - *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; - } else { - /* - * ECMA-357 9.2.1.1 starts here. - * - * Erratum: 9.2 is not completely clear that indexed properties - * correspond to kids, but that's what it seems to say, and it's - * what any sane user would want. - */ - if (index < xml->xml_kids.length) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - - *vp = OBJECT_TO_JSVAL(kidobj); - } else { - *vp = JSVAL_VOID; - } - } - return JS_TRUE; - } - - /* - * ECMA-357 9.2.1.1/9.1.1.1 qname case. - */ - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return js_GetXMLFunction(cx, obj, funid, vp); - - roots[0] = OBJECT_TO_JSVAL(nameqn->object); - JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (listobj) { - roots[1] = OBJECT_TO_JSVAL(listobj); - tvr.count++; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - attributes = (OBJ_GET_CLASS(cx, nameqn->object) == - &js_AttributeNameClass); - - if (xml->xml_class == JSXML_CLASS_LIST) { - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT && - !GetNamedProperty(cx, kid, nameqn, attributes, list)) { - listobj = NULL; - break; - } - } - XMLArrayCursorFinish(&cursor); - } else { - if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) - listobj = NULL; - } - - /* - * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given - * list's [[TargetProperty]] to the property that is being appended. - * This means that any use of the internal [[Get]] property returns - * a list which, when used by e.g. [[Insert]] duplicates the last - * element matched by id. - * See bug 336921. - */ - list->xml_target = xml; - list->xml_targetprop = nameqn; - *vp = OBJECT_TO_JSVAL(listobj); - } - - JS_POP_TEMP_ROOT(cx, &tvr); - return listobj != NULL; -} - -static JSXML * -CopyOnWrite(JSContext *cx, JSXML *xml, JSObject *obj) -{ - JS_ASSERT(xml->object != obj); - - xml = DeepCopy(cx, xml, obj, 0); - if (!xml) - return NULL; - - JS_ASSERT(xml->object == obj); - return xml; -} - -#define CHECK_COPY_ON_WRITE(cx,xml,obj) \ - (xml->object == obj ? xml : CopyOnWrite(cx, xml, obj)) - -static JSString * -KidToString(JSContext *cx, JSXML *xml, uint32 index) -{ - JSXML *kid; - JSObject *kidobj; - - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) - return cx->runtime->emptyString; - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return NULL; - return js_ValueToString(cx, OBJECT_TO_JSVAL(kidobj)); -} - -/* ECMA-357 9.1.1.2 XML [[Put]] and 9.2.1.2 XMLList [[Put]]. */ -static JSBool -PutProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSBool ok, primitiveAssign; - enum { OBJ_ROOT, ID_ROOT, VAL_ROOT }; - jsval roots[3]; - JSTempValueRooter tvr; - JSXML *xml, *vxml, *rxml, *kid, *attr, *parent, *copy, *kid2, *match; - JSObject *vobj, *nameobj, *attrobj, *parentobj, *kidobj, *copyobj; - JSXMLQName *targetprop, *nameqn, *attrqn; - uint32 index, i, j, k, n, q; - jsval attrval, nsval, junk; - jsid funid; - JSString *left, *right, *space; - JSXMLNamespace *ns; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - /* Precompute vxml for 9.2.1.2 2(c)(vii)(2-3) and 2(d) and 9.1.1.2 1. */ - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(*vp)) { - vobj = JSVAL_TO_OBJECT(*vp); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - /* Control flow after here must exit via label out. */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - roots[OBJ_ROOT] = OBJECT_TO_JSVAL(obj); - roots[ID_ROOT] = id; - roots[VAL_ROOT] = *vp; - JS_PUSH_TEMP_ROOT(cx, 3, roots, &tvr); - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.2. */ - if (js_IdIsIndex(id, &index)) { - /* Step 1 sets i to the property index. */ - i = index; - - /* 2(a-b). */ - if (xml->xml_target) { - ok = ResolveValue(cx, xml->xml_target, &rxml); - if (!ok) - goto out; - if (!rxml) - goto out; - JS_ASSERT(rxml->object); - } else { - rxml = NULL; - } - - /* 2(c). */ - if (index >= xml->xml_kids.length) { - /* 2(c)(i). */ - if (rxml) { - if (rxml->xml_class == JSXML_CLASS_LIST) { - if (rxml->xml_kids.length != 1) - goto out; - rxml = XMLARRAY_MEMBER(&rxml->xml_kids, 0, JSXML); - if (!rxml) - goto out; - ok = js_GetXMLObject(cx, rxml) != NULL; - if (!ok) - goto out; - } - - /* - * Erratum: ECMA-357 9.2.1.2 step 2(c)(ii) sets - * _y.[[Parent]] = r_ where _r_ is the result of - * [[ResolveValue]] called on _x.[[TargetObject]] in - * 2(a)(i). This can result in text parenting text: - * - * var MYXML = new XML(); - * MYXML.appendChild(new XML("Giants")); - * - * (testcase from Werner Sharp ). - * - * To match insertChildAfter, insertChildBefore, - * prependChild, and setChildren, we should silently - * do nothing in this case. - */ - if (!JSXML_HAS_KIDS(rxml)) - goto out; - } - - /* 2(c)(ii) is distributed below as several js_NewXML calls. */ - targetprop = xml->xml_targetprop; - if (!targetprop || IS_STAR(targetprop->localName)) { - /* 2(c)(iv)(1-2), out of order w.r.t. 2(c)(iii). */ - kid = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!kid) - goto bad; - } else { - nameobj = js_GetXMLQNameObject(cx, targetprop); - if (!nameobj) - goto bad; - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - /* - * 2(c)(iii)(1-3). - * Note that rxml can't be null here, because target - * and targetprop are non-null. - */ - ok = GetProperty(cx, rxml->object, id, &attrval); - if (!ok) - goto out; - if (JSVAL_IS_PRIMITIVE(attrval)) /* no such attribute */ - goto out; - attrobj = JSVAL_TO_OBJECT(attrval); - attr = (JSXML *) JS_GetPrivate(cx, attrobj); - if (JSXML_LENGTH(attr) != 0) - goto out; - - kid = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - } else { - /* 2(c)(v). */ - kid = js_NewXML(cx, JSXML_CLASS_ELEMENT); - } - if (!kid) - goto bad; - - /* An important bit of 2(c)(ii). */ - kid->name = targetprop; - } - - /* Final important bit of 2(c)(ii). */ - kid->parent = rxml; - - /* 2(c)(vi-vii). */ - i = xml->xml_kids.length; - if (kid->xml_class != JSXML_CLASS_ATTRIBUTE) { - /* - * 2(c)(vii)(1) tests whether _y.[[Parent]]_ is not null. - * y.[[Parent]] is here called kid->parent, which we know - * from 2(c)(ii) is _r_, here called rxml. So let's just - * test that! Erratum, the spec should be simpler here. - */ - if (rxml) { - JS_ASSERT(JSXML_HAS_KIDS(rxml)); - n = rxml->xml_kids.length; - j = n - 1; - if (n != 0 && i != 0) { - for (n = j, j = 0; j < n; j++) { - if (rxml->xml_kids.vector[j] == - xml->xml_kids.vector[i-1]) { - break; - } - } - } - - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - ok = Insert(cx, rxml, j + 1, OBJECT_TO_JSVAL(kidobj)); - if (!ok) - goto out; - } - - /* - * 2(c)(vii)(2-3). - * Erratum: [[PropertyName]] in 2(c)(vii)(3) must be a - * typo for [[TargetProperty]]. - */ - if (vxml) { - kid->name = (vxml->xml_class == JSXML_CLASS_LIST) - ? vxml->xml_targetprop - : vxml->name; - } - } - - /* 2(c)(viii). */ - ok = Append(cx, xml, kid); - if (!ok) - goto out; - } - - /* 2(d). */ - if (!vxml || - vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - roots[VAL_ROOT] = *vp; - } - - /* 2(e). */ - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - goto out; - parent = kid->parent; - if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) { - nameobj = js_GetAttributeNameObject(cx, kid->name); - if (!nameobj) - goto bad; - id = OBJECT_TO_JSVAL(nameobj); - - if (parent) { - /* 2(e)(i). */ - parentobj = js_GetXMLObject(cx, parent); - if (!parentobj) - goto bad; - ok = PutProperty(cx, parentobj, id, vp); - if (!ok) - goto out; - - /* 2(e)(ii). */ - ok = GetProperty(cx, parentobj, id, vp); - if (!ok) - goto out; - attr = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp)); - - /* 2(e)(iii). */ - xml->xml_kids.vector[i] = attr->xml_kids.vector[0]; - } - } - - /* 2(f). */ - else if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { - /* 2(f)(i) Create a shallow copy _c_ of _V_. */ - copyobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!copyobj) - goto bad; - copy = (JSXML *) JS_GetPrivate(cx, copyobj); - n = vxml->xml_kids.length; - ok = XMLArraySetCapacity(cx, ©->xml_kids, n); - if (!ok) - goto out; - for (k = 0; k < n; k++) { - kid2 = XMLARRAY_MEMBER(&vxml->xml_kids, k, JSXML); - XMLARRAY_SET_MEMBER(©->xml_kids, k, kid2); - } - - JS_ASSERT(parent != xml); - if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(q != XML_NOT_FOUND); - - ok = IndexToIdVal(cx, q, &id); - if (!ok) - goto out; - ok = Replace(cx, parent, id, OBJECT_TO_JSVAL(copyobj)); - if (!ok) - goto out; - -#ifdef DEBUG - /* Erratum: this loop in the spec is useless. */ - for (j = 0, n = copy->xml_kids.length; j < n; j++) { - kid2 = XMLARRAY_MEMBER(&parent->xml_kids, q + j, JSXML); - JS_ASSERT(XMLARRAY_MEMBER(©->xml_kids, j, JSXML) - == kid2); - } -#endif - } - - /* - * 2(f)(iv-vi). - * Erratum: notice the unhandled zero-length V basis case and - * the off-by-one errors for the n != 0 cases in the spec. - */ - if (n == 0) { - XMLArrayDelete(cx, &xml->xml_kids, i, JS_TRUE); - } else { - ok = XMLArrayInsert(cx, &xml->xml_kids, i + 1, n - 1); - if (!ok) - goto out; - - for (j = 0; j < n; j++) - xml->xml_kids.vector[i + j] = copy->xml_kids.vector[j]; - } - } - - /* 2(g). */ - else if (vxml || JSXML_HAS_VALUE(kid)) { - if (parent) { - q = XMLARRAY_FIND_MEMBER(&parent->xml_kids, kid, NULL); - JS_ASSERT(q != XML_NOT_FOUND); - - ok = IndexToIdVal(cx, q, &id); - if (!ok) - goto out; - ok = Replace(cx, parent, id, *vp); - if (!ok) - goto out; - - vxml = XMLARRAY_MEMBER(&parent->xml_kids, q, JSXML); - if (!vxml) - goto out; - roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vxml->object); - } - - /* - * 2(g)(iii). - * Erratum: _V_ may not be of type XML, but all index-named - * properties _x[i]_ in an XMLList _x_ must be of type XML, - * according to 9.2.1.1 Overview and other places in the spec. - * - * Thanks to 2(d), we know _V_ (*vp here) is either a string - * or an XML/XMLList object. If *vp is a string, call ToXML - * on it to satisfy the constraint. - */ - if (!vxml) { - JS_ASSERT(JSVAL_IS_STRING(*vp)); - vobj = ToXML(cx, *vp); - if (!vobj) - goto bad; - roots[VAL_ROOT] = *vp = OBJECT_TO_JSVAL(vobj); - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - XMLARRAY_SET_MEMBER(&xml->xml_kids, i, vxml); - } - - /* 2(h). */ - else { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - id = ATOM_KEY(cx->runtime->atomState.starAtom); - ok = PutProperty(cx, kidobj, id, vp); - if (!ok) - goto out; - } - } else { - /* - * 3. - * Erratum: if x.[[Length]] > 1 or [[ResolveValue]] returns null - * or an r with r.[[Length]] != 1, throw TypeError. - */ - n = JSXML_LENGTH(xml); - if (n > 1) - goto type_error; - if (n == 0) { - ok = ResolveValue(cx, xml, &rxml); - if (!ok) - goto out; - if (!rxml || JSXML_LENGTH(rxml) != 1) - goto type_error; - ok = Append(cx, xml, rxml); - if (!ok) - goto out; - } - JS_ASSERT(JSXML_LENGTH(xml) == 1); - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!kid) - goto out; - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - goto bad; - ok = PutProperty(cx, kidobj, id, vp); - if (!ok) - goto out; - } - } else { - /* - * ECMA-357 9.1.1.2. - * Erratum: move steps 3 and 4 to before 1 and 2, to avoid wasted - * effort in ToString or [[DeepCopy]]. - */ - if (js_IdIsIndex(id, &index)) { - /* See NOTE in spec: this variation is reserved for future use. */ - ReportBadXMLName(cx, id); - goto bad; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - goto bad; - if (funid) { - ok = js_SetProperty(cx, obj, funid, vp); - goto out; - } - nameobj = nameqn->object; - - if (JSXML_HAS_VALUE(xml)) - goto out; - - if (!vxml || - vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - } else { - rxml = DeepCopyInLRS(cx, vxml, 0); - if (!rxml || !js_GetXMLObject(cx, rxml)) - goto bad; - vxml = rxml; - *vp = OBJECT_TO_JSVAL(vxml->object); - } - roots[VAL_ROOT] = *vp; - - /* - * 6. - * Erratum: why is this done here, so early? use is way later.... - */ - ok = js_GetDefaultXMLNamespace(cx, &nsval); - if (!ok) - goto out; - - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - /* 7(a). */ - if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj))) - goto out; - - /* 7(b-c). */ - if (vxml && vxml->xml_class == JSXML_CLASS_LIST) { - n = vxml->xml_kids.length; - if (n == 0) { - *vp = STRING_TO_JSVAL(cx->runtime->emptyString); - } else { - left = KidToString(cx, vxml, 0); - if (!left) - goto bad; - - space = ATOM_TO_STRING(cx->runtime->atomState.spaceAtom); - for (i = 1; i < n; i++) { - left = js_ConcatStrings(cx, left, space); - if (!left) - goto bad; - right = KidToString(cx, vxml, i); - if (!right) - goto bad; - left = js_ConcatStrings(cx, left, right); - if (!left) - goto bad; - } - - roots[VAL_ROOT] = *vp = STRING_TO_JSVAL(left); - } - } else { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (!ok) - goto out; - roots[VAL_ROOT] = *vp; - } - - /* 7(d-e). */ - match = NULL; - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - attrqn = attr->name; - if (js_EqualStrings(attrqn->localName, nameqn->localName) && - (!nameqn->uri || - js_EqualStrings(attrqn->uri, nameqn->uri))) { - if (!match) { - match = attr; - } else { - nameobj = js_GetAttributeNameObject(cx, attrqn); - if (!nameobj) - goto bad; - - id = OBJECT_TO_JSVAL(nameobj); - ok = DeleteProperty(cx, obj, id, &junk); - if (!ok) - goto out; - --i; - } - } - } - - /* 7(f). */ - attr = match; - if (!attr) { - /* 7(f)(i-ii). */ - if (!nameqn->uri) { - left = right = cx->runtime->emptyString; - } else { - left = nameqn->uri; - right = nameqn->prefix; - } - nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); - if (!nameqn) - goto bad; - - /* 7(f)(iii). */ - attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE); - if (!attr) - goto bad; - attr->parent = xml; - attr->name = nameqn; - - /* 7(f)(iv). */ - ok = XMLARRAY_ADD_MEMBER(cx, &xml->xml_attrs, n, attr); - if (!ok) - goto out; - - /* 7(f)(v-vi). */ - ns = GetNamespace(cx, nameqn, NULL); - if (!ns) - goto bad; - ok = AddInScopeNamespace(cx, xml, ns); - if (!ok) - goto out; - } - - /* 7(g). */ - attr->xml_value = JSVAL_TO_STRING(*vp); - goto out; - } - - /* 8-9. */ - if (!js_IsXMLName(cx, OBJECT_TO_JSVAL(nameobj)) && - !IS_STAR(nameqn->localName)) { - goto out; - } - - /* 10-11. */ - id = JSVAL_VOID; - primitiveAssign = !vxml && !IS_STAR(nameqn->localName); - - /* 12. */ - k = n = xml->xml_kids.length; - kid2 = NULL; - while (k != 0) { - --k; - kid = XMLARRAY_MEMBER(&xml->xml_kids, k, JSXML); - if (kid && MatchElemName(nameqn, kid)) { - if (!JSVAL_IS_VOID(id)) { - ok = DeleteByIndex(cx, xml, id, &junk); - if (!ok) - goto out; - } - ok = IndexToIdVal(cx, k, &id); - if (!ok) - goto out; - kid2 = kid; - } - } - - /* - * Erratum: ECMA-357 specified child insertion inconsistently: - * insertChildBefore and insertChildAfter insert an arbitrary XML - * instance, and therefore can create cycles, but appendChild as - * specified by the "Overview" of 13.4.4.3 calls [[DeepCopy]] on - * its argument. But the "Semantics" in 13.4.4.3 do not include - * any [[DeepCopy]] call. - * - * Fixing this (https://bugzilla.mozilla.org/show_bug.cgi?id=312692) - * required adding cycle detection, and allowing duplicate kids to - * be created (see comment 6 in the bug). Allowing duplicate kid - * references means the loop above will delete all but the lowest - * indexed reference, and each [[DeleteByIndex]] nulls the kid's - * parent. Thus the need to restore parent here. This is covered - * by https://bugzilla.mozilla.org/show_bug.cgi?id=327564. - */ - if (kid2) { - JS_ASSERT(kid2->parent == xml || !kid2->parent); - if (!kid2->parent) - kid2->parent = xml; - } - - /* 13. */ - if (JSVAL_IS_VOID(id)) { - /* 13(a). */ - ok = IndexToIdVal(cx, n, &id); - if (!ok) - goto out; - - /* 13(b). */ - if (primitiveAssign) { - if (!nameqn->uri) { - ns = (JSXMLNamespace *) - JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - left = ns->uri; - right = ns->prefix; - } else { - left = nameqn->uri; - right = nameqn->prefix; - } - nameqn = js_NewXMLQName(cx, left, right, nameqn->localName); - if (!nameqn) - goto bad; - - /* 13(b)(iii). */ - vobj = js_NewXMLObject(cx, JSXML_CLASS_ELEMENT); - if (!vobj) - goto bad; - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - vxml->parent = xml; - vxml->name = nameqn; - - /* 13(b)(iv-vi). */ - ns = GetNamespace(cx, nameqn, NULL); - if (!ns) - goto bad; - ok = Replace(cx, xml, id, OBJECT_TO_JSVAL(vobj)); - if (!ok) - goto out; - ok = AddInScopeNamespace(cx, vxml, ns); - if (!ok) - goto out; - } - } - - /* 14. */ - if (primitiveAssign) { - JSXMLArrayCursor cursor; - - js_IdIsIndex(id, &index); - XMLArrayCursorInit(&cursor, &xml->xml_kids); - cursor.index = index; - kid = (JSXML *) XMLArrayCursorItem(&cursor); - if (JSXML_HAS_KIDS(kid)) { - XMLArrayFinish(cx, &kid->xml_kids); - ok = XMLArrayInit(cx, &kid->xml_kids, 1); - } - - /* 14(b-c). */ - /* XXXbe Erratum? redundant w.r.t. 7(b-c) else clause above */ - if (ok) { - ok = JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp); - if (ok && !IS_EMPTY(JSVAL_TO_STRING(*vp))) { - roots[VAL_ROOT] = *vp; - if ((JSXML *) XMLArrayCursorItem(&cursor) == kid) - ok = Replace(cx, kid, JSVAL_ZERO, *vp); - } - } - XMLArrayCursorFinish(&cursor); - } else { - /* 15(a). */ - ok = Replace(cx, xml, id, *vp); - } - } - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - js_LeaveLocalRootScope(cx); - return ok; - -type_error: - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_XMLLIST_PUT, - js_ValueToPrintableString(cx, id)); -bad: - ok = JS_FALSE; - goto out; -} - -/* ECMA-357 9.1.1.10 XML [[ResolveValue]], 9.2.1.10 XMLList [[ResolveValue]]. */ -static JSBool -ResolveValue(JSContext *cx, JSXML *list, JSXML **result) -{ - JSXML *target, *base; - JSXMLQName *targetprop; - JSObject *targetpropobj; - jsval id, tv; - - /* Our caller must be protecting newborn objects. */ - JS_ASSERT(cx->localRootStack); - - if (list->xml_class != JSXML_CLASS_LIST || list->xml_kids.length != 0) { - if (!js_GetXMLObject(cx, list)) - return JS_FALSE; - *result = list; - return JS_TRUE; - } - - target = list->xml_target; - targetprop = list->xml_targetprop; - if (!target || !targetprop || IS_STAR(targetprop->localName)) { - *result = NULL; - return JS_TRUE; - } - - targetpropobj = js_GetXMLQNameObject(cx, targetprop); - if (!targetpropobj) - return JS_FALSE; - if (OBJ_GET_CLASS(cx, targetpropobj) == &js_AttributeNameClass) { - *result = NULL; - return JS_TRUE; - } - - if (!ResolveValue(cx, target, &base)) - return JS_FALSE; - if (!base) { - *result = NULL; - return JS_TRUE; - } - if (!js_GetXMLObject(cx, base)) - return JS_FALSE; - - id = OBJECT_TO_JSVAL(targetpropobj); - if (!GetProperty(cx, base->object, id, &tv)) - return JS_FALSE; - target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); - - if (JSXML_LENGTH(target) == 0) { - if (base->xml_class == JSXML_CLASS_LIST && JSXML_LENGTH(base) > 1) { - *result = NULL; - return JS_TRUE; - } - tv = STRING_TO_JSVAL(cx->runtime->emptyString); - if (!PutProperty(cx, base->object, id, &tv)) - return JS_FALSE; - if (!GetProperty(cx, base->object, id, &tv)) - return JS_FALSE; - target = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(tv)); - } - - *result = target; - return JS_TRUE; -} - -/* - * HasProperty must be able to return a found JSProperty and the object in - * which it was found, if id is of the form function::name. For other ids, - * if they index or name an XML child, we return FOUND_XML_PROPERTY in *propp - * and null in *objp. - * - * DROP_PROPERTY helps HasProperty callers drop function properties without - * trying to drop the magic FOUND_XML_PROPERTY cookie. - */ -#define FOUND_XML_PROPERTY ((JSProperty *) 1) -#define DROP_PROPERTY(cx,pobj,prop) (((prop) != FOUND_XML_PROPERTY) \ - ? OBJ_DROP_PROPERTY(cx, pobj, prop) \ - : (void) 0) - -/* ECMA-357 9.1.1.6 XML [[HasProperty]] and 9.2.1.5 XMLList [[HasProperty]]. */ -static JSBool -HasProperty(JSContext *cx, JSObject *obj, jsval id, JSObject **objp, - JSProperty **propp) -{ - JSXML *xml, *kid; - JSXMLArrayCursor cursor; - JSObject *kidobj; - JSXMLQName *qn; - jsid funid; - JSXMLArray *array; - JSXMLNameMatcher matcher; - uint32 i, n; - - *objp = NULL; - *propp = NULL; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class == JSXML_CLASS_LIST) { - n = JSXML_LENGTH(xml); - if (js_IdIsIndex(id, &i)) { - if (i < n) - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !HasProperty(cx, kidobj, id, objp, propp)) - break; - if (*propp) - break; - } - } - XMLArrayCursorFinish(&cursor); - if (kid) - return *propp != NULL; - } else { - if (xml->xml_class == JSXML_CLASS_ELEMENT && js_IdIsIndex(id, &i)) { - if (i == 0) - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - - qn = ToXMLName(cx, id, &funid); - if (!qn) - return JS_FALSE; - if (funid) - return js_LookupProperty(cx, obj, funid, objp, propp); - - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - if (OBJ_GET_CLASS(cx, qn->object) == &js_AttributeNameClass) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - for (i = 0, n = array->length; i < n; i++) { - kid = XMLARRAY_MEMBER(array, i, JSXML); - if (kid && matcher(qn, kid)) { - *propp = FOUND_XML_PROPERTY; - return JS_TRUE; - } - } - } - - return JS_TRUE; -} - -static void -xml_finalize(JSContext *cx, JSObject *obj) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (!xml) - return; - if (xml->object == obj) - xml->object = NULL; - UNMETER(xml_stats.livexmlobj); -} - -static void -xml_mark_vector(JSContext *cx, JSXML **vec, uint32 len) -{ - uint32 i; - JSXML *elt; - - for (i = 0; i < len; i++) { - elt = vec[i]; - { -#ifdef GC_MARK_DEBUG - char buf[120]; - - if (elt->xml_class == JSXML_CLASS_LIST) { - strcpy(buf, js_XMLList_str); - } else if (JSXML_HAS_NAME(elt)) { - JSXMLQName *qn = elt->name; - - JS_snprintf(buf, sizeof buf, "%s::%s", - qn->uri ? JS_GetStringBytes(qn->uri) : "*", - JS_GetStringBytes(qn->localName)); - } else { - JSString *str = elt->xml_value; - size_t srclen = JSSTRING_LENGTH(str); - size_t dstlen = sizeof buf; - - if (srclen >= sizeof buf / 6) - srclen = sizeof buf / 6 - 1; - js_DeflateStringToBuffer(cx, JSSTRING_CHARS(str), srclen, - buf, &dstlen); - } -#endif - GC_MARK(cx, elt, buf); - } - } -} - -/* - * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to - * be native. Therefore, xml_lookupProperty must return a valid JSProperty - * pointer parameter via *propp to signify "property found". Since the only - * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from - * js_FindXMLProperty (in this file), js_FindProperty (in jsobj.c, called from - * jsinterp.c) or from JSOP_IN case in the interpreter, the only time we add a - * JSScopeProperty here is when an unqualified name or XML name is being - * accessed or when "name in xml" is called. - * - * This scope property keeps the JSOP_NAME code in js_Interpret happy by - * giving it an sprop with (getter, setter) == (GetProperty, PutProperty). - * - * NB: xml_deleteProperty must take care to remove any property added here. - * - * FIXME This clashes with the function namespace implementation which also - * uses native properties. Effectively after xml_lookupProperty any property - * stored previously using assignments to xml.function::name will be removed. - * We partially workaround the problem in js_GetXMLFunction. There we take - * advantage of the fact that typically function:: is used to access the - * functions from XML.prototype. So when js_GetProperty returns a non-function - * property, we assume that it represents the result of GetProperty setter - * hiding the function and use an extra prototype chain lookup to recover it. - * For a proper solution see bug 355257. - */ -static JSBool -xml_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, - JSProperty **propp) -{ - JSScopeProperty *sprop; - - if (!HasProperty(cx, obj, ID_TO_VALUE(id), objp, propp)) - return JS_FALSE; - - if (*propp == FOUND_XML_PROPERTY) { - sprop = js_AddNativeProperty(cx, obj, id, GetProperty, PutProperty, - SPROP_INVALID_SLOT, JSPROP_ENUMERATE, - 0, 0); - if (!sprop) - return JS_FALSE; - - JS_LOCK_OBJ(cx, obj); - *objp = obj; - *propp = (JSProperty *) sprop; - } - return JS_TRUE; -} - -static JSBool -xml_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, - JSPropertyOp getter, JSPropertyOp setter, uintN attrs, - JSProperty **propp) -{ - if (VALUE_IS_FUNCTION(cx, value) || getter || setter || - (attrs & JSPROP_ENUMERATE) == 0 || - (attrs & (JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED))) { - return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, - propp); - } - - if (!PutProperty(cx, obj, ID_TO_VALUE(id), &value)) - return JS_FALSE; - if (propp) - *propp = NULL; - return JS_TRUE; -} - -static JSBool -xml_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - if (id == JS_DEFAULT_XML_NAMESPACE_ID) { - *vp = JSVAL_VOID; - return JS_TRUE; - } - - return GetProperty(cx, obj, ID_TO_VALUE(id), vp); -} - -static JSBool -xml_setProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return PutProperty(cx, obj, ID_TO_VALUE(id), vp); -} - -static JSBool -FoundProperty(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - JSBool *foundp) -{ - JSObject *pobj; - - if (prop) { - *foundp = JS_TRUE; - } else { - if (!HasProperty(cx, obj, ID_TO_VALUE(id), &pobj, &prop)) - return JS_FALSE; - if (prop) - DROP_PROPERTY(cx, pobj, prop); - *foundp = (prop != NULL); - } - return JS_TRUE; -} - -static JSBool -xml_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool found; - - if (!FoundProperty(cx, obj, id, prop, &found)) - return JS_FALSE; - *attrsp = found ? JSPROP_ENUMERATE : 0; - return JS_TRUE; -} - -static JSBool -xml_setAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop, - uintN *attrsp) -{ - JSBool found; - - if (!FoundProperty(cx, obj, id, prop, &found)) - return JS_FALSE; - if (found) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_CANT_SET_XML_ATTRS); - } - return !found; -} - -static JSBool -xml_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval) -{ - /* - * If this object has its own (mutable) scope, and if id isn't an index, - * then we may have added a property to the scope in xml_lookupProperty - * for it to return to mean "found" and to provide a handle for access - * operations to call the property's getter or setter. The property also - * helps speed up unqualified accesses via the property cache, avoiding - * what amount to two HasProperty searches. - * - * But now it's time to remove any such property, to purge the property - * cache and remove the scope entry. - */ - if (OBJ_SCOPE(obj)->object == obj && !JSID_IS_INT(id)) { - if (!js_DeleteProperty(cx, obj, id, rval)) - return JS_FALSE; - } - - return DeleteProperty(cx, obj, ID_TO_VALUE(id), rval); -} - -static JSBool -xml_defaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp) -{ - JSXML *xml; - - if (hint == JSTYPE_OBJECT) { - /* Called from for..in code in js_Interpret: return an XMLList. */ - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (xml->xml_class != JSXML_CLASS_LIST) { - obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); - if (!obj) - return JS_FALSE; - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - - return JS_CallFunctionName(cx, obj, js_toString_str, 0, NULL, vp); -} - -static JSBool -xml_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp) -{ - JSXML *xml; - uint32 length, index; - JSXMLArrayCursor *cursor; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - length = JSXML_LENGTH(xml); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (length == 0) { - cursor = NULL; - } else { - cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); - if (!cursor) - return JS_FALSE; - XMLArrayCursorInit(cursor, &xml->xml_kids); - } - *statep = PRIVATE_TO_JSVAL(cursor); - if (idp) - *idp = INT_TO_JSID(length); - break; - - case JSENUMERATE_NEXT: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor && cursor->array && (index = cursor->index) < length) { - *idp = INT_TO_JSID(index); - cursor->index = index + 1; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor) { - XMLArrayCursorFinish(cursor); - JS_free(cx, cursor); - } - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -static JSBool -xml_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - return JS_TRUE; -} - -static uint32 -xml_mark(JSContext *cx, JSObject *obj, void *arg) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - GC_MARK(cx, xml, "private"); - return js_Mark(cx, obj, NULL); -} - -static void -xml_clear(JSContext *cx, JSObject *obj) -{ -} - -static JSBool -HasSimpleContent(JSXML *xml) -{ - JSXML *kid; - JSBool simple; - uint32 i, n; - -again: - switch (xml->xml_class) { - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - return JS_FALSE; - case JSXML_CLASS_LIST: - if (xml->xml_kids.length == 0) - return JS_TRUE; - if (xml->xml_kids.length == 1) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (kid) { - xml = kid; - goto again; - } - } - /* FALL THROUGH */ - default: - simple = JS_TRUE; - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - simple = JS_FALSE; - break; - } - } - return simple; - } -} - -/* - * 11.2.2.1 Step 3(d) onward. - */ -static JSObject * -xml_getMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSTempValueRooter tvr; - - JS_ASSERT(JS_InstanceOf(cx, obj, &js_XMLClass, NULL)); - - /* - * As our callers have a bad habit of passing a pointer to an unrooted - * local value as vp, we use a proper root here. - */ - JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr); - if (!js_GetXMLFunction(cx, obj, id, &tvr.u.value)) - obj = NULL; - *vp = tvr.u.value; - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -static JSBool -xml_setMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - return js_SetProperty(cx, obj, id, vp); -} - -static JSBool -xml_enumerateValues(JSContext *cx, JSObject *obj, JSIterateOp enum_op, - jsval *statep, jsid *idp, jsval *vp) -{ - JSXML *xml, *kid; - uint32 length, index; - JSXMLArrayCursor *cursor; - JSObject *kidobj; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - length = JSXML_LENGTH(xml); - JS_ASSERT(INT_FITS_IN_JSVAL(length)); - - switch (enum_op) { - case JSENUMERATE_INIT: - if (length == 0) { - cursor = NULL; - } else { - cursor = (JSXMLArrayCursor *) JS_malloc(cx, sizeof *cursor); - if (!cursor) - return JS_FALSE; - XMLArrayCursorInit(cursor, &xml->xml_kids); - } - *statep = PRIVATE_TO_JSVAL(cursor); - if (idp) - *idp = INT_TO_JSID(length); - if (vp) - *vp = JSVAL_VOID; - break; - - case JSENUMERATE_NEXT: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor && cursor->array && (index = cursor->index) < length) { - while (!(kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML))) { - if (++index == length) - goto destroy; - } - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - JS_ASSERT(INT_FITS_IN_JSVAL(index)); - *idp = INT_TO_JSID(index); - *vp = OBJECT_TO_JSVAL(kidobj); - cursor->index = index + 1; - break; - } - /* FALL THROUGH */ - - case JSENUMERATE_DESTROY: - cursor = JSVAL_TO_PRIVATE(*statep); - if (cursor) { - destroy: - XMLArrayCursorFinish(cursor); - JS_free(cx, cursor); - } - *statep = JSVAL_NULL; - break; - } - return JS_TRUE; -} - -static JSBool -xml_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp) -{ - JSXML *xml, *vxml; - JSObject *vobj; - JSBool ok; - JSString *str, *vstr; - jsdouble d, d2; - - xml = (JSXML *) JS_GetPrivate(cx, obj); - vxml = NULL; - if (!JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - } - - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = Equals(cx, xml, v, bp); - } else if (vxml) { - if (vxml->xml_class == JSXML_CLASS_LIST) { - ok = Equals(cx, vxml, OBJECT_TO_JSVAL(obj), bp); - } else { - if (((xml->xml_class == JSXML_CLASS_TEXT || - xml->xml_class == JSXML_CLASS_ATTRIBUTE) && - HasSimpleContent(vxml)) || - ((vxml->xml_class == JSXML_CLASS_TEXT || - vxml->xml_class == JSXML_CLASS_ATTRIBUTE) && - HasSimpleContent(xml))) { - ok = js_EnterLocalRootScope(cx); - if (ok) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - vstr = js_ValueToString(cx, v); - ok = str && vstr; - if (ok) - *bp = js_EqualStrings(str, vstr); - js_LeaveLocalRootScope(cx); - } - } else { - ok = XMLEquals(cx, xml, vxml, bp); - } - } - } else { - ok = js_EnterLocalRootScope(cx); - if (ok) { - if (HasSimpleContent(xml)) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - vstr = js_ValueToString(cx, v); - ok = str && vstr; - if (ok) - *bp = js_EqualStrings(str, vstr); - } else if (JSVAL_IS_STRING(v) || JSVAL_IS_NUMBER(v)) { - str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) { - ok = JS_FALSE; - } else if (JSVAL_IS_STRING(v)) { - *bp = js_EqualStrings(str, JSVAL_TO_STRING(v)); - } else { - ok = js_ValueToNumber(cx, STRING_TO_JSVAL(str), &d); - if (ok) { - d2 = JSVAL_IS_INT(v) ? JSVAL_TO_INT(v) - : *JSVAL_TO_DOUBLE(v); - *bp = JSDOUBLE_COMPARE(d, ==, d2, JS_FALSE); - } - } - } else { - *bp = JS_FALSE; - } - js_LeaveLocalRootScope(cx); - } - } - return ok; -} - -static JSBool -xml_concatenate(JSContext *cx, JSObject *obj, jsval v, jsval *vp) -{ - JSBool ok; - JSObject *listobj, *robj; - JSXML *list, *lxml, *rxml; - - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - goto out; - } - - list = (JSXML *) JS_GetPrivate(cx, listobj); - lxml = (JSXML *) JS_GetPrivate(cx, obj); - ok = Append(cx, list, lxml); - if (!ok) - goto out; - - if (VALUE_IS_XML(cx, v)) { - rxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - } else { - robj = ToXML(cx, v); - if (!robj) { - ok = JS_FALSE; - goto out; - } - rxml = (JSXML *) JS_GetPrivate(cx, robj); - } - ok = Append(cx, list, rxml); - if (!ok) - goto out; - - *vp = OBJECT_TO_JSVAL(listobj); -out: - js_LeaveLocalRootScopeWithResult(cx, *vp); - return ok; -} - -/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ -JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps = { - { js_NewObjectMap, js_DestroyObjectMap, - xml_lookupProperty, xml_defineProperty, - xml_getProperty, xml_setProperty, - xml_getAttributes, xml_setAttributes, - xml_deleteProperty, xml_defaultValue, - xml_enumerate, js_CheckAccess, - NULL, NULL, - NULL, NULL, - NULL, xml_hasInstance, - js_SetProtoOrParent, js_SetProtoOrParent, - xml_mark, xml_clear, - NULL, NULL }, - xml_getMethod, xml_setMethod, - xml_enumerateValues, xml_equality, - xml_concatenate -}; - -static JSObjectOps * -xml_getObjectOps(JSContext *cx, JSClass *clasp) -{ - return &js_XMLObjectOps.base; -} - -JS_FRIEND_DATA(JSClass) js_XMLClass = { - js_XML_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML), - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, xml_finalize, - xml_getObjectOps, NULL, NULL, NULL, - NULL, NULL, NULL, NULL -}; - -static JSObject * -CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp, - uintN argc, jsval *argv) -{ - JSObject *tmp; - jsval rval; - - while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) - obj = tmp; - if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval)) - return NULL; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); - return JSVAL_TO_OBJECT(rval); -} - -static JSXML * -StartNonListXMLMethod(JSContext *cx, JSObject **objp, jsval *argv) -{ - JSXML *xml; - JSFunction *fun; - - JS_ASSERT(VALUE_IS_FUNCTION(cx, argv[-2])); - - xml = (JSXML *) JS_GetInstancePrivate(cx, *objp, &js_XMLClass, argv); - if (!xml || xml->xml_class != JSXML_CLASS_LIST) - return xml; - - if (xml->xml_kids.length == 1) { - xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (xml) { - *objp = js_GetXMLObject(cx, xml); - if (!*objp) - return NULL; - argv[-1] = OBJECT_TO_JSVAL(*objp); - return xml; - } - } - - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[-2])); - if (fun) { - char numBuf[12]; - JS_snprintf(numBuf, sizeof numBuf, "%u", xml->xml_kids.length); - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_NON_LIST_XML_METHOD, - JS_GetFunctionName(fun), numBuf); - } - return NULL; -} - -#define XML_METHOD_PROLOG \ - JS_BEGIN_MACRO \ - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, argv); \ - if (!xml) \ - return JS_FALSE; \ - JS_END_MACRO - -#define NON_LIST_XML_METHOD_PROLOG \ - JS_BEGIN_MACRO \ - xml = StartNonListXMLMethod(cx, &obj, argv); \ - if (!xml) \ - return JS_FALSE; \ - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); \ - JS_END_MACRO - -static JSBool -xml_addNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSObject *nsobj; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); - if (!nsobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nsobj); - - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - if (!AddInScopeNamespace(cx, xml, ns)) - return JS_FALSE; - ns->declared = JS_TRUE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_appendChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *vxml; - jsval name, v; - JSObject *vobj; - - NON_LIST_XML_METHOD_PROLOG; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - if (!js_GetAnyName(cx, &name)) - return JS_FALSE; - - if (!GetProperty(cx, obj, name, &v)) - return JS_FALSE; - - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - vobj = JSVAL_TO_OBJECT(v); - JS_ASSERT(OBJECT_IS_XML(cx, vobj)); - vxml = (JSXML *) JS_GetPrivate(cx, vobj); - JS_ASSERT(vxml->xml_class == JSXML_CLASS_LIST); - - if (!IndexToIdVal(cx, vxml->xml_kids.length, &name)) - return JS_FALSE; - if (!PutProperty(cx, JSVAL_TO_OBJECT(v), name, &argv[0])) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_attribute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXMLQName *qn; - - qn = ToAttributeName(cx, argv[0]); - if (!qn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(qn->object); /* local root */ - return GetProperty(cx, obj, argv[0], rval); -} - -/* XML and XMLList */ -static JSBool -xml_attributes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - JSXMLQName *qn; - JSTempValueRooter tvr; - JSBool ok; - - name = ATOM_KEY(cx->runtime->atomState.starAtom); - qn = ToAttributeName(cx, name); - if (!qn) - return JS_FALSE; - name = OBJECT_TO_JSVAL(qn->object); - JS_PUSH_SINGLE_TEMP_ROOT(cx, name, &tvr); - ok = GetProperty(cx, obj, name, rval); - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -static JSXML * -xml_list_helper(JSContext *cx, JSXML *xml, jsval *rval) -{ - JSObject *listobj; - JSXML *list; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return NULL; - - *rval = OBJECT_TO_JSVAL(listobj); - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - return list; -} - -static JSBool -xml_child_helper(JSContext *cx, JSObject *obj, JSXML *xml, jsval name, - jsval *rval) -{ - uint32 index; - JSXML *kid; - JSObject *kidobj; - - /* ECMA-357 13.4.4.6 */ - JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST); - - if (js_IdIsIndex(name, &index)) { - if (index >= JSXML_LENGTH(xml)) { - *rval = JSVAL_VOID; - } else { - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (!kid) { - *rval = JSVAL_VOID; - } else { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(kidobj); - } - } - return JS_TRUE; - } - - return GetProperty(cx, obj, name, rval); -} - -/* XML and XMLList */ -static JSBool -xml_child(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSXMLArrayCursor cursor; - jsval name, v; - JSObject *kidobj; - - XML_METHOD_PROLOG; - name = argv[0]; - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 13.5.4.4 */ - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - break; - if (!xml_child_helper(cx, kidobj, kid, name, &v)) - break; - if (JSVAL_IS_VOID(v)) { - /* The property didn't exist in this kid. */ - continue; - } - - JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if ((!JSXML_HAS_KIDS(vxml) || vxml->xml_kids.length != 0) && - !Append(cx, list, vxml)) { - break; - } - } - XMLArrayCursorFinish(&cursor); - return !kid; - } - - /* ECMA-357 Edition 2 13.3.4.6 (note 13.3, not 13.4 as in Edition 1). */ - if (!xml_child_helper(cx, obj, xml, name, rval)) - return JS_FALSE; - if (JSVAL_IS_VOID(*rval) && !xml_list_helper(cx, xml, rval)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -xml_childIndex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *parent; - uint32 i, n; - - NON_LIST_XML_METHOD_PROLOG; - parent = xml->parent; - if (!parent || xml->xml_class == JSXML_CLASS_ATTRIBUTE) { - *rval = DOUBLE_TO_JSVAL(cx->runtime->jsNaN); - return JS_TRUE; - } - for (i = 0, n = JSXML_LENGTH(parent); i < n; i++) { - if (XMLARRAY_MEMBER(&parent->xml_kids, i, JSXML) == xml) - break; - } - JS_ASSERT(i < n); - return js_NewNumberValue(cx, i, rval); -} - -/* XML and XMLList */ -static JSBool -xml_children(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - - name = ATOM_KEY(cx->runtime->atomState.starAtom); - return GetProperty(cx, obj, name, rval); -} - -/* XML and XMLList */ -static JSBool -xml_comments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - JSBool ok; - uint32 i, n; - JSObject *kidobj; - jsval v; - - XML_METHOD_PROLOG; - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.6 Step 2. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_comments(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - } else { - /* 13.4.4.9 Step 2. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_COMMENT) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_contains(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval value; - JSBool eq; - JSXMLArrayCursor cursor; - JSObject *kidobj; - - XML_METHOD_PROLOG; - value = argv[0]; - if (xml->xml_class == JSXML_CLASS_LIST) { - eq = JS_FALSE; - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !xml_equality(cx, kidobj, value, &eq)) - break; - if (eq) - break; - } - XMLArrayCursorFinish(&cursor); - if (kid && !eq) - return JS_FALSE; - } else { - if (!xml_equality(cx, obj, value, &eq)) - return JS_FALSE; - } - *rval = BOOLEAN_TO_JSVAL(eq); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *copy; - - XML_METHOD_PROLOG; - copy = DeepCopy(cx, xml, NULL, 0); - if (!copy) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(copy->object); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_descendants(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list; - jsval name; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - list = Descendants(cx, xml, name); - if (!list) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(list->object); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_elements(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - jsval name, v; - JSXMLQName *nameqn; - jsid funid; - JSBool ok; - JSXMLArrayCursor cursor; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - nameqn = ToXMLName(cx, name, &funid); - if (!nameqn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameqn->object); - - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - if (funid) - return JS_TRUE; - - list->xml_targetprop = nameqn; - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.6 */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_elements(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } else { - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT && - MatchElemName(nameqn, kid)) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -/* XML and XMLList */ -static JSBool -xml_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval name; - JSObject *pobj; - JSProperty *prop; - - if (!JS_InstanceOf(cx, obj, &js_XMLClass, argv)) - return JS_FALSE; - - name = argv[0]; - if (!HasProperty(cx, obj, name, &pobj, &prop)) - return JS_FALSE; - if (!prop) { - return js_HasOwnPropertyHelper(cx, obj, js_LookupProperty, argc, argv, - rval); - } - DROP_PROPERTY(cx, pobj, prop); - *rval = JSVAL_TRUE; - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_hasComplexContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; -again: - switch (xml->xml_class) { - case JSXML_CLASS_ATTRIBUTE: - case JSXML_CLASS_COMMENT: - case JSXML_CLASS_PROCESSING_INSTRUCTION: - case JSXML_CLASS_TEXT: - *rval = JSVAL_FALSE; - break; - case JSXML_CLASS_LIST: - if (xml->xml_kids.length == 0) { - *rval = JSVAL_TRUE; - } else if (xml->xml_kids.length == 1) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (kid) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - return JS_FALSE; - obj = kidobj; - xml = (JSXML *) JS_GetPrivate(cx, obj); - goto again; - } - } - /* FALL THROUGH */ - default: - *rval = JSVAL_FALSE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - *rval = JSVAL_TRUE; - break; - } - } - break; - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_hasSimpleContent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - *rval = BOOLEAN_TO_JSVAL(HasSimpleContent(xml)); - return JS_TRUE; -} - -typedef struct JSTempRootedNSArray { - JSTempValueRooter tvr; - JSXMLArray array; - jsval value; /* extra root for temporaries */ -} JSTempRootedNSArray; - -JS_STATIC_DLL_CALLBACK(void) -mark_temp_ns_array(JSContext *cx, JSTempValueRooter *tvr) -{ - JSTempRootedNSArray *tmp = (JSTempRootedNSArray *)tvr; - - namespace_mark_vector(cx, - (JSXMLNamespace **)tmp->array.vector, - tmp->array.length); - XMLArrayCursorMark(cx, tmp->array.cursors); - if (JSVAL_IS_GCTHING(tmp->value)) - GC_MARK(cx, JSVAL_TO_GCTHING(tmp->value), "temp_ns_array_value"); -} - -static void -InitTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) -{ - XMLArrayInit(cx, &tmp->array, 0); - tmp->value = JSVAL_NULL; - JS_PUSH_TEMP_ROOT_MARKER(cx, mark_temp_ns_array, &tmp->tvr); -} - -static void -FinishTempNSArray(JSContext *cx, JSTempRootedNSArray *tmp) -{ - JS_ASSERT(tmp->tvr.u.marker == mark_temp_ns_array); - JS_POP_TEMP_ROOT(cx, &tmp->tvr); - XMLArrayFinish(cx, &tmp->array); -} - -/* - * Populate a new JS array with elements of JSTempRootedNSArray.array and - * place the result into rval. rval must point to a rooted location. - */ -static JSBool -TempNSArrayToJSArray(JSContext *cx, JSTempRootedNSArray *tmp, jsval *rval) -{ - JSObject *arrayobj; - uint32 i, n; - JSXMLNamespace *ns; - JSObject *nsobj; - - arrayobj = js_NewArrayObject(cx, 0, NULL); - if (!arrayobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(arrayobj); - for (i = 0, n = tmp->array.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&tmp->array, i, JSXMLNamespace); - if (!ns) - continue; - nsobj = js_GetXMLNamespaceObject(cx, ns); - if (!nsobj) - return JS_FALSE; - tmp->value = OBJECT_TO_JSVAL(nsobj); - if (!OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(i), &tmp->value)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -FindInScopeNamespaces(JSContext *cx, JSXML *xml, JSXMLArray *nsarray) -{ - uint32 length, i, j, n; - JSXMLNamespace *ns, *ns2; - - length = nsarray->length; - do { - if (xml->xml_class != JSXML_CLASS_ELEMENT) - continue; - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - - for (j = 0; j < length; j++) { - ns2 = XMLARRAY_MEMBER(nsarray, j, JSXMLNamespace); - if (ns2 && - ((ns2->prefix && ns->prefix) - ? js_EqualStrings(ns2->prefix, ns->prefix) - : js_EqualStrings(ns2->uri, ns->uri))) { - break; - } - } - - if (j == length) { - if (!XMLARRAY_APPEND(cx, nsarray, ns)) - return JS_FALSE; - ++length; - } - } - } while ((xml = xml->parent) != NULL); - JS_ASSERT(length == nsarray->length); - - return JS_TRUE; -} - -static JSBool -xml_inScopeNamespaces(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSTempRootedNSArray namespaces; - JSBool ok; - - NON_LIST_XML_METHOD_PROLOG; - - InitTempNSArray(cx, &namespaces); - ok = FindInScopeNamespaces(cx, xml, &namespaces.array) && - TempNSArrayToJSArray(cx, &namespaces, rval); - FinishTempNSArray(cx, &namespaces); - return ok; -} - -static JSBool -xml_insertChildAfter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval arg; - uint32 i; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - arg = argv[0]; - if (JSVAL_IS_NULL(arg)) { - kid = NULL; - i = 0; - } else { - if (!VALUE_IS_XML(cx, arg)) - return JS_TRUE; - kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); - if (i == XML_NOT_FOUND) - return JS_TRUE; - ++i; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - if (!Insert(cx, xml, i, argv[1])) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_insertChildBefore(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid; - jsval arg; - uint32 i; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - arg = argv[0]; - if (JSVAL_IS_NULL(arg)) { - kid = NULL; - i = xml->xml_kids.length; - } else { - if (!VALUE_IS_XML(cx, arg)) - return JS_TRUE; - kid = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(arg)); - i = XMLARRAY_FIND_MEMBER(&xml->xml_kids, kid, NULL); - if (i == XML_NOT_FOUND) - return JS_TRUE; - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - if (!Insert(cx, xml, i, argv[1])) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_length(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml; - - XML_METHOD_PROLOG; - if (xml->xml_class != JSXML_CLASS_LIST) { - *rval = JSVAL_ONE; - } else { - if (!js_NewNumberValue(cx, xml->xml_kids.length, rval)) - return JS_FALSE; - } - return JS_TRUE; -} - -static JSBool -xml_localName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - NON_LIST_XML_METHOD_PROLOG; - *rval = xml->name ? STRING_TO_JSVAL(xml->name->localName) : JSVAL_NULL; - return JS_TRUE; -} - -static JSBool -xml_name(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml; - JSObject *nameobj; - - NON_LIST_XML_METHOD_PROLOG; - if (!xml->name) { - *rval = JSVAL_NULL; - } else { - nameobj = js_GetXMLQNameObject(cx, xml->name); - if (!nameobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(nameobj); - } - return JS_TRUE; -} - -static JSBool -xml_namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *prefix; - JSTempRootedNSArray inScopeNSes; - JSBool ok; - jsuint i, length; - JSXMLNamespace *ns; - JSObject *nsobj; - - NON_LIST_XML_METHOD_PROLOG; - if (argc == 0 && !JSXML_HAS_NAME(xml)) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - - if (argc == 0) { - prefix = NULL; - } else { - prefix = js_ValueToString(cx, argv[0]); - if (!prefix) - return JS_FALSE; - argv[0] = STRING_TO_JSVAL(prefix); /* local root */ - } - - /* After this point the control must flow through label out. */ - InitTempNSArray(cx, &inScopeNSes); - ok = FindInScopeNamespaces(cx, xml, &inScopeNSes.array); - if (!ok) - goto out; - - if (!prefix) { - ns = GetNamespace(cx, xml->name, &inScopeNSes.array); - if (!ns) { - ok = JS_FALSE; - goto out; - } - } else { - ns = NULL; - for (i = 0, length = inScopeNSes.array.length; i < length; i++) { - ns = XMLARRAY_MEMBER(&inScopeNSes.array, i, JSXMLNamespace); - if (ns && ns->prefix && js_EqualStrings(ns->prefix, prefix)) - break; - ns = NULL; - } - } - - if (!ns) { - *rval = JSVAL_VOID; - } else { - nsobj = js_GetXMLNamespaceObject(cx, ns); - if (!nsobj) { - ok = JS_FALSE; - goto out; - } - *rval = OBJECT_TO_JSVAL(nsobj); - } - - out: - FinishTempNSArray(cx, &inScopeNSes); - return JS_TRUE; -} - -static JSBool -xml_namespaceDeclarations(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *yml; - JSBool ok; - JSTempRootedNSArray ancestors, declared; - uint32 i, n; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - if (JSXML_HAS_VALUE(xml)) - return JS_TRUE; - - /* From here, control flow must goto out to finish these arrays. */ - ok = JS_TRUE; - InitTempNSArray(cx, &ancestors); - InitTempNSArray(cx, &declared); - yml = xml; - - while ((yml = yml->parent) != NULL) { - JS_ASSERT(yml->xml_class == JSXML_CLASS_ELEMENT); - for (i = 0, n = yml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&yml->xml_namespaces, i, JSXMLNamespace); - if (ns && - !XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { - ok = XMLARRAY_APPEND(cx, &ancestors.array, ns); - if (!ok) - goto out; - } - } - } - - for (i = 0, n = xml->xml_namespaces.length; i < n; i++) { - ns = XMLARRAY_MEMBER(&xml->xml_namespaces, i, JSXMLNamespace); - if (!ns) - continue; - if (!ns->declared) - continue; - if (!XMLARRAY_HAS_MEMBER(&ancestors.array, ns, namespace_match)) { - ok = XMLARRAY_APPEND(cx, &declared.array, ns); - if (!ok) - goto out; - } - } - - ok = TempNSArrayToJSArray(cx, &declared, rval); - -out: - /* Finishing must be in reverse order of initialization to follow LIFO. */ - FinishTempNSArray(cx, &declared); - FinishTempNSArray(cx, &ancestors); - return ok; -} - -static const char js_attribute_str[] = "attribute"; -static const char js_text_str[] = "text"; - -/* Exported to jsgc.c #ifdef GC_MARK_DEBUG. */ -const char *js_xml_class_str[] = { - "list", - "element", - js_attribute_str, - "processing-instruction", - js_text_str, - "comment" -}; - -static JSBool -xml_nodeKind(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *str; - - NON_LIST_XML_METHOD_PROLOG; - str = JS_InternString(cx, js_xml_class_str[xml->xml_class]); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -static JSBool -NormalizingDelete(JSContext *cx, JSObject *obj, JSXML *xml, jsval id) -{ - jsval junk; - - if (xml->xml_class == JSXML_CLASS_LIST) - return DeleteProperty(cx, obj, id, &junk); - return DeleteByIndex(cx, xml, id, &junk); -} - -/* - * Erratum? the testcase js/tests/e4x/XML/13.4.4.26.js wants all-whitespace - * text between tags to be removed by normalize. - */ -static JSBool -IsXMLSpace(JSString *str) -{ - const jschar *cp, *end; - - cp = JSSTRING_CHARS(str); - end = cp + JSSTRING_LENGTH(str); - while (cp < end) { - if (!JS_ISXMLSPACE(*cp)) - return JS_FALSE; - ++cp; - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_normalize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *kid, *kid2; - uint32 i, n; - JSObject *kidobj; - JSString *str; - jsval junk; - - XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (!JSXML_HAS_KIDS(xml)) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (!kid) - continue; - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj || !xml_normalize(cx, kidobj, argc, argv, &junk)) - return JS_FALSE; - } else if (kid->xml_class == JSXML_CLASS_TEXT) { - while (i + 1 < n && - (kid2 = XMLARRAY_MEMBER(&xml->xml_kids, i + 1, JSXML)) && - kid2->xml_class == JSXML_CLASS_TEXT) { - str = js_ConcatStrings(cx, kid->xml_value, kid2->xml_value); - if (!str) - return JS_FALSE; - if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i + 1))) - return JS_FALSE; - n = xml->xml_kids.length; - kid->xml_value = str; - } - if (IS_EMPTY(kid->xml_value) || IsXMLSpace(kid->xml_value)) { - if (!NormalizingDelete(cx, obj, xml, INT_TO_JSVAL(i))) - return JS_FALSE; - n = xml->xml_kids.length; - --i; - } - } - } - - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *parent, *kid; - uint32 i, n; - JSObject *parentobj; - - XML_METHOD_PROLOG; - parent = xml->parent; - if (xml->xml_class == JSXML_CLASS_LIST) { - *rval = JSVAL_VOID; - n = xml->xml_kids.length; - if (n == 0) - return JS_TRUE; - - kid = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML); - if (!kid) - return JS_TRUE; - parent = kid->parent; - for (i = 1; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->parent != parent) - return JS_TRUE; - } - } - - if (!parent) { - *rval = JSVAL_NULL; - return JS_TRUE; - } - - parentobj = js_GetXMLObject(cx, parent); - if (!parentobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(parentobj); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_processingInstructions(JSContext *cx, JSObject *obj, uintN argc, - jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - jsval name, v; - JSXMLQName *nameqn; - jsid funid; - JSBool ok; - JSXMLArrayCursor cursor; - JSObject *kidobj; - uint32 i, n; - - XML_METHOD_PROLOG; - name = (argc == 0) ? ATOM_KEY(cx->runtime->atomState.starAtom) : argv[0]; - nameqn = ToXMLName(cx, name, &funid); - if (!nameqn) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameqn->object); - - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - if (funid) - return JS_TRUE; - - list->xml_targetprop = nameqn; - ok = JS_TRUE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.17 Step 4 (misnumbered 9 -- Erratum?). */ - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_processingInstructions(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - break; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0) { - ok = Append(cx, list, vxml); - if (!ok) - break; - } - } - } - XMLArrayCursorFinish(&cursor); - } else { - /* 13.4.4.28 Step 4. */ - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && - (IS_STAR(nameqn->localName) || - js_EqualStrings(nameqn->localName, kid->name->localName))) { - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - } - - return ok; -} - -static JSBool -xml_prependChild(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - - NON_LIST_XML_METHOD_PROLOG; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); - return Insert(cx, xml, 0, argv[0]); -} - -/* XML and XMLList */ -static JSBool -xml_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - jsval name; - uint32 index; - - XML_METHOD_PROLOG; - name = argv[0]; - *rval = JSVAL_FALSE; - if (js_IdIsIndex(name, &index)) { - if (xml->xml_class == JSXML_CLASS_LIST) { - /* 13.5.4.18. */ - *rval = BOOLEAN_TO_JSVAL(index < xml->xml_kids.length); - } else { - /* 13.4.4.30. */ - *rval = BOOLEAN_TO_JSVAL(index == 0); - } - } - return JS_TRUE; -} - -static JSBool -namespace_full_match(const void *a, const void *b) -{ - const JSXMLNamespace *nsa = (const JSXMLNamespace *) a; - const JSXMLNamespace *nsb = (const JSXMLNamespace *) b; - - if (nsa->prefix && nsb->prefix && - !js_EqualStrings(nsa->prefix, nsb->prefix)) { - return JS_FALSE; - } - return js_EqualStrings(nsa->uri, nsb->uri); -} - -static JSBool -xml_removeNamespace_helper(JSContext *cx, JSXML *xml, JSXMLNamespace *ns) -{ - JSXMLNamespace *thisns, *attrns; - uint32 i, n; - JSXML *attr, *kid; - - thisns = GetNamespace(cx, xml->name, &xml->xml_namespaces); - JS_ASSERT(thisns); - if (thisns == ns) - return JS_TRUE; - - for (i = 0, n = xml->xml_attrs.length; i < n; i++) { - attr = XMLARRAY_MEMBER(&xml->xml_attrs, i, JSXML); - if (!attr) - continue; - attrns = GetNamespace(cx, attr->name, &xml->xml_namespaces); - JS_ASSERT(attrns); - if (attrns == ns) - return JS_TRUE; - } - - i = XMLARRAY_FIND_MEMBER(&xml->xml_namespaces, ns, namespace_full_match); - if (i != XML_NOT_FOUND) - XMLArrayDelete(cx, &xml->xml_namespaces, i, JS_TRUE); - - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - if (!xml_removeNamespace_helper(cx, kid, ns)) - return JS_FALSE; - } - } - return JS_TRUE; -} - -static JSBool -xml_removeNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSObject *nsobj; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, argv); - if (!nsobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nsobj); - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - - /* NOTE: remove ns from each ancestor if not used by that ancestor. */ - return xml_removeNamespace_helper(cx, xml, ns); -} - -static JSBool -xml_replace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *vxml, *kid; - jsval name, value, id, junk; - uint32 index; - JSObject *nameobj; - JSXMLQName *nameqn; - - NON_LIST_XML_METHOD_PROLOG; - *rval = OBJECT_TO_JSVAL(obj); - if (xml->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - - value = argv[1]; - vxml = VALUE_IS_XML(cx, value) - ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value)) - : NULL; - if (!vxml) { - if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &argv[1])) - return JS_FALSE; - value = argv[1]; - } else { - vxml = DeepCopy(cx, vxml, NULL, 0); - if (!vxml) - return JS_FALSE; - value = argv[1] = OBJECT_TO_JSVAL(vxml->object); - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - - name = argv[0]; - if (js_IdIsIndex(name, &index)) - return Replace(cx, xml, name, value); - - /* Call function QName per spec, not ToXMLName, to avoid attribute names. */ - nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name); - if (!nameobj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(nameobj); - nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); - - id = JSVAL_VOID; - index = xml->xml_kids.length; - while (index != 0) { - --index; - kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML); - if (kid && MatchElemName(nameqn, kid)) { - if (!JSVAL_IS_VOID(id) && !DeleteByIndex(cx, xml, id, &junk)) - return JS_FALSE; - if (!IndexToIdVal(cx, index, &id)) - return JS_FALSE; - } - } - if (JSVAL_IS_VOID(id)) - return JS_TRUE; - return Replace(cx, xml, id, value); -} - -static JSBool -xml_setChildren(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - if (!StartNonListXMLMethod(cx, &obj, argv)) - return JS_FALSE; - - if (!PutProperty(cx, obj, ATOM_KEY(cx->runtime->atomState.starAtom), - &argv[0])) { - return JS_FALSE; - } - - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSBool -xml_setLocalName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - jsval name; - JSXMLQName *nameqn; - JSString *namestr; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - name = argv[0]; - if (!JSVAL_IS_PRIMITIVE(name) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base) { - nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name)); - namestr = nameqn->localName; - } else { - if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &argv[0])) - return JS_FALSE; - name = argv[0]; - namestr = JSVAL_TO_STRING(name); - } - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - xml->name->localName = namestr; - return JS_TRUE; -} - -static JSBool -xml_setName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *nsowner; - jsval name; - JSXMLQName *nameqn; - JSObject *nameobj; - JSXMLArray *nsarray; - uint32 i, n; - JSXMLNamespace *ns; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - name = argv[0]; - if (!JSVAL_IS_PRIMITIVE(name) && - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(name)) == &js_QNameClass.base && - !(nameqn = (JSXMLQName *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(name))) - ->uri) { - name = argv[0] = STRING_TO_JSVAL(nameqn->localName); - } - - nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, &name); - if (!nameobj) - return JS_FALSE; - nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj); - - /* ECMA-357 13.4.4.35 Step 4. */ - if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION) - nameqn->uri = cx->runtime->emptyString; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml) - return JS_FALSE; - xml->name = nameqn; - - /* - * Erratum: nothing in 13.4.4.35 talks about making the name match the - * in-scope namespaces, either by finding an in-scope namespace with a - * matching uri and setting the new name's prefix to that namespace's - * prefix, or by extending the in-scope namespaces for xml (which are in - * xml->parent if xml is an attribute or a PI). - */ - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - nsowner = xml; - } else { - if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - nsowner = xml->parent; - } - - if (nameqn->prefix) { - /* - * The name being set has a prefix, which originally came from some - * namespace object (which may be the null namespace, where both the - * prefix and uri are the empty string). We must go through a full - * GetNamespace in case that namespace is in-scope in nsowner. - * - * If we find such an in-scope namespace, we return true right away, - * in this block. Otherwise, we fall through to the final return of - * AddInScopeNamespace(cx, nsowner, ns). - */ - ns = GetNamespace(cx, nameqn, &nsowner->xml_namespaces); - if (!ns) - return JS_FALSE; - - /* XXXbe have to test membership to see whether GetNamespace added */ - if (XMLARRAY_HAS_MEMBER(&nsowner->xml_namespaces, ns, NULL)) - return JS_TRUE; - } else { - /* - * At this point, we know nameqn->prefix is null, so nameqn->uri can't - * be the empty string (the null namespace always uses the empty string - * for both prefix and uri). - * - * This means we must inline GetNamespace and specialize it to match - * uri only, never prefix. If we find a namespace with nameqn's uri - * already in nsowner->xml_namespaces, then all that we need do is set - * nameqn->prefix to that namespace's prefix. - * - * If no such namespace exists, we can create one without going through - * the constructor, because we know nameqn->uri is non-empty (so prefix - * does not need to be converted from null to empty by QName). - */ - JS_ASSERT(!IS_EMPTY(nameqn->uri)); - - nsarray = &nsowner->xml_namespaces; - for (i = 0, n = nsarray->length; i < n; i++) { - ns = XMLARRAY_MEMBER(nsarray, i, JSXMLNamespace); - if (ns && js_EqualStrings(ns->uri, nameqn->uri)) { - nameqn->prefix = ns->prefix; - return JS_TRUE; - } - } - - ns = js_NewXMLNamespace(cx, NULL, nameqn->uri, JS_TRUE); - if (!ns) - return JS_FALSE; - } - - return AddInScopeNamespace(cx, nsowner, ns); -} - -static JSBool -xml_setNamespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml, *nsowner; - JSObject *nsobj, *qnobj; - JSXMLNamespace *ns; - jsval qnargv[2]; - - NON_LIST_XML_METHOD_PROLOG; - if (!JSXML_HAS_NAME(xml)) - return JS_TRUE; - - xml = CHECK_COPY_ON_WRITE(cx, xml, obj); - if (!xml || !js_GetXMLQNameObject(cx, xml->name)) - return JS_FALSE; - - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 1, argv); - if (!nsobj) - return JS_FALSE; - ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj); - ns->declared = JS_TRUE; - - qnargv[0] = argv[0] = OBJECT_TO_JSVAL(nsobj); - qnargv[1] = OBJECT_TO_JSVAL(xml->name->object); - qnobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, qnargv); - if (!qnobj) - return JS_FALSE; - - xml->name = (JSXMLQName *) JS_GetPrivate(cx, qnobj); - - /* - * Erratum: the spec fails to update the governing in-scope namespaces. - * See the erratum noted in xml_setName, above. - */ - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - nsowner = xml; - } else { - if (!xml->parent || xml->parent->xml_class != JSXML_CLASS_ELEMENT) - return JS_TRUE; - nsowner = xml->parent; - } - return AddInScopeNamespace(cx, nsowner, ns); -} - -/* XML and XMLList */ -static JSBool -xml_text(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSXML *xml, *list, *kid, *vxml; - uint32 i, n; - JSBool ok; - JSObject *kidobj; - jsval v; - - XML_METHOD_PROLOG; - list = xml_list_helper(cx, xml, rval); - if (!list) - return JS_FALSE; - - if (xml->xml_class == JSXML_CLASS_LIST) { - ok = JS_TRUE; - for (i = 0, n = xml->xml_kids.length; i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = js_EnterLocalRootScope(cx); - if (!ok) - break; - kidobj = js_GetXMLObject(cx, kid); - if (kidobj) { - ok = xml_text(cx, kidobj, argc, argv, &v); - } else { - ok = JS_FALSE; - v = JSVAL_NULL; - } - js_LeaveLocalRootScopeWithResult(cx, v); - if (!ok) - return JS_FALSE; - vxml = (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v)); - if (JSXML_LENGTH(vxml) != 0 && !Append(cx, list, vxml)) - return JS_FALSE; - } - } - } else { - for (i = 0, n = JSXML_LENGTH(xml); i < n; i++) { - kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML); - if (kid && kid->xml_class == JSXML_CLASS_TEXT) { - if (!Append(cx, list, kid)) - return JS_FALSE; - } - } - } - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_toXMLString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSString *str; - - str = ToXMLString(cx, OBJECT_TO_JSVAL(obj)); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSString * -xml_toString_helper(JSContext *cx, JSXML *xml) -{ - JSString *str, *kidstr; - JSXML *kid; - JSXMLArrayCursor cursor; - - if (xml->xml_class == JSXML_CLASS_ATTRIBUTE || - xml->xml_class == JSXML_CLASS_TEXT) { - return xml->xml_value; - } - - if (!HasSimpleContent(xml)) - return ToXMLString(cx, OBJECT_TO_JSVAL(xml->object)); - - str = cx->runtime->emptyString; - js_EnterLocalRootScope(cx); - XMLArrayCursorInit(&cursor, &xml->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class != JSXML_CLASS_COMMENT && - kid->xml_class != JSXML_CLASS_PROCESSING_INSTRUCTION) { - kidstr = xml_toString_helper(cx, kid); - if (!kidstr) { - str = NULL; - break; - } - str = js_ConcatStrings(cx, str, kidstr); - if (!str) - break; - } - } - XMLArrayCursorFinish(&cursor); - js_LeaveLocalRootScopeWithResult(cx, STRING_TO_JSVAL(str)); - return str; -} - -static JSBool -xml_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSXML *xml; - JSString *str; - - XML_METHOD_PROLOG; - str = xml_toString_helper(cx, xml); - if (!str) - return JS_FALSE; - *rval = STRING_TO_JSVAL(str); - return JS_TRUE; -} - -/* XML and XMLList */ -static JSBool -xml_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -static JSFunctionSpec xml_methods[] = { - {"addNamespace", xml_addNamespace, 1,0,0}, - {"appendChild", xml_appendChild, 1,0,0}, - {js_attribute_str, xml_attribute, 1,0,0}, - {"attributes", xml_attributes, 0,0,0}, - {"child", xml_child, 1,0,0}, - {"childIndex", xml_childIndex, 0,0,0}, - {"children", xml_children, 0,0,0}, - {"comments", xml_comments, 0,0,0}, - {"contains", xml_contains, 1,0,0}, - {"copy", xml_copy, 0,0,0}, - {"descendants", xml_descendants, 1,0,0}, - {"elements", xml_elements, 1,0,0}, - {"hasOwnProperty", xml_hasOwnProperty, 1,0,0}, - {"hasComplexContent", xml_hasComplexContent, 1,0,0}, - {"hasSimpleContent", xml_hasSimpleContent, 1,0,0}, - {"inScopeNamespaces", xml_inScopeNamespaces, 0,0,0}, - {"insertChildAfter", xml_insertChildAfter, 2,0,0}, - {"insertChildBefore", xml_insertChildBefore, 2,0,0}, - {js_length_str, xml_length, 0,0,0}, - {js_localName_str, xml_localName, 0,0,0}, - {js_name_str, xml_name, 0,0,0}, - {js_namespace_str, xml_namespace, 1,0,0}, - {"namespaceDeclarations", xml_namespaceDeclarations, 0,0,0}, - {"nodeKind", xml_nodeKind, 0,0,0}, - {"normalize", xml_normalize, 0,0,0}, - {js_xml_parent_str, xml_parent, 0,0,0}, - {"processingInstructions",xml_processingInstructions,1,0,0}, - {"prependChild", xml_prependChild, 1,0,0}, - {"propertyIsEnumerable", xml_propertyIsEnumerable, 1,0,0}, - {"removeNamespace", xml_removeNamespace, 1,0,0}, - {"replace", xml_replace, 2,0,0}, - {"setChildren", xml_setChildren, 1,0,0}, - {"setLocalName", xml_setLocalName, 1,0,0}, - {"setName", xml_setName, 1,0,0}, - {"setNamespace", xml_setNamespace, 1,0,0}, - {js_text_str, xml_text, 0,0,0}, - {js_toString_str, xml_toString, 0,0,0}, - {js_toXMLString_str, xml_toXMLString, 0,0,0}, - {js_toSource_str, xml_toXMLString, 0,0,0}, - {js_valueOf_str, xml_valueOf, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -CopyXMLSettings(JSContext *cx, JSObject *from, JSObject *to) -{ - int i; - const char *name; - jsval v; - - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - name = xml_static_props[i].name; - if (!JS_GetProperty(cx, from, name, &v)) - return JS_FALSE; - if (JSVAL_IS_BOOLEAN(v) && !JS_SetProperty(cx, to, name, &v)) - return JS_FALSE; - } - - name = xml_static_props[i].name; - if (!JS_GetProperty(cx, from, name, &v)) - return JS_FALSE; - if (JSVAL_IS_NUMBER(v) && !JS_SetProperty(cx, to, name, &v)) - return JS_FALSE; - return JS_TRUE; -} - -static JSBool -SetDefaultXMLSettings(JSContext *cx, JSObject *obj) -{ - int i; - jsval v; - - for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) { - v = JSVAL_TRUE; - if (!JS_SetProperty(cx, obj, xml_static_props[i].name, &v)) - return JS_FALSE; - } - v = INT_TO_JSVAL(2); - return JS_SetProperty(cx, obj, xml_static_props[i].name, &v); -} - -static JSBool -xml_settings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - JSObject *settings; - - settings = JS_NewObject(cx, NULL, NULL, NULL); - if (!settings) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(settings); - return CopyXMLSettings(cx, obj, settings); -} - -static JSBool -xml_setSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - jsval v; - JSBool ok; - JSObject *settings; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) { - cx->xmlSettingFlags = 0; - ok = SetDefaultXMLSettings(cx, obj); - } else { - if (JSVAL_IS_PRIMITIVE(v)) - return JS_TRUE; - settings = JSVAL_TO_OBJECT(v); - cx->xmlSettingFlags = 0; - ok = CopyXMLSettings(cx, settings, obj); - } - if (ok) - cx->xmlSettingFlags |= XSF_CACHE_VALID; - return ok; -} - -static JSBool -xml_defaultSettings(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - JSObject *settings; - - settings = JS_NewObject(cx, NULL, NULL, NULL); - if (!settings) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(settings); - return SetDefaultXMLSettings(cx, settings); -} - -static JSFunctionSpec xml_static_methods[] = { - {"settings", xml_settings, 0,0,0}, - {"setSettings", xml_setSettings, 1,0,0}, - {"defaultSettings", xml_defaultSettings, 0,0,0}, - {0,0,0,0,0} -}; - -static JSBool -XML(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSXML *xml, *copy; - JSObject *xobj, *vobj; - JSClass *clasp; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - v = STRING_TO_JSVAL(cx->runtime->emptyString); - - xobj = ToXML(cx, v); - if (!xobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(xobj); - xml = (JSXML *) JS_GetPrivate(cx, xobj); - - if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - clasp = OBJ_GET_CLASS(cx, vobj); - if (clasp == &js_XMLClass || - (clasp->flags & JSCLASS_DOCUMENT_OBSERVER)) { - /* No need to lock obj, it's newly constructed and thread local. */ - copy = DeepCopy(cx, xml, obj, 0); - if (!copy) - return JS_FALSE; - JS_ASSERT(copy->object == obj); - *rval = OBJECT_TO_JSVAL(obj); - return JS_TRUE; - } - } - return JS_TRUE; -} - -static JSBool -XMLList(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) -{ - jsval v; - JSObject *vobj, *listobj; - JSXML *xml, *list; - - v = argv[0]; - if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) - v = STRING_TO_JSVAL(cx->runtime->emptyString); - - if ((cx->fp->flags & JSFRAME_CONSTRUCTING) && !JSVAL_IS_PRIMITIVE(v)) { - vobj = JSVAL_TO_OBJECT(v); - if (OBJECT_IS_XML(cx, vobj)) { - xml = (JSXML *) JS_GetPrivate(cx, vobj); - if (xml->xml_class == JSXML_CLASS_LIST) { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - return JS_FALSE; - *rval = OBJECT_TO_JSVAL(listobj); - - list = (JSXML *) JS_GetPrivate(cx, listobj); - if (!Append(cx, list, xml)) - return JS_FALSE; - return JS_TRUE; - } - } - } - - /* Toggle on XML support since the script has explicitly requested it. */ - listobj = ToXMLList(cx, v); - if (!listobj) - return JS_FALSE; - - *rval = OBJECT_TO_JSVAL(listobj); - return JS_TRUE; -} - -#define JSXML_LIST_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLListVar)) -#define JSXML_ELEMENT_SIZE (offsetof(JSXML, u) + sizeof(struct JSXMLVar)) -#define JSXML_LEAF_SIZE (offsetof(JSXML, u) + sizeof(JSString *)) - -static size_t sizeof_JSXML[JSXML_CLASS_LIMIT] = { - JSXML_LIST_SIZE, /* JSXML_CLASS_LIST */ - JSXML_ELEMENT_SIZE, /* JSXML_CLASS_ELEMENT */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_ATTRIBUTE */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_PROCESSING_INSTRUCTION */ - JSXML_LEAF_SIZE, /* JSXML_CLASS_TEXT */ - JSXML_LEAF_SIZE /* JSXML_CLASS_COMMENT */ -}; - -#ifdef DEBUG_notme -JSCList xml_leaks = JS_INIT_STATIC_CLIST(&xml_leaks); -uint32 xml_serial; -#endif - -JSXML * -js_NewXML(JSContext *cx, JSXMLClass xml_class) -{ - JSXML *xml; - - xml = (JSXML *) js_NewGCThing(cx, GCX_XML, sizeof_JSXML[xml_class]); - if (!xml) - return NULL; - - xml->object = NULL; - xml->domnode = NULL; - xml->parent = NULL; - xml->name = NULL; - xml->xml_class = xml_class; - xml->xml_flags = 0; - if (JSXML_CLASS_HAS_VALUE(xml_class)) { - xml->xml_value = cx->runtime->emptyString; - } else { - XMLArrayInit(cx, &xml->xml_kids, 0); - if (xml_class == JSXML_CLASS_LIST) { - xml->xml_target = NULL; - xml->xml_targetprop = NULL; - } else { - XMLArrayInit(cx, &xml->xml_namespaces, 0); - XMLArrayInit(cx, &xml->xml_attrs, 0); - } - } - -#ifdef DEBUG_notme - JS_APPEND_LINK(&xml->links, &xml_leaks); - xml->serial = xml_serial++; -#endif - METER(xml_stats.xml); - METER(xml_stats.livexml); - return xml; -} - -void -js_MarkXML(JSContext *cx, JSXML *xml) -{ - GC_MARK(cx, xml->object, "object"); - GC_MARK(cx, xml->name, "name"); - GC_MARK(cx, xml->parent, "xml_parent"); - - if (JSXML_HAS_VALUE(xml)) { - GC_MARK(cx, xml->xml_value, "value"); - return; - } - - xml_mark_vector(cx, - (JSXML **) xml->xml_kids.vector, - xml->xml_kids.length); - XMLArrayCursorMark(cx, xml->xml_kids.cursors); - XMLArrayTrim(&xml->xml_kids); - - if (xml->xml_class == JSXML_CLASS_LIST) { - if (xml->xml_target) - GC_MARK(cx, xml->xml_target, "target"); - if (xml->xml_targetprop) - GC_MARK(cx, xml->xml_targetprop, "targetprop"); - } else { - namespace_mark_vector(cx, - (JSXMLNamespace **) xml->xml_namespaces.vector, - xml->xml_namespaces.length); - XMLArrayCursorMark(cx, xml->xml_namespaces.cursors); - XMLArrayTrim(&xml->xml_namespaces); - - xml_mark_vector(cx, - (JSXML **) xml->xml_attrs.vector, - xml->xml_attrs.length); - XMLArrayCursorMark(cx, xml->xml_attrs.cursors); - XMLArrayTrim(&xml->xml_attrs); - } -} - -void -js_FinalizeXML(JSContext *cx, JSXML *xml) -{ - if (JSXML_HAS_KIDS(xml)) { - XMLArrayFinish(cx, &xml->xml_kids); - if (xml->xml_class == JSXML_CLASS_ELEMENT) { - XMLArrayFinish(cx, &xml->xml_namespaces); - XMLArrayFinish(cx, &xml->xml_attrs); - } - } - -#ifdef DEBUG_notme - JS_REMOVE_LINK(&xml->links); -#endif - - UNMETER(xml_stats.livexml); -} - -JSObject * -js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn) -{ - jsval nsval; - JSXMLNamespace *ns; - JSXMLArray nsarray; - JSXML *xml; - - if (!js_GetDefaultXMLNamespace(cx, &nsval)) - return NULL; - JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); - ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(nsval)); - - if (!XMLArrayInit(cx, &nsarray, 1)) - return NULL; - - XMLARRAY_APPEND(cx, &nsarray, ns); - xml = ParseNodeToXML(cx, pn, &nsarray, XSF_PRECOMPILED_ROOT); - XMLArrayFinish(cx, &nsarray); - if (!xml) - return NULL; - - return xml->object; -} - -JSObject * -js_NewXMLObject(JSContext *cx, JSXMLClass xml_class) -{ - JSXML *xml; - JSObject *obj; - JSTempValueRooter tvr; - - xml = js_NewXML(cx, xml_class); - if (!xml) - return NULL; - JS_PUSH_TEMP_ROOT_GCTHING(cx, xml, &tvr); - obj = js_GetXMLObject(cx, xml); - JS_POP_TEMP_ROOT(cx, &tvr); - return obj; -} - -static JSObject * -NewXMLObject(JSContext *cx, JSXML *xml) -{ - JSObject *obj; - - obj = js_NewObject(cx, &js_XMLClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, xml)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - return NULL; - } - METER(xml_stats.xmlobj); - METER(xml_stats.livexmlobj); - return obj; -} - -JSObject * -js_GetXMLObject(JSContext *cx, JSXML *xml) -{ - JSObject *obj; - - obj = xml->object; - if (obj) { - JS_ASSERT(JS_GetPrivate(cx, obj) == xml); - return obj; - } - - /* - * A JSXML cannot be shared among threads unless it has an object. - * A JSXML cannot be given an object unless: - * (a) it has no parent; or - * (b) its parent has no object (therefore is thread-private); or - * (c) its parent's object is locked. - * - * Once given an object, a JSXML is immutable. - */ - JS_ASSERT(!xml->parent || - !xml->parent->object || - JS_IS_OBJ_LOCKED(cx, xml->parent->object)); - - obj = NewXMLObject(cx, xml); - if (!obj) - return NULL; - xml->object = obj; - return obj; -} - -JSObject * -js_InitNamespaceClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_NamespaceClass.base, Namespace, 2, - namespace_props, namespace_methods, NULL, NULL); -} - -JSObject * -js_InitQNameClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_QNameClass.base, QName, 2, - qname_props, qname_methods, NULL, NULL); -} - -JSObject * -js_InitAttributeNameClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2, - qname_props, qname_methods, NULL, NULL); -} - -JSObject * -js_InitAnyNameClass(JSContext *cx, JSObject *obj) -{ - jsval v; - - if (!js_GetAnyName(cx, &v)) - return NULL; - return JSVAL_TO_OBJECT(v); -} - -JSObject * -js_InitXMLClass(JSContext *cx, JSObject *obj) -{ - JSObject *proto, *pobj, *ctor; - JSFunction *fun; - JSXML *xml; - JSProperty *prop; - JSScopeProperty *sprop; - jsval cval, argv[1], junk; - - /* Define the isXMLName function. */ - if (!JS_DefineFunction(cx, obj, js_isXMLName_str, xml_isXMLName, 1, 0)) - return NULL; - - /* Define the XML class constructor and prototype. */ - proto = JS_InitClass(cx, obj, NULL, &js_XMLClass, XML, 1, - NULL, xml_methods, - xml_static_props, xml_static_methods); - if (!proto) - return NULL; - - xml = js_NewXML(cx, JSXML_CLASS_TEXT); - if (!xml || !JS_SetPrivate(cx, proto, xml)) - return NULL; - xml->object = proto; - METER(xml_stats.xmlobj); - METER(xml_stats.livexmlobj); - - /* - * Prepare to set default settings on the XML constructor we just made. - * NB: We can't use JS_GetConstructor, because it calls OBJ_GET_PROPERTY, - * which is xml_getProperty, which creates a new XMLList every time! We - * must instead call js_LookupProperty directly. - */ - if (!js_LookupProperty(cx, proto, - ATOM_TO_JSID(cx->runtime->atomState.constructorAtom), - &pobj, &prop)) { - return NULL; - } - JS_ASSERT(prop); - sprop = (JSScopeProperty *) prop; - JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))); - cval = OBJ_GET_SLOT(cx, pobj, sprop->slot); - OBJ_DROP_PROPERTY(cx, pobj, prop); - JS_ASSERT(VALUE_IS_FUNCTION(cx, cval)); - - /* Set default settings. */ - ctor = JSVAL_TO_OBJECT(cval); - argv[0] = JSVAL_VOID; - if (!xml_setSettings(cx, ctor, 1, argv, &junk)) - return NULL; - - /* Define the XMLList function and give it the same prototype as XML. */ - fun = JS_DefineFunction(cx, obj, js_XMLList_str, XMLList, 1, 0); - if (!fun) - return NULL; - if (!js_SetClassPrototype(cx, fun->object, proto, - JSPROP_READONLY | JSPROP_PERMANENT)) { - return NULL; - } - return proto; -} - -JSObject * -js_InitXMLClasses(JSContext *cx, JSObject *obj) -{ - if (!js_InitNamespaceClass(cx, obj)) - return NULL; - if (!js_InitQNameClass(cx, obj)) - return NULL; - if (!js_InitAttributeNameClass(cx, obj)) - return NULL; - if (!js_InitAnyNameClass(cx, obj)) - return NULL; - return js_InitXMLClass(cx, obj); -} - -JSBool -js_GetFunctionNamespace(JSContext *cx, jsval *vp) -{ - JSRuntime *rt; - JSObject *obj; - JSAtom *atom; - JSString *prefix, *uri; - - /* An invalid URI, for internal use only, guaranteed not to collide. */ - static const char anti_uri[] = "@mozilla.org/js/function"; - - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ - rt = cx->runtime; - obj = rt->functionNamespaceObject; - if (!obj) { - JS_LOCK_GC(rt); - obj = rt->functionNamespaceObject; - if (!obj) { - JS_UNLOCK_GC(rt); - atom = js_Atomize(cx, js_function_str, 8, 0); - JS_ASSERT(atom); - prefix = ATOM_TO_STRING(atom); - - /* - * Note that any race to atomize anti_uri here is resolved by - * the atom table code, such that at most one atom for anti_uri - * is created. We store in rt->atomState.lazy unconditionally, - * since we are guaranteed to overwrite either null or the same - * atom pointer. - */ - atom = js_Atomize(cx, anti_uri, sizeof anti_uri - 1, ATOM_PINNED); - if (!atom) - return JS_FALSE; - rt->atomState.lazy.functionNamespaceURIAtom = atom; - - uri = ATOM_TO_STRING(atom); - obj = js_NewXMLNamespaceObject(cx, prefix, uri, JS_FALSE); - if (!obj) - return JS_FALSE; - - /* - * Avoid entraining any in-scope Object.prototype. The loss of - * Namespace.prototype is not detectable, as there is no way to - * refer to this instance in scripts. When used to qualify method - * names, its prefix and uri references are copied to the QName. - */ - OBJ_SET_PROTO(cx, obj, NULL); - OBJ_SET_PARENT(cx, obj, NULL); - - JS_LOCK_GC(rt); - if (!rt->functionNamespaceObject) - rt->functionNamespaceObject = obj; - else - obj = rt->functionNamespaceObject; - } - JS_UNLOCK_GC(rt); - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -/* - * Note the asymmetry between js_GetDefaultXMLNamespace and js_SetDefaultXML- - * Namespace. Get searches fp->scopeChain for JS_DEFAULT_XML_NAMESPACE_ID, - * while Set sets JS_DEFAULT_XML_NAMESPACE_ID in fp->varobj (unless fp is a - * lightweight function activation). There's no requirement that fp->varobj - * lie directly on fp->scopeChain, although it should be reachable using the - * prototype chain from a scope object (cf. JSOPTION_VAROBJFIX in jsapi.h). - * - * If Get can't find JS_DEFAULT_XML_NAMESPACE_ID along the scope chain, it - * creates a default namespace via 'new Namespace()'. In contrast, Set uses - * its v argument as the uri of a new Namespace, with "" as the prefix. See - * ECMA-357 12.1 and 12.1.1. Note that if Set is called with a Namespace n, - * the default XML namespace will be set to ("", n.uri). So the uri string - * is really the only usefully stored value of the default namespace. - */ -JSBool -js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp) -{ - JSStackFrame *fp; - JSObject *nsobj, *obj, *tmp; - jsval v; - - fp = cx->fp; - nsobj = fp->xmlNamespace; - if (nsobj) { - *vp = OBJECT_TO_JSVAL(nsobj); - return JS_TRUE; - } - - obj = NULL; - for (tmp = fp->scopeChain; tmp; tmp = OBJ_GET_PARENT(cx, obj)) { - obj = tmp; - if (!OBJ_GET_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, &v)) - return JS_FALSE; - if (!JSVAL_IS_PRIMITIVE(v)) { - fp->xmlNamespace = JSVAL_TO_OBJECT(v); - *vp = v; - return JS_TRUE; - } - } - - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL); - if (!nsobj) - return JS_FALSE; - v = OBJECT_TO_JSVAL(nsobj); - if (obj && - !OBJ_DEFINE_PROPERTY(cx, obj, JS_DEFAULT_XML_NAMESPACE_ID, v, - JS_PropertyStub, JS_PropertyStub, - JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - fp->xmlNamespace = nsobj; - *vp = v; - return JS_TRUE; -} - -JSBool -js_SetDefaultXMLNamespace(JSContext *cx, jsval v) -{ - jsval argv[2]; - JSObject *nsobj, *varobj; - JSStackFrame *fp; - - argv[0] = STRING_TO_JSVAL(cx->runtime->emptyString); - argv[1] = v; - nsobj = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, - 2, argv); - if (!nsobj) - return JS_FALSE; - v = OBJECT_TO_JSVAL(nsobj); - - fp = cx->fp; - varobj = fp->varobj; - if (varobj) { - if (!OBJ_DEFINE_PROPERTY(cx, varobj, JS_DEFAULT_XML_NAMESPACE_ID, v, - JS_PropertyStub, JS_PropertyStub, - JSPROP_PERMANENT, NULL)) { - return JS_FALSE; - } - } else { - JS_ASSERT(fp->fun && !JSFUN_HEAVYWEIGHT_TEST(fp->fun->flags)); - } - fp->xmlNamespace = JSVAL_TO_OBJECT(v); - return JS_TRUE; -} - -JSBool -js_ToAttributeName(JSContext *cx, jsval *vp) -{ - JSXMLQName *qn; - - qn = ToAttributeName(cx, *vp); - if (!qn) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(qn->object); - return JS_TRUE; -} - -JSString * -js_EscapeAttributeValue(JSContext *cx, JSString *str) -{ - return EscapeAttributeValue(cx, NULL, str); -} - -JSString * -js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, JSString *str2) -{ - size_t len, len2, newlen; - jschar *chars; - - if (JSSTRING_IS_DEPENDENT(str) || - !(*js_GetGCThingFlags(str) & GCF_MUTABLE)) { - str = js_NewStringCopyN(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), - 0); - if (!str) - return NULL; - } - - len = str->length; - len2 = JSSTRING_LENGTH(str2); - newlen = (isName) ? len + 1 + len2 : len + 2 + len2 + 1; - chars = (jschar *) JS_realloc(cx, str->chars, (newlen+1) * sizeof(jschar)); - if (!chars) - return NULL; - - /* - * Reallocating str (because we know it has no other references) requires - * purging any deflated string cached for it. - */ - js_PurgeDeflatedStringCache(cx->runtime, str); - - str->chars = chars; - str->length = newlen; - chars += len; - if (isName) { - *chars++ = ' '; - js_strncpy(chars, JSSTRING_CHARS(str2), len2); - chars += len2; - } else { - *chars++ = '='; - *chars++ = '"'; - js_strncpy(chars, JSSTRING_CHARS(str2), len2); - chars += len2; - *chars++ = '"'; - } - *chars = 0; - return str; -} - -JSString * -js_EscapeElementValue(JSContext *cx, JSString *str) -{ - return EscapeElementValue(cx, NULL, str); -} - -JSString * -js_ValueToXMLString(JSContext *cx, jsval v) -{ - return ToXMLString(cx, v); -} - -static JSBool -anyname_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) -{ - *rval = ATOM_KEY(cx->runtime->atomState.starAtom); - return JS_TRUE; -} - -JSBool -js_GetAnyName(JSContext *cx, jsval *vp) -{ - JSRuntime *rt; - JSObject *obj; - JSXMLQName *qn; - JSBool ok; - - /* Optimize by avoiding JS_LOCK_GC(rt) for the common case. */ - rt = cx->runtime; - obj = rt->anynameObject; - if (!obj) { - JS_LOCK_GC(rt); - obj = rt->anynameObject; - if (!obj) { - JS_UNLOCK_GC(rt); - - /* - * Protect multiple newborns created below, in the do-while(0) - * loop used to ensure that we leave this local root scope. - */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - do { - qn = js_NewXMLQName(cx, rt->emptyString, rt->emptyString, - ATOM_TO_STRING(rt->atomState.starAtom)); - if (!qn) { - ok = JS_FALSE; - break; - } - - obj = js_NewObject(cx, &js_AnyNameClass, NULL, NULL); - if (!obj || !JS_SetPrivate(cx, obj, qn)) { - cx->weakRoots.newborn[GCX_OBJECT] = NULL; - ok = JS_FALSE; - break; - } - qn->object = obj; - METER(xml_stats.qnameobj); - METER(xml_stats.liveqnameobj); - - /* - * Avoid entraining any Object.prototype found via cx's scope - * chain or global object. This loses the default toString, - * but no big deal: we want to customize toString anyway for - * clearer diagnostics. - */ - if (!JS_DefineFunction(cx, obj, js_toString_str, - anyname_toString, 0, 0)) { - ok = JS_FALSE; - break; - } - OBJ_SET_PROTO(cx, obj, NULL); - JS_ASSERT(!OBJ_GET_PARENT(cx, obj)); - } while (0); - - js_LeaveLocalRootScopeWithResult(cx, OBJECT_TO_JSVAL(obj)); - if (!ok) - return JS_FALSE; - - JS_LOCK_GC(rt); - if (!rt->anynameObject) - rt->anynameObject = obj; - else - obj = rt->anynameObject; - } - JS_UNLOCK_GC(rt); - } - *vp = OBJECT_TO_JSVAL(obj); - return JS_TRUE; -} - -JSBool -js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep) -{ - JSXMLQName *qn; - jsid funid, id; - JSObject *obj, *pobj, *lastobj; - JSProperty *prop; - const char *printable; - - qn = ToXMLName(cx, name, &funid); - if (!qn) - return JS_FALSE; - id = OBJECT_TO_JSID(qn->object); - - obj = cx->fp->scopeChain; - do { - if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) - return JS_FALSE; - if (prop) { - OBJ_DROP_PROPERTY(cx, pobj, prop); - - /* - * Call OBJ_THIS_OBJECT to skip any With object that wraps an XML - * object to carry scope chain linkage in js_FilterXMLList. - */ - pobj = OBJ_THIS_OBJECT(cx, obj); - if (OBJECT_IS_XML(cx, pobj)) { - *objp = pobj; - *namep = ID_TO_VALUE(id); - return JS_TRUE; - } - } - - lastobj = obj; - } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL); - - printable = js_ValueToPrintableString(cx, name); - if (printable) { - JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, - js_GetErrorMessage, NULL, - JSMSG_UNDEFINED_XML_NAME, printable); - } - return JS_FALSE; -} - -JSBool -js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) -{ - return GetProperty(cx, obj, name, vp); -} - -JSBool -js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp) -{ - JSObject *target; - JSXML *xml; - JSTempValueRooter tvr; - JSBool ok; - - JS_ASSERT(OBJECT_IS_XML(cx, obj)); - - /* After this point, control must flow through label out: to exit. */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, NULL, &tvr); - - /* - * See comments before xml_lookupProperty about the need for the proto - * chain lookup. - */ - target = obj; - for (;;) { - ok = js_GetProperty(cx, target, id, vp); - if (!ok) - goto out; - if (VALUE_IS_FUNCTION(cx, *vp)) { - ok = JS_TRUE; - goto out; - } - target = OBJ_GET_PROTO(cx, target); - if (target == NULL) - break; - tvr.u.object = target; - } - - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (HasSimpleContent(xml)) { - /* Search in String.prototype to implement 11.2.2.1 Step 3(f). */ - ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(JSProto_String), - &tvr.u.object); - if (!ok) - goto out; - JS_ASSERT(tvr.u.object); - ok = OBJ_GET_PROPERTY(cx, tvr.u.object, id, vp); - } - - out: - JS_POP_TEMP_ROOT(cx, &tvr); - return ok; -} - -JSBool -js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp) -{ - return PutProperty(cx, obj, name, vp); -} - -static JSXML * -GetPrivate(JSContext *cx, JSObject *obj, const char *method) -{ - JSXML *xml; - - xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); - if (!xml) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_INCOMPATIBLE_METHOD, - js_XML_str, method, OBJ_GET_CLASS(cx, obj)->name); - } - return xml; -} - -JSBool -js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - JSXML *xml, *list; - - xml = GetPrivate(cx, obj, "descendants internal method"); - if (!xml) - return JS_FALSE; - - list = Descendants(cx, xml, id); - if (!list) - return JS_FALSE; - *vp = OBJECT_TO_JSVAL(list->object); - return JS_TRUE; -} - -JSBool -js_DeleteXMLListElements(JSContext *cx, JSObject *listobj) -{ - JSXML *list; - uint32 n; - jsval junk; - - list = (JSXML *) JS_GetPrivate(cx, listobj); - for (n = list->xml_kids.length; n != 0; --n) { - if (!DeleteProperty(cx, listobj, INT_TO_JSID(0), &junk)) - return JS_FALSE; - } - return JS_TRUE; -} - -JSBool -js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp) -{ - JSBool ok, match; - JSStackFrame *fp; - uint32 flags; - JSObject *scobj, *listobj, *resobj, *withobj, *kidobj; - JSXML *xml, *list, *result, *kid; - JSXMLArrayCursor cursor; - - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - /* All control flow after this point must exit via label out or bad. */ - *vp = JSVAL_NULL; - fp = cx->fp; - flags = fp->flags; - fp->flags = flags | JSFRAME_FILTERING; - scobj = js_GetScopeChain(cx, fp); - withobj = NULL; - if (!scobj) - goto bad; - xml = GetPrivate(cx, obj, "filtering predicate operator"); - if (!xml) - goto bad; - - if (xml->xml_class == JSXML_CLASS_LIST) { - list = xml; - } else { - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) - goto bad; - list = (JSXML *) JS_GetPrivate(cx, listobj); - ok = Append(cx, list, xml); - if (!ok) - goto out; - } - - resobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!resobj) - goto bad; - result = (JSXML *) JS_GetPrivate(cx, resobj); - - /* Hoist the scope chain update out of the loop over kids. */ - withobj = js_NewWithObject(cx, NULL, scobj, -1); - if (!withobj) - goto bad; - fp->scopeChain = withobj; - - XMLArrayCursorInit(&cursor, &list->xml_kids); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) - break; - OBJ_SET_PROTO(cx, withobj, kidobj); - ok = js_Interpret(cx, pc, vp) && js_ValueToBoolean(cx, *vp, &match); - if (ok && match) - ok = Append(cx, result, kid); - if (!ok) - break; - } - XMLArrayCursorFinish(&cursor); - if (!ok) - goto out; - if (kid) - goto bad; - - *vp = OBJECT_TO_JSVAL(resobj); - -out: - fp->flags = flags | (fp->flags & JSFRAME_POP_BLOCKS); - if (withobj) { - fp->scopeChain = scobj; - JS_SetPrivate(cx, withobj, NULL); - } - js_LeaveLocalRootScopeWithResult(cx, *vp); - return ok; -bad: - ok = JS_FALSE; - goto out; -} - -JSObject * -js_ValueToXMLObject(JSContext *cx, jsval v) -{ - return ToXML(cx, v); -} - -JSObject * -js_ValueToXMLListObject(JSContext *cx, jsval v) -{ - return ToXMLList(cx, v); -} - -JSObject * -js_CloneXMLObject(JSContext *cx, JSObject *obj) -{ - uintN flags; - JSXML *xml; - - if (!GetXMLSettingFlags(cx, &flags)) - return NULL; - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (flags & (XSF_IGNORE_COMMENTS | - XSF_IGNORE_PROCESSING_INSTRUCTIONS | - XSF_IGNORE_WHITESPACE)) { - xml = DeepCopy(cx, xml, NULL, flags); - if (!xml) - return NULL; - return xml->object; - } - return NewXMLObject(cx, xml); -} - -JSObject * -js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, - JSString *value) -{ - uintN flags; - JSObject *obj; - JSXML *xml; - JSXMLQName *qn; - - if (!GetXMLSettingFlags(cx, &flags)) - return NULL; - - if ((xml_class == JSXML_CLASS_COMMENT && - (flags & XSF_IGNORE_COMMENTS)) || - (xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION && - (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS))) { - return js_NewXMLObject(cx, JSXML_CLASS_TEXT); - } - - obj = js_NewXMLObject(cx, xml_class); - if (!obj) - return NULL; - xml = (JSXML *) JS_GetPrivate(cx, obj); - if (name) { - qn = js_NewXMLQName(cx, cx->runtime->emptyString, NULL, name); - if (!qn) - return NULL; - xml->name = qn; - } - xml->xml_value = value; - return obj; -} - -JSString * -js_MakeXMLCDATAString(JSContext *cx, JSString *str) -{ - return MakeXMLCDATAString(cx, NULL, str); -} - -JSString * -js_MakeXMLCommentString(JSContext *cx, JSString *str) -{ - return MakeXMLCommentString(cx, NULL, str); -} - -JSString * -js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str) -{ - return MakeXMLPIString(cx, NULL, name, str); -} - -#endif /* JS_HAS_XML_SUPPORT */ diff --git a/spidermonkey/libjs/jsxml.h b/spidermonkey/libjs/jsxml.h deleted file mode 100644 index 71e591a..0000000 --- a/spidermonkey/libjs/jsxml.h +++ /dev/null @@ -1,332 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is SpiderMonkey E4X code, released August, 2004. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef jsxml_h___ -#define jsxml_h___ - -#include "jsstddef.h" -#include "jspubtd.h" - -extern const char js_AnyName_str[]; -extern const char js_AttributeName_str[]; -extern const char js_isXMLName_str[]; -extern const char js_XMLList_str[]; - -extern const char js_amp_entity_str[]; -extern const char js_gt_entity_str[]; -extern const char js_lt_entity_str[]; -extern const char js_quot_entity_str[]; - -struct JSXMLNamespace { - JSObject *object; - JSString *prefix; - JSString *uri; - JSBool declared; /* true if declared in its XML tag */ -}; - -extern JSXMLNamespace * -js_NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared); - -extern void -js_MarkXMLNamespace(JSContext *cx, JSXMLNamespace *ns); - -extern void -js_FinalizeXMLNamespace(JSContext *cx, JSXMLNamespace *ns); - -extern JSObject * -js_NewXMLNamespaceObject(JSContext *cx, JSString *prefix, JSString *uri, - JSBool declared); - -extern JSObject * -js_GetXMLNamespaceObject(JSContext *cx, JSXMLNamespace *ns); - -struct JSXMLQName { - JSObject *object; - JSString *uri; - JSString *prefix; - JSString *localName; -}; - -extern JSXMLQName * -js_NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName); - -extern void -js_MarkXMLQName(JSContext *cx, JSXMLQName *qn); - -extern void -js_FinalizeXMLQName(JSContext *cx, JSXMLQName *qn); - -extern JSObject * -js_NewXMLQNameObject(JSContext *cx, JSString *uri, JSString *prefix, - JSString *localName); - -extern JSObject * -js_GetXMLQNameObject(JSContext *cx, JSXMLQName *qn); - -extern JSObject * -js_GetAttributeNameObject(JSContext *cx, JSXMLQName *qn); - -extern JSObject * -js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval); - -typedef JSBool -(* JS_DLL_CALLBACK JSIdentityOp)(const void *a, const void *b); - -struct JSXMLArray { - uint32 length; - uint32 capacity; - void **vector; - JSXMLArrayCursor *cursors; -}; - -#define JSXML_PRESET_CAPACITY JS_BIT(31) -#define JSXML_CAPACITY_MASK JS_BITMASK(31) -#define JSXML_CAPACITY(array) ((array)->capacity & JSXML_CAPACITY_MASK) - -struct JSXMLArrayCursor { - JSXMLArray *array; - uint32 index; - JSXMLArrayCursor *next; - JSXMLArrayCursor **prevp; - void *root; -}; - -/* - * NB: don't reorder this enum without changing all array initializers that - * depend on it in jsxml.c. - */ -typedef enum JSXMLClass { - JSXML_CLASS_LIST, - JSXML_CLASS_ELEMENT, - JSXML_CLASS_ATTRIBUTE, - JSXML_CLASS_PROCESSING_INSTRUCTION, - JSXML_CLASS_TEXT, - JSXML_CLASS_COMMENT, - JSXML_CLASS_LIMIT -} JSXMLClass; - -#define JSXML_CLASS_HAS_KIDS(class_) ((class_) < JSXML_CLASS_ATTRIBUTE) -#define JSXML_CLASS_HAS_VALUE(class_) ((class_) >= JSXML_CLASS_ATTRIBUTE) -#define JSXML_CLASS_HAS_NAME(class_) \ - ((uintN)((class_) - JSXML_CLASS_ELEMENT) <= \ - (uintN)(JSXML_CLASS_PROCESSING_INSTRUCTION - JSXML_CLASS_ELEMENT)) - -#ifdef DEBUG_notme -#include "jsclist.h" -#endif - -struct JSXML { -#ifdef DEBUG_notme - JSCList links; - uint32 serial; -#endif - JSObject *object; - void *domnode; /* DOM node if mapped info item */ - JSXML *parent; - JSXMLQName *name; - uint16 xml_class; /* discriminates u, below */ - uint16 xml_flags; /* flags, see below */ - union { - struct JSXMLListVar { - JSXMLArray kids; /* NB: must come first */ - JSXML *target; - JSXMLQName *targetprop; - } list; - struct JSXMLVar { - JSXMLArray kids; /* NB: must come first */ - JSXMLArray namespaces; - JSXMLArray attrs; - } elem; - JSString *value; - } u; - - /* Don't add anything after u -- see js_NewXML for why. */ -}; - -/* union member shorthands */ -#define xml_kids u.list.kids -#define xml_target u.list.target -#define xml_targetprop u.list.targetprop -#define xml_namespaces u.elem.namespaces -#define xml_attrs u.elem.attrs -#define xml_value u.value - -/* xml_flags values */ -#define XMLF_WHITESPACE_TEXT 0x1 - -/* xml_class-testing macros */ -#define JSXML_HAS_KIDS(xml) JSXML_CLASS_HAS_KIDS((xml)->xml_class) -#define JSXML_HAS_VALUE(xml) JSXML_CLASS_HAS_VALUE((xml)->xml_class) -#define JSXML_HAS_NAME(xml) JSXML_CLASS_HAS_NAME((xml)->xml_class) -#define JSXML_LENGTH(xml) (JSXML_CLASS_HAS_KIDS((xml)->xml_class) \ - ? (xml)->xml_kids.length \ - : 0) - -extern JSXML * -js_NewXML(JSContext *cx, JSXMLClass xml_class); - -extern void -js_MarkXML(JSContext *cx, JSXML *xml); - -extern void -js_FinalizeXML(JSContext *cx, JSXML *xml); - -extern JSObject * -js_ParseNodeToXMLObject(JSContext *cx, JSParseNode *pn); - -extern JSObject * -js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); - -extern JSObject * -js_GetXMLObject(JSContext *cx, JSXML *xml); - -extern JS_FRIEND_DATA(JSXMLObjectOps) js_XMLObjectOps; -extern JS_FRIEND_DATA(JSClass) js_XMLClass; -extern JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass; -extern JS_FRIEND_DATA(JSExtendedClass) js_QNameClass; -extern JS_FRIEND_DATA(JSClass) js_AttributeNameClass; -extern JS_FRIEND_DATA(JSClass) js_AnyNameClass; - -/* - * Macros to test whether an object or a value is of type "xml" (per typeof). - * NB: jsapi.h must be included before any call to VALUE_IS_XML. - */ -#define OBJECT_IS_XML(cx,obj) ((obj)->map->ops == &js_XMLObjectOps.base) -#define VALUE_IS_XML(cx,v) (!JSVAL_IS_PRIMITIVE(v) && \ - OBJECT_IS_XML(cx, JSVAL_TO_OBJECT(v))) - -extern JSObject * -js_InitNamespaceClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitQNameClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitAttributeNameClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitAnyNameClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitXMLClass(JSContext *cx, JSObject *obj); - -extern JSObject * -js_InitXMLClasses(JSContext *cx, JSObject *obj); - -extern JSBool -js_GetFunctionNamespace(JSContext *cx, jsval *vp); - -extern JSBool -js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); - -extern JSBool -js_SetDefaultXMLNamespace(JSContext *cx, jsval v); - -/* - * Return true if v is a XML QName object, or if it converts to a string that - * contains a valid XML qualified name (one containing no :), false otherwise. - * NB: This function is an infallible predicate, it hides exceptions. - */ -extern JSBool -js_IsXMLName(JSContext *cx, jsval v); - -extern JSBool -js_ToAttributeName(JSContext *cx, jsval *vp); - -extern JSString * -js_EscapeAttributeValue(JSContext *cx, JSString *str); - -extern JSString * -js_AddAttributePart(JSContext *cx, JSBool isName, JSString *str, - JSString *str2); - -extern JSString * -js_EscapeElementValue(JSContext *cx, JSString *str); - -extern JSString * -js_ValueToXMLString(JSContext *cx, jsval v); - -extern JSBool -js_GetAnyName(JSContext *cx, jsval *vp); - -extern JSBool -js_FindXMLProperty(JSContext *cx, jsval name, JSObject **objp, jsval *namep); - -extern JSBool -js_GetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); - -extern JSBool -js_GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp); - -extern JSBool -js_SetXMLProperty(JSContext *cx, JSObject *obj, jsval name, jsval *vp); - -extern JSBool -js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp); - -extern JSBool -js_DeleteXMLListElements(JSContext *cx, JSObject *listobj); - -extern JSBool -js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp); - -extern JSObject * -js_ValueToXMLObject(JSContext *cx, jsval v); - -extern JSObject * -js_ValueToXMLListObject(JSContext *cx, jsval v); - -extern JSObject * -js_CloneXMLObject(JSContext *cx, JSObject *obj); - -extern JSObject * -js_NewXMLSpecialObject(JSContext *cx, JSXMLClass xml_class, JSString *name, - JSString *value); - -extern JSString * -js_MakeXMLCDATAString(JSContext *cx, JSString *str); - -extern JSString * -js_MakeXMLCommentString(JSContext *cx, JSString *str); - -extern JSString * -js_MakeXMLPIString(JSContext *cx, JSString *name, JSString *str); - -#endif /* jsxml_h___ */ diff --git a/spidermonkey/libjs/prmjtime.c b/spidermonkey/libjs/prmjtime.c deleted file mode 100644 index 6e08423..0000000 --- a/spidermonkey/libjs/prmjtime.c +++ /dev/null @@ -1,440 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * PR time code. - */ -#include "jsstddef.h" -#ifdef SOLARIS -#define _REENTRANT 1 -#endif -#include -#include -#include "jstypes.h" -#include "jsutil.h" - -#include "jsprf.h" -#include "prmjtime.h" - -#define PRMJ_DO_MILLISECONDS 1 - -#ifdef XP_OS2 -#include -#endif -#ifdef XP_WIN -#include -#include -#endif - -#if defined(XP_UNIX) || defined(XP_BEOS) - -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ -extern int gettimeofday(struct timeval *tv); -#endif - -#include - -#endif /* XP_UNIX */ - -#define IS_LEAP(year) \ - (year != 0 && ((((year & 0x3) == 0) && \ - ((year - ((year/100) * 100)) != 0)) || \ - (year - ((year/400) * 400)) == 0)) - -#define PRMJ_HOUR_SECONDS 3600L -#define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS) -#define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L) -#define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */ -/* function prototypes */ -static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm); -/* - * get the difference in seconds between this time zone and UTC (GMT) - */ -JSInt32 -PRMJ_LocalGMTDifference() -{ -#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) - struct tm ltime; - - /* get the difference between this time zone and GMT */ - memset((char *)<ime,0,sizeof(ltime)); - ltime.tm_mday = 2; - ltime.tm_year = 70; -#ifdef SUNOS4 - ltime.tm_zone = 0; - ltime.tm_gmtoff = 0; - return timelocal(<ime) - (24 * 3600); -#else - return mktime(<ime) - (24L * 3600L); -#endif -#endif -} - -/* Constants for GMT offset from 1970 */ -#define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */ -#define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */ - -#define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */ -#define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */ - -/* Convert from base time to extended time */ -static JSInt64 -PRMJ_ToExtendedTime(JSInt32 base_time) -{ - JSInt64 exttime; - JSInt64 g1970GMTMicroSeconds; - JSInt64 low; - JSInt32 diff; - JSInt64 tmp; - JSInt64 tmp1; - - diff = PRMJ_LocalGMTDifference(); - JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC); - JSLL_I2L(tmp1,diff); - JSLL_MUL(tmp,tmp,tmp1); - - JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI); - JSLL_UI2L(low,G1970GMTMICROLOW); -#ifndef JS_HAVE_LONG_LONG - JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); - JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16); -#else - JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32); -#endif - JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low); - - JSLL_I2L(exttime,base_time); - JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds); - JSLL_SUB(exttime,exttime,tmp); - return exttime; -} - -JSInt64 -PRMJ_Now(void) -{ -#ifdef XP_OS2 - JSInt64 s, us, ms2us, s2us; - struct timeb b; -#endif -#ifdef XP_WIN - JSInt64 s, us, - win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000), - ten = JSLL_INIT(0, 10); - FILETIME time, midnight; -#endif -#if defined(XP_UNIX) || defined(XP_BEOS) - struct timeval tv; - JSInt64 s, us, s2us; -#endif /* XP_UNIX */ - -#ifdef XP_OS2 - ftime(&b); - JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC); - JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); - JSLL_UI2L(s, b.time); - JSLL_UI2L(us, b.millitm); - JSLL_MUL(us, us, ms2us); - JSLL_MUL(s, s, s2us); - JSLL_ADD(s, s, us); - return s; -#endif -#ifdef XP_WIN - /* The windows epoch is around 1600. The unix epoch is around 1970. - win2un is the difference (in windows time units which are 10 times - more precise than the JS time unit) */ - GetSystemTimeAsFileTime(&time); - /* Win9x gets confused at midnight - http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423 - So if the low part (precision <8mins) is 0 then we get the time - again. */ - if (!time.dwLowDateTime) { - GetSystemTimeAsFileTime(&midnight); - time.dwHighDateTime = midnight.dwHighDateTime; - } - JSLL_UI2L(s, time.dwHighDateTime); - JSLL_UI2L(us, time.dwLowDateTime); - JSLL_SHL(s, s, 32); - JSLL_ADD(s, s, us); - JSLL_SUB(s, s, win2un); - JSLL_DIV(s, s, ten); - return s; -#endif - -#if defined(XP_UNIX) || defined(XP_BEOS) -#ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris */ - gettimeofday(&tv); -#else - gettimeofday(&tv, 0); -#endif /* _SVID_GETTOD */ - JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC); - JSLL_UI2L(s, tv.tv_sec); - JSLL_UI2L(us, tv.tv_usec); - JSLL_MUL(s, s, s2us); - JSLL_ADD(s, s, us); - return s; -#endif /* XP_UNIX */ -} - -/* Get the DST timezone offset for the time passed in */ -JSInt64 -PRMJ_DSTOffset(JSInt64 local_time) -{ - JSInt64 us2s; - time_t local; - JSInt32 diff; - JSInt64 maxtimet; - struct tm tm; - PRMJTime prtm; -#ifndef HAVE_LOCALTIME_R - struct tm *ptm; -#endif - - - JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC); - JSLL_DIV(local_time, local_time, us2s); - - /* get the maximum of time_t value */ - JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET); - - if(JSLL_CMP(local_time,>,maxtimet)){ - JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET); - } else if(!JSLL_GE_ZERO(local_time)){ - /*go ahead a day to make localtime work (does not work with 0) */ - JSLL_UI2L(local_time,PRMJ_DAY_SECONDS); - } - JSLL_L2UI(local,local_time); - PRMJ_basetime(local_time,&prtm); -#ifndef HAVE_LOCALTIME_R - ptm = localtime(&local); - if(!ptm){ - return JSLL_ZERO; - } - tm = *ptm; -#else - localtime_r(&local,&tm); /* get dst information */ -#endif - - diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) + - ((tm.tm_min - prtm.tm_min) * 60); - - if(diff < 0){ - diff += PRMJ_DAY_SECONDS; - } - - JSLL_UI2L(local_time,diff); - - JSLL_MUL(local_time,local_time,us2s); - - return(local_time); -} - -/* Format a time value into a buffer. Same semantics as strftime() */ -size_t -PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm) -{ -#if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) - struct tm a; - - /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int - * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets - * confused and dumps core. NSPR20 prtime.c attempts to fill these in by - * calling mktime on the partially filled struct, but this doesn't seem to - * work as well; the result string has "can't get timezone" for ECMA-valid - * years. Might still make sense to use this, but find the range of years - * for which valid tz information exists, and map (per ECMA hint) from the - * given year into that range. - - * N.B. This hasn't been tested with anything that actually _uses_ - * tm_gmtoff; zero might be the wrong thing to set it to if you really need - * to format a time. This fix is for jsdate.c, which only uses - * JS_FormatTime to get a string representing the time zone. */ - memset(&a, 0, sizeof(struct tm)); - - a.tm_sec = prtm->tm_sec; - a.tm_min = prtm->tm_min; - a.tm_hour = prtm->tm_hour; - a.tm_mday = prtm->tm_mday; - a.tm_mon = prtm->tm_mon; - a.tm_wday = prtm->tm_wday; - a.tm_year = prtm->tm_year - 1900; - a.tm_yday = prtm->tm_yday; - a.tm_isdst = prtm->tm_isdst; - - /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff - * are null. This doesn't quite work, though - the timezone is off by - * tzoff + dst. (And mktime seems to return -1 for the exact dst - * changeover time.) - - */ - -#if defined(SUNOS4) - if (mktime(&a) == -1) { - /* Seems to fail whenever the requested date is outside of the 32-bit - * UNIX epoch. We could proceed at this point (setting a.tm_zone to - * "") but then strftime returns a string with a 2-digit field of - * garbage for the year. So we return 0 and hope jsdate.c - * will fall back on toString. - */ - return 0; - } -#endif - - return strftime(buf, buflen, fmt, &a); -#endif -} - -/* table for number of days in a month */ -static int mtab[] = { - /* jan, feb,mar,apr,may,jun */ - 31,28,31,30,31,30, - /* july,aug,sep,oct,nov,dec */ - 31,31,30,31,30,31 -}; - -/* - * basic time calculation functionality for localtime and gmtime - * setups up prtm argument with correct values based upon input number - * of seconds. - */ -static void -PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm) -{ - /* convert tsecs back to year,month,day,hour,secs */ - JSInt32 year = 0; - JSInt32 month = 0; - JSInt32 yday = 0; - JSInt32 mday = 0; - JSInt32 wday = 6; /* start on a Sunday */ - JSInt32 days = 0; - JSInt32 seconds = 0; - JSInt32 minutes = 0; - JSInt32 hours = 0; - JSInt32 isleap = 0; - JSInt64 result; - JSInt64 result1; - JSInt64 result2; - JSInt64 base; - - JSLL_UI2L(result,0); - JSLL_UI2L(result1,0); - JSLL_UI2L(result2,0); - - /* get the base time via UTC */ - base = PRMJ_ToExtendedTime(0); - JSLL_UI2L(result, PRMJ_USEC_PER_SEC); - JSLL_DIV(base,base,result); - JSLL_ADD(tsecs,tsecs,base); - - JSLL_UI2L(result, PRMJ_YEAR_SECONDS); - JSLL_UI2L(result1,PRMJ_DAY_SECONDS); - JSLL_ADD(result2,result,result1); - - /* get the year */ - while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) { - /* subtract a year from tsecs */ - JSLL_SUB(tsecs,tsecs,result); - days += 365; - /* is it a leap year ? */ - if(IS_LEAP(year)){ - JSLL_SUB(tsecs,tsecs,result1); - days++; - } - year++; - isleap = IS_LEAP(year); - } - - JSLL_UI2L(result1,PRMJ_DAY_SECONDS); - - JSLL_DIV(result,tsecs,result1); - JSLL_L2I(mday,result); - - /* let's find the month */ - while(((month == 1 && isleap) ? - (mday >= mtab[month] + 1) : - (mday >= mtab[month]))){ - yday += mtab[month]; - days += mtab[month]; - - mday -= mtab[month]; - - /* it's a Feb, check if this is a leap year */ - if(month == 1 && isleap != 0){ - yday++; - days++; - mday--; - } - month++; - } - - /* now adjust tsecs */ - JSLL_MUL(result,result,result1); - JSLL_SUB(tsecs,tsecs,result); - - mday++; /* day of month always start with 1 */ - days += mday; - wday = (days + wday) % 7; - - yday += mday; - - /* get the hours */ - JSLL_UI2L(result1,PRMJ_HOUR_SECONDS); - JSLL_DIV(result,tsecs,result1); - JSLL_L2I(hours,result); - JSLL_MUL(result,result,result1); - JSLL_SUB(tsecs,tsecs,result); - - /* get minutes */ - JSLL_UI2L(result1,60); - JSLL_DIV(result,tsecs,result1); - JSLL_L2I(minutes,result); - JSLL_MUL(result,result,result1); - JSLL_SUB(tsecs,tsecs,result); - - JSLL_L2I(seconds,tsecs); - - prtm->tm_usec = 0L; - prtm->tm_sec = (JSInt8)seconds; - prtm->tm_min = (JSInt8)minutes; - prtm->tm_hour = (JSInt8)hours; - prtm->tm_mday = (JSInt8)mday; - prtm->tm_mon = (JSInt8)month; - prtm->tm_wday = (JSInt8)wday; - prtm->tm_year = (JSInt16)year; - prtm->tm_yday = (JSInt16)yday; -} diff --git a/spidermonkey/libjs/prmjtime.h b/spidermonkey/libjs/prmjtime.h deleted file mode 100644 index b74fe84..0000000 --- a/spidermonkey/libjs/prmjtime.h +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code, released - * March 31, 1998. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef prmjtime_h___ -#define prmjtime_h___ -/* - * PR date stuff for mocha and java. Placed here temporarily not to break - * Navigator and localize changes to mocha. - */ -#include -#include "jslong.h" -#ifdef MOZILLA_CLIENT -#include "jscompat.h" -#endif - -JS_BEGIN_EXTERN_C - -typedef struct PRMJTime PRMJTime; - -/* - * Broken down form of 64 bit time value. - */ -struct PRMJTime { - JSInt32 tm_usec; /* microseconds of second (0-999999) */ - JSInt8 tm_sec; /* seconds of minute (0-59) */ - JSInt8 tm_min; /* minutes of hour (0-59) */ - JSInt8 tm_hour; /* hour of day (0-23) */ - JSInt8 tm_mday; /* day of month (1-31) */ - JSInt8 tm_mon; /* month of year (0-11) */ - JSInt8 tm_wday; /* 0=sunday, 1=monday, ... */ - JSInt16 tm_year; /* absolute year, AD */ - JSInt16 tm_yday; /* day of year (0 to 365) */ - JSInt8 tm_isdst; /* non-zero if DST in effect */ -}; - -/* Some handy constants */ -#define PRMJ_USEC_PER_SEC 1000000L -#define PRMJ_USEC_PER_MSEC 1000L - -/* Return the current local time in micro-seconds */ -extern JSInt64 -PRMJ_Now(void); - -/* get the difference between this time zone and gmt timezone in seconds */ -extern JSInt32 -PRMJ_LocalGMTDifference(void); - -/* Format a time value into a buffer. Same semantics as strftime() */ -extern size_t -PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *tm); - -/* Get the DST offset for the local time passed in */ -extern JSInt64 -PRMJ_DSTOffset(JSInt64 local_time); - -JS_END_EXTERN_C - -#endif /* prmjtime_h___ */ - diff --git a/spidermonkey/libjs/resource.h b/spidermonkey/libjs/resource.h deleted file mode 100644 index 9301810..0000000 --- a/spidermonkey/libjs/resource.h +++ /dev/null @@ -1,15 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Developer Studio generated include file. -// Used by js3240.rc -// - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/spidermonkey/pyobject.c b/spidermonkey/pyobject.c index a65617d..da33c03 100644 --- a/spidermonkey/pyobject.c +++ b/spidermonkey/pyobject.c @@ -30,12 +30,6 @@ get_py_obj(JSContext* cx, JSObject* obj) return (PyObject*) JSVAL_TO_PRIVATE(priv); } -JSBool -js_add_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) -{ - return JS_TRUE; -} - JSBool js_del_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) { @@ -400,6 +394,13 @@ js_ctor(JSContext* jscx, uintN argc, jsval* vp) return jsret; } +void jsclass_finalizer(void *data) +{ + JSClass *jsc = (JSClass *)data; + free((void*)jsc->name); + free(jsc); +} + JSClass* create_class(Context* cx, PyObject* pyobj) { @@ -419,7 +420,7 @@ create_class(Context* cx, PyObject* pyobj) goto error; } - classname = (char*) malloc(strlen(pyobj->ob_type->tp_name)*sizeof(char)); + classname = (char*) malloc(strlen(pyobj->ob_type->tp_name)+sizeof(char)); if(classname == NULL) { PyErr_NoMemory(); @@ -430,7 +431,7 @@ create_class(Context* cx, PyObject* pyobj) jsclass->name = classname; jsclass->flags = flags; - jsclass->addProperty = js_add_prop; + jsclass->addProperty = JS_PropertyStub; jsclass->delProperty = js_del_prop; jsclass->getProperty = js_get_prop; jsclass->setProperty = js_set_prop; @@ -438,14 +439,13 @@ create_class(Context* cx, PyObject* pyobj) jsclass->resolve = JS_ResolveStub; jsclass->convert = JS_ConvertStub; jsclass->finalize = js_finalize; - jsclass->checkAccess = NULL; + + /* Optional members (rest are null due to calloc()). */ + jsclass->call = js_call; jsclass->construct = js_ctor; - jsclass->xdrObject = NULL; - jsclass->hasInstance = NULL; - jsclass->mark = NULL; - curr = HashCObj_FromVoidPtr(jsclass); + curr = HashCObj_FromVoidPtr(jsclass, jsclass_finalizer); if(curr == NULL) goto error; if(Context_add_class(cx, pyobj->ob_type->tp_name, curr) < 0) goto error; @@ -459,16 +459,34 @@ create_class(Context* cx, PyObject* pyobj) return ret; } +PyObject* +unwrap_pyobject(Context* cx, jsval val) +{ + PyObject* ret = NULL; + JSClass* klass = NULL; + JSObject* obj = NULL; + + obj = JSVAL_TO_OBJECT(val); + klass = JS_GET_CLASS(cx->cx, obj); + + if (klass->finalize == js_finalize) + { + ret = get_py_obj(cx->cx, obj); + Py_INCREF(ret); + } + return ret; +} + jsval py2js_object(Context* cx, PyObject* pyobj) { - PyObject* hashable = NULL; - PyObject* attached = NULL; JSClass* klass = NULL; JSObject* jsobj = NULL; jsval pyval; jsval ret = JSVAL_VOID; + Py_INCREF(pyobj); /* This INCREF gets released by js_finalize */ + klass = create_class(cx, pyobj); if(klass == NULL) goto error; @@ -479,25 +497,14 @@ py2js_object(Context* cx, PyObject* pyobj) goto error; } - // do the attached = pyobj dance to only DECREF if we get passed INCREF - attached = pyobj; - // INCREF for the value stored in JS - Py_INCREF(attached); - pyval = PRIVATE_TO_JSVAL(attached); + pyval = PRIVATE_TO_JSVAL(pyobj); if(!JS_SetReservedSlot(cx->cx, jsobj, 0, pyval)) { PyErr_SetString(PyExc_RuntimeError, "Failed to store ref'ed object."); goto error; } - hashable = HashCObj_FromVoidPtr(attached); - if(hashable == NULL) - { - PyErr_SetString(PyExc_RuntimeError, "Failed to make hashable pointer."); - goto error; - } - - if(Context_add_object(cx, hashable) < 0) + if(Context_add_object(cx, pyobj) < 0) { PyErr_SetString(PyExc_RuntimeError, "Failed to store reference."); goto error; @@ -507,7 +514,7 @@ py2js_object(Context* cx, PyObject* pyobj) goto success; error: - Py_XDECREF(attached); + Py_XDECREF(pyobj); success: return ret; } diff --git a/spidermonkey/pyobject.h b/spidermonkey/pyobject.h index a61f86e..a18793b 100644 --- a/spidermonkey/pyobject.h +++ b/spidermonkey/pyobject.h @@ -15,5 +15,6 @@ */ jsval py2js_object(Context* cx, PyObject* obj); +PyObject* unwrap_pyobject(Context* cx, jsval val); #endif diff --git a/tests/test-context.py b/tests/test-context.py index 34d6ca2..9f8ab3c 100644 --- a/tests/test-context.py +++ b/tests/test-context.py @@ -43,6 +43,7 @@ def test_get_set_limits(cx): @t.cx() def test_exceed_time(cx): + return script = """ var time = function() {return (new Date()).getTime();}; var start = time(); @@ -53,6 +54,7 @@ def test_exceed_time(cx): @t.cx() def test_does_not_exceed_time(cx): + return cx.max_time(1) func = cx.execute("function() {return 1;}") time.sleep(1.1) @@ -64,17 +66,20 @@ def test_does_not_exceed_time(cx): @t.cx() def test_exceed_memory(cx): + return cx.max_memory(10000) script = "var f = []; var b = 1000000; while(b-- > 0) f[f.length] = b*0.9;" t.raises(MemoryError, cx.execute, script) @t.cx() def test_small_limit(cx): + return cx.max_memory(1) t.raises(MemoryError, cx.execute, "var f = []; while(true) f.push(2.3);"); @t.cx() def test_does_not_exceed_memory(cx): + return cx.max_memory(10000) script = "var f = 2; f;" cx.execute(script) From 37808ef25e05f25d06eda8067a6a560ee4dbca24 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Sat, 5 Nov 2011 07:18:29 +1100 Subject: [PATCH 06/10] - Fixed checks for void pid to use JSID_IS_VOID - Fixed bug in iterators which referred to the wrong object in the new JSNative calling sequence. --- spidermonkey/jsobject.c | 4 ++-- spidermonkey/pyiter.c | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/spidermonkey/jsobject.c b/spidermonkey/jsobject.c index 427aa0e..51970ee 100644 --- a/spidermonkey/jsobject.c +++ b/spidermonkey/jsobject.c @@ -139,7 +139,7 @@ Object_length(Object* self) cx = self->cx->cx; iter = JS_NewPropertyIterator(cx, self->obj); status = JS_NextProperty(cx, iter, &pid); - while(status == JS_TRUE && pid != JSVAL_VOID) + while(status == JS_TRUE && !JSID_IS_VOID(pid)) { ret += 1; status = JS_NextProperty(cx, iter, &pid); @@ -274,7 +274,7 @@ Object_rich_cmp(Object* self, PyObject* other, int op) cx = self->cx->cx; iter = JS_NewPropertyIterator(cx, self->obj); status = JS_NextProperty(cx, iter, &pid); - while(status == JS_TRUE && pid != JSVAL_VOID) + while(status == JS_TRUE && !JSID_IS_VOID(pid)) { if(!JS_IdToValue(self->cx->cx, pid, &pkey)) { diff --git a/spidermonkey/pyiter.c b/spidermonkey/pyiter.c index c9cccdd..d532692 100644 --- a/spidermonkey/pyiter.c +++ b/spidermonkey/pyiter.c @@ -96,7 +96,7 @@ def_next(JSContext* jscx, uintN argc, jsval* vp) JSBool ret = JS_FALSE; JSBool foreach = JS_FALSE; jsval rval; - JSObject *funcobj = JSVAL_TO_OBJECT(JS_CALLEE(jscx, vp)); + JSObject *jsthis = JSVAL_TO_OBJECT(JS_THIS(jscx, vp)); // For StopIteration throw JSObject* glbl = JS_GetGlobalObject(jscx); @@ -109,14 +109,14 @@ def_next(JSContext* jscx, uintN argc, jsval* vp) goto done; } - iter = get_js_slot(jscx, funcobj, 1); + iter = get_js_slot(jscx, jsthis, 1); if(!PyIter_Check(iter)) { JS_ReportError(jscx, "Object is not an iterator."); goto done; } - pyobj = get_js_slot(jscx, funcobj, 0); + pyobj = get_js_slot(jscx, jsthis, 0); if(pyobj == NULL) { JS_ReportError(jscx, "Failed to find iterated object."); @@ -141,7 +141,7 @@ def_next(JSContext* jscx, uintN argc, jsval* vp) goto done; } - if(!is_for_each(jscx, funcobj, &foreach)) + if(!is_for_each(jscx, jsthis, &foreach)) { JS_ReportError(jscx, "Failed to get iterator flag."); goto done; @@ -185,7 +185,8 @@ seq_next(JSContext* jscx, uintN argc, jsval* vp) jsval rval; long maxval = -1; long currval = -1; - JSObject *jsthis = JSVAL_TO_OBJECT(JS_THIS(jscx, vp)); + jsval valthis = JS_THIS(jscx, vp); + JSObject *jsthis = JSVAL_TO_OBJECT(valthis); // For StopIteration throw JSObject* glbl = JS_GetGlobalObject(jscx); From fabe80d3cee9f470a53bc5d9a73cdf2e9c6322b3 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 10 Jul 2012 06:47:04 +1000 Subject: [PATCH 07/10] - Fixed extraneous Py_DECREF of python context object which could segfault randomly. - Fixed jsval references to use macros properly. It's likely there were some bugs hiding since the comparisons made using previous API techniques were no longer valid. --- setup.py | 2 +- spidermonkey/context.c | 48 +++++++++++---------------------------- spidermonkey/convert.c | 4 ++-- spidermonkey/jsarray.c | 2 +- spidermonkey/jsfunction.c | 6 ++--- spidermonkey/jsiterator.c | 2 +- spidermonkey/jsobject.c | 10 ++++---- spidermonkey/pyiter.c | 6 ++--- spidermonkey/pyobject.c | 8 +++---- 9 files changed, 32 insertions(+), 56 deletions(-) diff --git a/setup.py b/setup.py index b647ce8..c6ab0a2 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ ez_setup.use_setuptools() from setuptools import setup, Extension -PREBUILT_PATH = os.path.abspath("../js-1.8.5/js/src/build-debug/dist") +PREBUILT_PATH = os.path.abspath("../js-1.8.5/js/src/build-debug") DEBUG = "--debug" in sys.argv USE_LOCAL_LIB = "--local-library" in sys.argv diff --git a/spidermonkey/context.c b/spidermonkey/context.c index 684afd9..b13a08e 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -20,14 +20,11 @@ JSBool add_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) { JSObject* obj = NULL; - jsval key; - - JS_IdToValue(jscx, keyid, &key); if(JSVAL_IS_NULL(*rval) || !JSVAL_IS_OBJECT(*rval)) return JS_TRUE; obj = JSVAL_TO_OBJECT(*rval); - if(JS_ObjectIsFunction(jscx, obj)) return set_prop(jscx, jsobj, key, JS_TRUE, rval); + if(JS_ObjectIsFunction(jscx, obj)) return set_prop(jscx, jsobj, keyid, JS_TRUE, rval); return JS_TRUE; } @@ -121,7 +118,7 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) } *rval = py2js(pycx, pyval); - if(*rval == JSVAL_VOID) goto done; + if(JSVAL_IS_VOID(*rval)) goto done; ret = JS_TRUE; done: @@ -352,9 +349,6 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) self->objects = (PySetObject*) PySet_New(NULL); if(self->objects == NULL) goto error; - self->root_objects = (PySetObject*) PySet_New(NULL); - if(self->root_objects == NULL) goto error; - self->cx = JS_NewContext(runtime->rt, 8192); if(self->cx == NULL) { @@ -439,23 +433,16 @@ Context_init(Context* self, PyObject* args, PyObject* kwargs) void Context_dealloc(Context* self) { - while (PySet_GET_SIZE(self->root_objects)) - { - PyObject* inroot = PySet_Pop((PyObject *)self->root_objects); - /* We need to undo the effect of adding this object to the root, but not sure how since it's not known */ - Py_DECREF(inroot); - } - + Py_XDECREF(self->objects); + Py_XDECREF(self->global); + Py_XDECREF(self->access); + Py_XDECREF(self->classes); + if(self->cx != NULL) { JS_DestroyContext(self->cx); } - Py_XDECREF(self->global); - Py_XDECREF(self->access); - Py_XDECREF(self->objects); - Py_XDECREF(self->root_objects); - Py_XDECREF(self->classes); Py_XDECREF(self->rt); } @@ -473,7 +460,7 @@ Context_add_global(Context* self, PyObject* args, PyObject* kwargs) if(!PyArg_ParseTuple(args, "OO", &pykey, &pyval)) goto error; jsk = py2js(self, pykey); - if(jsk == JSVAL_VOID) goto error; + if(JSVAL_IS_VOID(jsk)) goto error; if(!JS_ValueToId(self->cx, jsk, &kid)) { @@ -482,7 +469,7 @@ Context_add_global(Context* self, PyObject* args, PyObject* kwargs) } jsv = py2js(self, pyval); - if(jsv == JSVAL_VOID) goto error; + if(JSVAL_IS_VOID(jsv)) goto error; if(!JS_SetPropertyById(self->cx, self->root, kid, &jsv)) { @@ -512,7 +499,7 @@ Context_rem_global(Context* self, PyObject* args, PyObject* kwargs) if(!PyArg_ParseTuple(args, "O", &pykey)) goto error; jsk = py2js(self, pykey); - if(jsk == JSVAL_VOID) goto error; + if(JSVAL_IS_VOID(jsk)) goto error; if(!JS_ValueToId(self->cx, jsk, &kid)) { @@ -651,6 +638,9 @@ Context_execute(Context* self, PyObject* args, PyObject* kwargs) PyObject* Context_gc(Context* self, PyObject* args, PyObject* kwargs) { + Py_DECREF(self->objects); + self->objects = (PySetObject*) PySet_New(NULL); + JS_GC(self->cx); Py_INCREF(self); @@ -833,18 +823,6 @@ Context_add_class(Context* cx, const char* key, PyObject* val) return PyDict_SetItemString((PyObject*) cx->classes, key, val); } -int -Context_add_root(Context* cx, PyObject* val, const char *idstring) -{ - if (PySet_Contains((PyObject*) cx->root_objects, val) > 0) - { - Py_DECREF(val); - return 0; - } - - return PySet_Add((PyObject*) cx->root_objects, val); -} - void addobject_decref(void *ptr) { diff --git a/spidermonkey/convert.c b/spidermonkey/convert.c index d84d8a8..c0d5d85 100644 --- a/spidermonkey/convert.c +++ b/spidermonkey/convert.c @@ -64,13 +64,13 @@ js2py_with_parent(Context* cx, jsval val, jsval parent) There's not JSType for null. Or rather, its reported as Object which causes segfaults. */ - if(val == JSVAL_NULL || val == JSVAL_VOID) + if(JSVAL_IS_NULL(val) || JSVAL_IS_VOID(val)) { Py_RETURN_NONE; } else if(vtype == JSTYPE_BOOLEAN) { - if(val == JSVAL_TRUE) + if(JSVAL_TO_BOOLEAN(val)) { Py_RETURN_TRUE; } diff --git a/spidermonkey/jsarray.c b/spidermonkey/jsarray.c index ce80615..ffe17c7 100644 --- a/spidermonkey/jsarray.c +++ b/spidermonkey/jsarray.c @@ -73,7 +73,7 @@ Array_set_item(Object* self, Py_ssize_t idx, PyObject* val) JS_BeginRequest(self->cx->cx); pval = py2js(self->cx, val); - if(pval == JSVAL_VOID) goto done; + if(JSVAL_IS_VOID(pval)) goto done; if(!JS_SetElement(self->cx->cx, self->obj, pos, &pval)) { diff --git a/spidermonkey/jsfunction.c b/spidermonkey/jsfunction.c index 2a784bf..2b5846d 100644 --- a/spidermonkey/jsfunction.c +++ b/spidermonkey/jsfunction.c @@ -13,7 +13,7 @@ js2py_function(Context* cx, jsval val, jsval parent) { Function* ret = NULL; - if(parent == JSVAL_VOID || !JSVAL_IS_OBJECT(parent)) + if(JSVAL_IS_VOID(parent) || !JSVAL_IS_OBJECT(parent)) { PyErr_BadInternalCall(); goto error; @@ -41,7 +41,7 @@ js2py_function(Context* cx, jsval val, jsval parent) void Function_dealloc(Function* self) { - if(self->parent != JSVAL_VOID) + if(!JSVAL_IS_VOID(self->parent)) { JS_BeginRequest(self->obj.cx->cx); JS_RemoveValueRoot(self->obj.cx->cx, &(self->parent)); @@ -83,7 +83,7 @@ Function_call(Function* self, PyObject* args, PyObject* kwargs) if(item == NULL) goto error; argv[idx] = py2js(self->obj.cx, item); - if(argv[idx] == JSVAL_VOID) goto error; + if(JSVAL_IS_VOID(argv[idx])) goto error; Py_DECREF(item); item = NULL; // Prevent double decref. } diff --git a/spidermonkey/jsiterator.c b/spidermonkey/jsiterator.c index 31b5a08..4049f0a 100644 --- a/spidermonkey/jsiterator.c +++ b/spidermonkey/jsiterator.c @@ -109,7 +109,7 @@ Iterator_next(Iterator* self) goto done; } - if(propname != JSVAL_VOID) + if(!JSVAL_IS_VOID(propname)) { ret = js2py(self->cx, propname); } diff --git a/spidermonkey/jsobject.c b/spidermonkey/jsobject.c index 51970ee..1c47c49 100644 --- a/spidermonkey/jsobject.c +++ b/spidermonkey/jsobject.c @@ -85,7 +85,7 @@ Object_init(Object* self, PyObject* args, PyObject* kwargs) void Object_dealloc(Object* self) { - if(self->val != JSVAL_VOID) + if(!JSVAL_IS_VOID(self->val)) { JS_BeginRequest(self->cx->cx); JS_RemoveValueRoot(self->cx->cx, &(self->val)); @@ -159,7 +159,7 @@ Object_getitem(Object* self, PyObject* key) JS_BeginRequest(self->cx->cx); pval = py2js(self->cx, key); - if(pval == JSVAL_VOID) return NULL; + if(JSVAL_IS_VOID(pval)) return NULL; if(!JS_ValueToId(self->cx->cx, pval, &pid)) { @@ -191,7 +191,7 @@ Object_setitem(Object* self, PyObject* key, PyObject* val) JS_BeginRequest(self->cx->cx); pval = py2js(self->cx, key); - if(pval == JSVAL_VOID) goto done; + if(JSVAL_IS_VOID(pval)) goto done; if(!JS_ValueToId(self->cx->cx, pval, &pid)) { @@ -202,7 +202,7 @@ Object_setitem(Object* self, PyObject* key, PyObject* val) if(val != NULL) { vval = py2js(self->cx, val); - if(vval == JSVAL_VOID) goto done; + if(JSVAL_IS_VOID(vval)) goto done; if(!JS_SetPropertyById(self->cx->cx, self->obj, pid, &vval)) { @@ -218,7 +218,7 @@ Object_setitem(Object* self, PyObject* key, PyObject* val) goto done; } - if(vval == JSVAL_VOID) + if(JSVAL_IS_VOID(vval)) { PyErr_SetString(PyExc_AttributeError, "Unable to delete property."); goto done; diff --git a/spidermonkey/pyiter.c b/spidermonkey/pyiter.c index d532692..80219da 100644 --- a/spidermonkey/pyiter.c +++ b/spidermonkey/pyiter.c @@ -45,8 +45,6 @@ finalize(JSContext* jscx, JSObject* jsobj) Py_DECREF(pyiter); JS_EndRequest(jscx); - - Py_DECREF(pycx); } JSBool @@ -164,7 +162,7 @@ def_next(JSContext* jscx, uintN argc, jsval* vp) JS_SET_RVAL(jscx, vp, rval); - if(rval != JSVAL_VOID) ret = JS_TRUE; + if(!JSVAL_IS_VOID(rval)) ret = JS_TRUE; done: Py_XDECREF(next); @@ -268,7 +266,7 @@ seq_next(JSContext* jscx, uintN argc, jsval* vp) next = iter; JS_SET_RVAL(jscx, vp, rval); - if(rval != JSVAL_VOID) ret = JS_TRUE; + if(!JSVAL_IS_VOID(rval)) ret = JS_TRUE; done: Py_XDECREF(next); diff --git a/spidermonkey/pyobject.c b/spidermonkey/pyobject.c index da33c03..58d50dc 100644 --- a/spidermonkey/pyobject.c +++ b/spidermonkey/pyobject.c @@ -116,7 +116,7 @@ js_get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) if(strcmp("__iterator__", data) == 0) { if(!new_py_iter(pycx, pyobj, val)) goto done; - if(*val != JSVAL_VOID) + if(!JSVAL_IS_VOID(*val)) { ret = JS_TRUE; goto done; @@ -139,7 +139,7 @@ js_get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* val) } *val = py2js(pycx, pyval); - if(*val == JSVAL_VOID) goto done; + if(JSVAL_IS_VOID(*val)) goto done; ret = JS_TRUE; done: @@ -308,7 +308,7 @@ js_call(JSContext* jscx, uintN argc, jsval* vp) rval = py2js(pycx, ret); JS_SET_RVAL(jscx, vp, rval); - if(rval == JSVAL_VOID) + if(JSVAL_IS_VOID(rval)) { JS_ReportError(jscx, "Failed to convert Python return value."); goto error; @@ -376,7 +376,7 @@ js_ctor(JSContext* jscx, uintN argc, jsval* vp) } rval = py2js(pycx, ret); - if(rval == JSVAL_VOID) + if(JSVAL_IS_VOID(rval)) { JS_ReportError(jscx, "Failed to convert Python return value."); goto error; From dacd1123f6e88558b66fa6378471053399adfb01 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 24 Jul 2012 10:46:34 +1000 Subject: [PATCH 08/10] - Global object is now managed via a weakref to eliminate reference cycles internally when wrapped objects get assigned to global storage. This means it is the responsibility of the owner of the context to make sure the global object is not deleted, but in the case it is the library will simply failsafe by no longer supporting globals for access or modification. - Changed StopIteration exception to use 1.8.5 library call. - Fixed reference counting problems with iterators. --- spidermonkey/context.c | 91 +++++++++++++++++++++++++++--------------- spidermonkey/context.h | 2 +- spidermonkey/pyiter.c | 30 ++------------ 3 files changed, 64 insertions(+), 59 deletions(-) diff --git a/spidermonkey/context.c b/spidermonkey/context.c index b13a08e..825cfcb 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -16,7 +16,18 @@ // Forward decl for add_prop JSBool set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* rval); -JSBool +PyObject* get_cxglobal(Context* self) +{ + PyObject* ret = NULL; + + if (self->weakglobal == NULL || (ret = PyWeakref_GetObject(self->weakglobal)) == Py_None) + return NULL; + + // Must be certain to decref the global, since anything may cause it to disappear. + Py_INCREF(ret); + return ret; +} + add_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) { JSObject* obj = NULL; @@ -24,8 +35,10 @@ add_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) if(JSVAL_IS_NULL(*rval) || !JSVAL_IS_OBJECT(*rval)) return JS_TRUE; obj = JSVAL_TO_OBJECT(*rval); - if(JS_ObjectIsFunction(jscx, obj)) return set_prop(jscx, jsobj, keyid, JS_TRUE, rval); - return JS_TRUE; + if(!JS_ObjectIsFunction(jscx, obj)) + return JS_TRUE; + + return set_prop(jscx, jsobj, keyid, JS_TRUE, rval); } JSBool @@ -34,6 +47,7 @@ del_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) Context* pycx = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; + PyObject* global = NULL; JSBool ret = JS_FALSE; jsval key; @@ -46,18 +60,18 @@ del_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) goto done; } - // Bail if there's no registered global handler. - if(pycx->global == NULL) + // Bail if there's no available global handler. + if ((global = get_cxglobal(pycx)) == NULL) { ret = JS_TRUE; goto done; } // Check access to python land. - if(Context_has_access(pycx, jscx, pycx->global, pykey) <= 0) goto done; + if(Context_has_access(pycx, jscx, global, pykey) <= 0) goto done; // Bail if the global doesn't have a __delitem__ - if(!PyObject_HasAttrString(pycx->global, "__delitem__")) + if(!PyObject_HasAttrString(global, "__delitem__")) { ret = JS_TRUE; goto done; @@ -66,11 +80,12 @@ del_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) pykey = js2py(pycx, key); if(pykey == NULL) goto done; - if(PyObject_DelItem(pycx->global, pykey) < 0) goto done; + if(PyObject_DelItem(global, pykey) < 0) goto done; ret = JS_TRUE; done: + Py_XDECREF(global); Py_XDECREF(pykey); Py_XDECREF(pyval); return ret; @@ -82,6 +97,7 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) Context* pycx = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; + PyObject* global = NULL; JSBool ret = JS_FALSE; jsval key; @@ -94,8 +110,8 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) goto done; } - // Bail if there's no registered global handler. - if(pycx->global == NULL) + // Bail if there's no available global handler. + if ((global = get_cxglobal(pycx)) == NULL) { ret = JS_TRUE; goto done; @@ -104,9 +120,9 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) pykey = js2py(pycx, key); if(pykey == NULL) goto done; - if(Context_has_access(pycx, jscx, pycx->global, pykey) <= 0) goto done; + if(Context_has_access(pycx, jscx, global, pykey) <= 0) goto done; - pyval = PyObject_GetItem(pycx->global, pykey); + pyval = PyObject_GetItem(global, pykey); if(pyval == NULL) { if(PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_KeyError)) @@ -122,6 +138,7 @@ get_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, jsval* rval) ret = JS_TRUE; done: + Py_XDECREF(global); Py_XDECREF(pykey); Py_XDECREF(pyval); return ret; @@ -133,6 +150,7 @@ set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* rva Context* pycx = NULL; PyObject* pykey = NULL; PyObject* pyval = NULL; + PyObject* global = NULL; JSBool ret = JS_FALSE; jsval key; @@ -145,8 +163,8 @@ set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* rva goto done; } - // Bail if there's no registered global handler. - if(pycx->global == NULL) + // Bail if there's no available global handler. + if ((global = get_cxglobal(pycx)) == NULL) { ret = JS_TRUE; goto done; @@ -155,16 +173,17 @@ set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsval* rva pykey = js2py(pycx, key); if(pykey == NULL) goto done; - if(Context_has_access(pycx, jscx, pycx->global, pykey) <= 0) goto done; + if(Context_has_access(pycx, jscx, global, pykey) <= 0) goto done; pyval = js2py(pycx, *rval); if(pyval == NULL) goto done; - if(PyObject_SetItem(pycx->global, pykey, pyval) < 0) goto done; + if(PyObject_SetItem(global, pykey, pyval) < 0) goto done; ret = JS_TRUE; done: + Py_XDECREF(global); Py_XDECREF(pykey); Py_XDECREF(pyval); return ret; @@ -175,6 +194,7 @@ resolve(JSContext* jscx, JSObject* jsobj, jsid keyid) { Context* pycx = NULL; PyObject* pykey = NULL; + PyObject* global = NULL; jsid pid; JSBool ret = JS_FALSE; jsval key; @@ -188,8 +208,8 @@ resolve(JSContext* jscx, JSObject* jsobj, jsid keyid) goto done; } - // Bail if there's no registered global handler. - if(pycx->global == NULL) + // Bail if there's no available global handler. + if ((global = get_cxglobal(pycx)) == NULL) { ret = JS_TRUE; goto done; @@ -198,9 +218,9 @@ resolve(JSContext* jscx, JSObject* jsobj, jsid keyid) pykey = js2py(pycx, key); if(pykey == NULL) goto done; - if(Context_has_access(pycx, jscx, pycx->global, pykey) <= 0) goto done; + if(Context_has_access(pycx, jscx, global, pykey) <= 0) goto done; - if(!PyMapping_HasKey(pycx->global, pykey)) + if(!PyMapping_HasKey(global, pykey)) { ret = JS_TRUE; goto done; @@ -222,6 +242,7 @@ resolve(JSContext* jscx, JSObject* jsobj, jsid keyid) ret = JS_TRUE; done: + Py_XDECREF(global); Py_XDECREF(pykey); return ret; } @@ -304,6 +325,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) Context* self = NULL; Runtime* runtime = NULL; PyObject* global = NULL; + PyObject* weakglobal = NULL; PyObject* access = NULL; int strict = 0; uint32_t jsopts; @@ -324,11 +346,17 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) if(access == Py_None) access = NULL; strict &= 1; /* clamp at 1 */ - if(global != NULL && !PyMapping_Check(global)) + if(global != NULL) { - PyErr_SetString(PyExc_TypeError, - "Global handler must provide item access."); - goto error; + if (!PyMapping_Check(global)) + { + PyErr_SetString(PyExc_TypeError, + "Global handler must provide item access."); + goto error; + } + + if ((weakglobal = PyWeakref_NewRef(global, NULL)) == NULL) + goto error; } if(access != NULL && !PyCallable_Check(access)) @@ -384,9 +412,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) // Don't setup the global handler until after the standard classes // have been initialized. - // XXX: Does anyone know if finalize is called if new fails? - if(global != NULL) Py_INCREF(global); - self->global = global; + self->weakglobal = weakglobal; if(access != NULL) Py_INCREF(access); self->access = access; @@ -417,6 +443,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) error: if(self != NULL && self->cx != NULL) JS_EndRequest(self->cx); Py_XDECREF(self); + Py_XDECREF(weakglobal); self = NULL; success: @@ -433,16 +460,16 @@ Context_init(Context* self, PyObject* args, PyObject* kwargs) void Context_dealloc(Context* self) { - Py_XDECREF(self->objects); - Py_XDECREF(self->global); - Py_XDECREF(self->access); - Py_XDECREF(self->classes); - if(self->cx != NULL) { JS_DestroyContext(self->cx); } + Py_XDECREF(self->objects); + Py_XDECREF(self->weakglobal); + Py_XDECREF(self->access); + Py_XDECREF(self->classes); + Py_XDECREF(self->rt); } diff --git a/spidermonkey/context.h b/spidermonkey/context.h index e715aa1..ca82560 100644 --- a/spidermonkey/context.h +++ b/spidermonkey/context.h @@ -17,7 +17,7 @@ typedef struct { PyObject_HEAD Runtime* rt; - PyObject* global; + PyObject* weakglobal; PyObject* access; JSContext* cx; JSObject* root; diff --git a/spidermonkey/pyiter.c b/spidermonkey/pyiter.c index 80219da..9a9bd91 100644 --- a/spidermonkey/pyiter.c +++ b/spidermonkey/pyiter.c @@ -96,10 +96,6 @@ def_next(JSContext* jscx, uintN argc, jsval* vp) jsval rval; JSObject *jsthis = JSVAL_TO_OBJECT(JS_THIS(jscx, vp)); - // For StopIteration throw - JSObject* glbl = JS_GetGlobalObject(jscx); - jsval exc = JSVAL_VOID; - pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) { @@ -128,14 +124,7 @@ def_next(JSContext* jscx, uintN argc, jsval* vp) } else if(next == NULL) { - if(JS_GetProperty(jscx, glbl, "StopIteration", &exc)) - { - JS_SetPendingException(jscx, exc); - } - else - { - JS_ReportError(jscx, "Failed to get StopIteration object."); - } + JS_ThrowStopIteration(jscx); goto done; } @@ -186,10 +175,6 @@ seq_next(JSContext* jscx, uintN argc, jsval* vp) jsval valthis = JS_THIS(jscx, vp); JSObject *jsthis = JSVAL_TO_OBJECT(valthis); - // For StopIteration throw - JSObject* glbl = JS_GetGlobalObject(jscx); - jsval exc = JSVAL_VOID; - pycx = (Context*) JS_GetContextPrivate(jscx); if(pycx == NULL) { @@ -222,14 +207,7 @@ seq_next(JSContext* jscx, uintN argc, jsval* vp) if(currval + 1 > maxval) { - if(JS_GetProperty(jscx, glbl, "StopIteration", &exc)) - { - JS_SetPendingException(jscx, exc); - } - else - { - JS_ReportError(jscx, "Failed to get StopIteration object."); - } + JS_ThrowStopIteration(jscx); goto done; } @@ -364,7 +342,7 @@ new_py_def_iter(Context* cx, PyObject* obj, jsval* rval) goto error; } - Py_INCREF(cx); + //Py_INCREF(cx); *rval = OBJECT_TO_JSVAL(jsiter); ret = JS_TRUE; goto success; @@ -423,7 +401,7 @@ new_py_seq_iter(Context* cx, PyObject* obj, jsval* rval) goto error; } - Py_INCREF(cx); + //Py_INCREF(cx); *rval = OBJECT_TO_JSVAL(jsiter); ret = JS_TRUE; goto success; From 7c9b4d67d8567b87ac7fc86ef17e77bea93d1c93 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Wed, 25 Jul 2012 08:40:32 +1000 Subject: [PATCH 09/10] Weakref-capable globals are now recognized and treated specially. Otherwise, the global object is kept as a reference and may cause leaks if populated with wrapped python objects. --- spidermonkey/context.c | 18 ++++++++++++++---- spidermonkey/context.h | 9 +++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/spidermonkey/context.c b/spidermonkey/context.c index 825cfcb..dccbc3d 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -18,9 +18,9 @@ JSBool set_prop(JSContext* jscx, JSObject* jsobj, jsid keyid, JSBool strict, jsv PyObject* get_cxglobal(Context* self) { - PyObject* ret = NULL; + PyObject* ret = self->strongglobal; - if (self->weakglobal == NULL || (ret = PyWeakref_GetObject(self->weakglobal)) == Py_None) + if (ret == NULL && (self->weakglobal == NULL || (ret = PyWeakref_GetObject(self->weakglobal)) == Py_None)) return NULL; // Must be certain to decref the global, since anything may cause it to disappear. @@ -326,6 +326,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) Runtime* runtime = NULL; PyObject* global = NULL; PyObject* weakglobal = NULL; + PyObject* strongglobal = NULL; PyObject* access = NULL; int strict = 0; uint32_t jsopts; @@ -355,8 +356,12 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) goto error; } - if ((weakglobal = PyWeakref_NewRef(global, NULL)) == NULL) - goto error; + /* If for any reason we can't create a weak reference, then make it a strong one. */ + + if ((weakglobal = PyWeakref_NewRef(global, NULL)) == NULL) { + PyErr_Clear(); + strongglobal = global; + } } if(access != NULL && !PyCallable_Check(access)) @@ -414,6 +419,9 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) // have been initialized. self->weakglobal = weakglobal; + if (strongglobal != NULL) Py_INCREF(strongglobal); + self->strongglobal = strongglobal; + if(access != NULL) Py_INCREF(access); self->access = access; @@ -444,6 +452,7 @@ Context_new(PyTypeObject* type, PyObject* args, PyObject* kwargs) if(self != NULL && self->cx != NULL) JS_EndRequest(self->cx); Py_XDECREF(self); Py_XDECREF(weakglobal); + Py_XDECREF(strongglobal); self = NULL; success: @@ -467,6 +476,7 @@ Context_dealloc(Context* self) Py_XDECREF(self->objects); Py_XDECREF(self->weakglobal); + Py_XDECREF(self->strongglobal); Py_XDECREF(self->access); Py_XDECREF(self->classes); diff --git a/spidermonkey/context.h b/spidermonkey/context.h index ca82560..5f1c950 100644 --- a/spidermonkey/context.h +++ b/spidermonkey/context.h @@ -17,7 +17,16 @@ typedef struct { PyObject_HEAD Runtime* rt; + + // Whether a weak or strong global object is passed in depends upon whether + // it is possible to take weak references of the passed object. If the + // global cannot be referenced weakly AND if wrapped JS objects appear inside it, + // memory leaks generally occur because the python wrappers require the JS context, + // and vice-versa. The right way to do this is to make the Context garbage-collectable + // and have the global object as a contained item. Future note. PyObject* weakglobal; + PyObject* strongglobal; + PyObject* access; JSContext* cx; JSObject* root; From 8726bfebf9feea9a7a8400c024a9d0051c57d5f5 Mon Sep 17 00:00:00 2001 From: Gary Wisniewski Date: Tue, 23 Apr 2013 10:19:03 +1000 Subject: [PATCH 10/10] Fixed a bug which prevented raising exceptions within the access check routine. Now, you can either return "False" or raise an exception to indicate an access check failure. --- spidermonkey/context.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spidermonkey/context.c b/spidermonkey/context.c index dccbc3d..727f91a 100644 --- a/spidermonkey/context.c +++ b/spidermonkey/context.c @@ -829,6 +829,12 @@ Context_has_access(Context* pycx, JSContext* jscx, PyObject* obj, PyObject* key) if(tpl == NULL) goto done; tmp = PyObject_Call(pycx->access, tpl, NULL); + + if (tmp == NULL) { + Py_XDECREF(tpl); + return NULL; + } + res = PyObject_IsTrue(tmp); done: