From 24043e321265c53ae53cf6dd6184492fdf784389 Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Wed, 11 Apr 2001 23:07:26 +0000 Subject: [PATCH] - Don't initialize the arguments class, and do rename it to "Object" so that arguments objects are prototyped by Object.prototype, per ECMA. We still want a custom class, in order to lazily resolve active-JSStackFrame-based properties. - Use a reserved slot to hold a bitmap of deleted arguments elements, so we don't re-resolve them after they've been deleted (which would make them seem to be permanent, contrary to ECMA). This work involved sprucing up several fundamental types (jsbitmap) and macros (jsbitmap helper macros in jsbit.h, JS_HOWMANY in jstypes.h, JSVAL_INT_BITS in jsapi.h). git-svn-id: svn://10.0.0.236/trunk@92016 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/src/jsapi.c | 6 - mozilla/js/src/jsapi.h | 1 + mozilla/js/src/jsbit.h | 9 +- mozilla/js/src/jsfun.c | 329 +++++++++++++++++++++++++++++++-------- mozilla/js/src/jstypes.h | 8 +- 5 files changed, 272 insertions(+), 81 deletions(-) diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index 07da9e86a3a..9bbe09615a8 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -1098,9 +1098,6 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj) js_InitMathClass(cx, obj) && js_InitNumberClass(cx, obj) && js_InitStringClass(cx, obj) && -#if JS_HAS_ARGS_OBJECT - js_InitArgumentsClass(cx, obj) && -#endif #if JS_HAS_CALL_OBJECT js_InitCallClass(cx, obj) && #endif @@ -1143,9 +1140,6 @@ static struct { {js_InitMathClass, ATOM_OFFSET(Math)}, {js_InitNumberClass, ATOM_OFFSET(Number)}, {js_InitStringClass, ATOM_OFFSET(String)}, -#if JS_HAS_ARGS_OBJECT - {js_InitArgumentsClass, ATOM_OFFSET(Arguments)}, -#endif #if JS_HAS_CALL_OBJECT {js_InitCallClass, ATOM_OFFSET(Call)}, #endif diff --git a/mozilla/js/src/jsapi.h b/mozilla/js/src/jsapi.h index 6a72309e6e1..26670960bd5 100644 --- a/mozilla/js/src/jsapi.h +++ b/mozilla/js/src/jsapi.h @@ -90,6 +90,7 @@ JS_BEGIN_EXTERN_C : 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) diff --git a/mozilla/js/src/jsbit.h b/mozilla/js/src/jsbit.h index 0b57ffd63f0..950cc36ba4f 100644 --- a/mozilla/js/src/jsbit.h +++ b/mozilla/js/src/jsbit.h @@ -41,14 +41,15 @@ JS_BEGIN_EXTERN_C /* ** A jsbitmap_t is a long integer that can be used for bitmaps */ -typedef unsigned long jsbitmap_t; +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_LONG_LOG2] & (1L << ((_bit) & (JS_BITS_PER_LONG-1)))) + ((_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_LONG_LOG2] |= (1L << ((_bit) & (JS_BITS_PER_LONG-1)))) + ((_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_LONG_LOG2] &= ~(1L << ((_bit) & (JS_BITS_PER_LONG-1)))) + ((_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 diff --git a/mozilla/js/src/jsfun.c b/mozilla/js/src/jsfun.c index dd9c5cd028e..e340adec538 100644 --- a/mozilla/js/src/jsfun.c +++ b/mozilla/js/src/jsfun.c @@ -38,6 +38,7 @@ #include "jsstddef.h" #include #include "jstypes.h" +#include "jsbit.h" #include "jsutil.h" /* Added by JSIFY */ #include "jsapi.h" #include "jsarray.h" @@ -94,6 +95,68 @@ js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp) 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) @@ -124,11 +187,17 @@ js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, *vp = JSVAL_VOID; if (JSVAL_IS_INT(id)) { slot = (uintN) JSVAL_TO_INT(id); - if (slot < fp->argc) + 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 { - if (id == (jsid) cx->runtime->atomState.lengthAtom) + if (id == (jsid) cx->runtime->atomState.lengthAtom) { + if (fp->argsobj && TEST_BIT(ARGS_LENGTH, fp->overrides)) + return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp); *vp = INT_TO_JSVAL((jsint) fp->argc); + } } return JS_TRUE; } @@ -161,17 +230,29 @@ JSBool js_PutArgsObject(JSContext *cx, JSStackFrame *fp) { JSObject *argsobj; + jsval bmapval, rval; JSBool ok; JSRuntime *rt; - jsval rval; /* * Reuse args_enumerate here to reflect fp's actual arguments as indexed - * elements of argsobj. + * 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. @@ -193,32 +274,34 @@ js_PutArgsObject(JSContext *cx, JSStackFrame *fp) } static JSBool -Arguments(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - if (JS_HAS_STRICT_OPTION(cx) && - !JS_ReportErrorFlagsAndNumber(cx, - JSREPORT_WARNING | JSREPORT_STRICT, - js_GetErrorMessage, NULL, - JSMSG_DEPRECATED_USAGE, - js_ArgumentsClass.name)) { - return JS_FALSE; - } + jsint slot; + JSStackFrame *fp; - if (!cx->fp->constructing) { - obj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL); - if (!obj) + 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_BIT(slot, fp->overrides); + break; + + default: + if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot)) return JS_FALSE; - *rval = OBJECT_TO_JSVAL(obj); + break; } return JS_TRUE; } -static JSPropertySpec args_props[] = { - {js_length_str, ARGS_LENGTH, 0,0,0}, - {js_callee_str, ARGS_CALLEE, 0,0,0}, - {0,0,0,0,0} -}; - static JSBool args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { @@ -227,25 +310,26 @@ args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) if (!JSVAL_IS_INT(id)) return JS_TRUE; - slot = JSVAL_TO_INT(id); 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 (fp && !TEST_BIT(slot, fp->overrides)) + if (!TEST_BIT(slot, fp->overrides)) *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); break; case ARGS_LENGTH: - if (fp && !TEST_BIT(slot, fp->overrides)) + if (!TEST_BIT(slot, fp->overrides)) *vp = INT_TO_JSVAL((jsint)fp->argc); break; default: - if (fp && (uintN)slot < fp->argc) + if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) *vp = fp->argv[slot]; break; } @@ -258,70 +342,185 @@ args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) jsint slot; JSStackFrame *fp; jsdouble argc; + jsval bmapval; + jsbitmap *bitmap; if (!JSVAL_IS_INT(id)) return JS_TRUE; - slot = JSVAL_TO_INT(id); 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 (fp) - SET_BIT(slot, fp->overrides); + SET_BIT(slot, fp->overrides); break; case ARGS_LENGTH: - if (fp) { - if (!js_ValueToNumber(cx, *vp, &argc)) - return JS_FALSE; - argc = js_DoubleToInteger(argc); - if (0 <= argc && argc < fp->argc) - fp->argc = (uintN)argc; - SET_BIT(slot, fp->overrides); + if (!js_ValueToNumber(cx, *vp, &argc)) + return JS_FALSE; + argc = js_DoubleToInteger(argc); + if (0 <= argc && argc < fp->argc) { + /* Check whether we need to free the deleted args bitmap. */ + if (fp->argc > JSVAL_INT_BITS && + (uintN)argc <= JSVAL_INT_BITS) { + (void) JS_GetReservedSlot(cx, obj, 0, &bmapval); + if (!JSVAL_IS_VOID(bmapval)) { + bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval); + bmapval = INT_TO_JSVAL(bitmap[0]); + JS_free(cx, bitmap); + JS_SetReservedSlot(cx, obj, 0, bmapval); + } + } + + /* Reduce fp's actual argument count accordingly. */ + fp->argc = (uintN)argc; } + SET_BIT(slot, fp->overrides); break; default: - if (fp && (uintN)slot < fp->argc) + if ((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; + JSScopeProperty *sprop; + intN tinyid; + jsval value; + + *objp = NULL; + fp = (JSStackFrame *) + JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); + if (!fp) + return JS_TRUE; + + 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, (jsid) id, fp->argv[slot], + args_getProperty, args_setProperty, + JSVERSION_IS_ECMA(cx->version) + ? 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; + } + } + + if (atom && !TEST_BIT(tinyid, fp->overrides)) { + if (!js_DefineProperty(cx, obj, (jsid) atom, value, + args_getProperty, args_setProperty, 0, + (JSProperty **) &sprop)) { + return JS_FALSE; + } +#ifdef JS_DOUBLE_HASHING + sprop->attrs |= JSPROP_INDEX; + sprop->tinyid = tinyid; +#else + sprop->id = INT_TO_JSVAL(tinyid); +#endif + OBJ_DROP_PROPERTY(cx, obj, (JSProperty *) sprop); + *objp = obj; + } + } + + return JS_TRUE; +} + static JSBool args_enumerate(JSContext *cx, JSObject *obj) { JSStackFrame *fp; - uintN attrs, slot; + JSObject *pobj; + JSProperty *prop; + uintN slot; fp = (JSStackFrame *) JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL); if (!fp) return JS_TRUE; - /* XXX ECMA specs DontEnum, contrary to all other array-like objects */ - attrs = JSVERSION_IS_ECMA(cx->version) ? 0 : JSPROP_ENUMERATE; + /* + * 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, (jsid) cx->runtime->atomState.lengthAtom, + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); + + if (!js_LookupProperty(cx, obj, (jsid) cx->runtime->atomState.calleeAtom, + &pobj, &prop)) { + return JS_FALSE; + } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); for (slot = 0; slot < fp->argc; slot++) { - if (!js_DefineProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot), - fp->argv[slot], NULL, NULL, attrs, - NULL)) { + if (!js_LookupProperty(cx, obj, (jsid) INT_TO_JSVAL((jsint)slot), + &pobj, &prop)) { return JS_FALSE; } + if (prop) + OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE; } +/* + * 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_Arguments_str, - JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, - JS_PropertyStub, JS_PropertyStub, + js_Object_str, + JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1), + JS_PropertyStub, args_delProperty, args_getProperty, args_setProperty, - args_enumerate, JS_ResolveStub, + args_enumerate, (JSResolveOp) args_resolve, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS }; @@ -436,14 +635,16 @@ call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSStackFrame *fp; jsint slot; - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!JSVAL_IS_INT(id)) return JS_TRUE; - slot = JSVAL_TO_INT(id); + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + slot = JSVAL_TO_INT(id); switch (slot) { case CALL_ARGUMENTS: - if (fp && !TEST_BIT(slot, fp->overrides)) { + if (!TEST_BIT(slot, fp->overrides)) { JSObject *argsobj = js_GetArgsObject(cx, fp); if (!argsobj) return JS_FALSE; @@ -452,17 +653,17 @@ call_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) break; case CALL_CALLEE: - if (fp && !TEST_BIT(slot, fp->overrides)) + if (!TEST_BIT(slot, fp->overrides)) *vp = fp->argv ? fp->argv[-2] : OBJECT_TO_JSVAL(fp->fun->object); break; case FUN_CALL: - if (fp && !TEST_BIT(slot, fp->overrides)) + if (!TEST_BIT(slot, fp->overrides)) *vp = OBJECT_TO_JSVAL(obj); break; default: - if (fp && (uintN)slot < fp->argc) + if ((uintN)slot < fp->argc) *vp = fp->argv[slot]; break; } @@ -475,21 +676,22 @@ call_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) JSStackFrame *fp; jsint slot; - fp = (JSStackFrame *) JS_GetPrivate(cx, obj); if (!JSVAL_IS_INT(id)) return JS_TRUE; - slot = JSVAL_TO_INT(id); + fp = (JSStackFrame *) JS_GetPrivate(cx, obj); + if (!fp) + return JS_TRUE; + slot = JSVAL_TO_INT(id); switch (slot) { case CALL_ARGUMENTS: case CALL_CALLEE: case FUN_CALL: - if (fp) - SET_BIT(slot, fp->overrides); + SET_BIT(slot, fp->overrides); break; default: - if (fp && (uintN)slot < fp->argc) + if ((uintN)slot < fp->argc) fp->argv[slot] = *vp; break; } @@ -1655,15 +1857,6 @@ bad: return NULL; } -#if JS_HAS_ARGS_OBJECT -JSObject * -js_InitArgumentsClass(JSContext *cx, JSObject *obj) -{ - return JS_InitClass(cx, obj, NULL, &js_ArgumentsClass, Arguments, 0, - args_props, NULL, NULL, NULL); -} -#endif - #if JS_HAS_CALL_OBJECT JSObject * js_InitCallClass(JSContext *cx, JSObject *obj) diff --git a/mozilla/js/src/jstypes.h b/mozilla/js/src/jstypes.h index a4cf3e18750..f3361191f51 100644 --- a/mozilla/js/src/jstypes.h +++ b/mozilla/js/src/jstypes.h @@ -217,13 +217,15 @@ #define JS_BITMASK(n) (JS_BIT(n) - 1) /*********************************************************************** -** MACROS: JS_ROUNDUP +** MACROS: JS_HOWMANY +** JS_ROUNDUP ** JS_MIN -** JS_MAX +** JS_MAX ** DESCRIPTION: ** Commonly used macros for operations on compatible types. ***********************************************************************/ -#define JS_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y)) +#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))