Bug 559250 - encapsulate JSSLOT_{PRIMITIVE,DATE,REGEXP}_* within JSObject. r=brendan.
☠☠ backed out by 5da61a630e98 ☠ ☠
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 14 Apr 2010 16:18:03 -0700
changeset 40899 681b0067e61b4f964dfa871dee1747cd7f9e9ed1
parent 40898 d9e5e022384fbc8c585fa6855110d00ab223c9c3
child 40900 5da61a630e9869141abdc88fdc5841ddb6a898b7
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs559250
milestone1.9.3a5pre
Bug 559250 - encapsulate JSSLOT_{PRIMITIVE,DATE,REGEXP}_* within JSObject. r=brendan.
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jsdate.cpp
js/src/jsdate.h
js/src/jsinterp.cpp
js/src/jsnum.cpp
js/src/jsnum.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsregexp.cpp
js/src/jsregexp.h
js/src/jsstr.cpp
js/src/jsstr.h
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -49,16 +49,18 @@
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 #include "jsvector.h"
 
+#include "jsobjinlines.h"
+
 /* Check pseudo-booleans values. */
 JS_STATIC_ASSERT(!(JSVAL_TRUE & JSVAL_HOLE_FLAG));
 JS_STATIC_ASSERT(!(JSVAL_FALSE & JSVAL_HOLE_FLAG));
 JS_STATIC_ASSERT(!(JSVAL_VOID & JSVAL_HOLE_FLAG));
 JS_STATIC_ASSERT((JSVAL_HOLE & JSVAL_HOLE_FLAG));
 JS_STATIC_ASSERT((JSVAL_HOLE & ~JSVAL_HOLE_FLAG) == JSVAL_VOID);
 JS_STATIC_ASSERT(!(JSVAL_ARETURN & JSVAL_HOLE_FLAG));
 
@@ -134,30 +136,30 @@ Boolean(JSContext *cx, JSObject *obj, ui
     jsval bval;
 
     bval = (argc != 0)
            ? BOOLEAN_TO_JSVAL(js_ValueToBoolean(argv[0]))
            : JSVAL_FALSE;
     if (!JS_IsConstructing(cx))
         *rval = bval;
     else
-        obj->fslots[JSSLOT_PRIMITIVE_THIS] = bval;
+        obj->setPrimitiveThis(bval);
     return 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;
-    proto->fslots[JSSLOT_PRIMITIVE_THIS] = JSVAL_FALSE;
+    proto->setPrimitiveThis(JSVAL_FALSE);
     return proto;
 }
 
 JSString *
 js_BooleanToString(JSContext *cx, JSBool b)
 {
     return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[b ? 1 : 0]);
 }
--- a/js/src/jsbool.h
+++ b/js/src/jsbool.h
@@ -39,16 +39,17 @@
 
 #ifndef jsbool_h___
 #define jsbool_h___
 /*
  * JS boolean interface.
  */
 
 #include "jsapi.h"
+#include "jsobj.h"
 
 JS_BEGIN_EXTERN_C
 
 /*
  * Special values, 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
@@ -62,16 +63,22 @@ JS_BEGIN_EXTERN_C
  * NB: SPECIAL_TO_JSVAL(2) is JSVAL_VOID (see jsapi.h).
  */
 #define JSVAL_HOLE_FLAG jsval(4 << JSVAL_TAGBITS)
 #define JSVAL_HOLE      (JSVAL_VOID | JSVAL_HOLE_FLAG)
 #define JSVAL_ARETURN   SPECIAL_TO_JSVAL(8)
 
 extern JSClass js_BooleanClass;
 
+inline bool
+JSObject::isBoolean() const
+{
+    return getClass() == &js_BooleanClass;
+}
+
 extern JSObject *
 js_InitBooleanClass(JSContext *cx, JSObject *obj);
 
 extern JSString *
 js_BooleanToString(JSContext *cx, JSBool b);
 
 extern JSBool
 js_BooleanToCharBuffer(JSContext *cx, JSBool b, JSCharBuffer &cb);
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -487,28 +487,19 @@ msFromTime(jsdouble t)
 /**
  * end of ECMA 'support' functions
  */
 
 /*
  * Other Support routines and definitions
  */
 
-/*
- * We use the first reseved slot to store UTC time, and the second for caching
- * the local time. The initial value of the cache entry is NaN.
- */
-const uint32 JSSLOT_UTC_TIME    = JSSLOT_PRIVATE;
-const uint32 JSSLOT_LOCAL_TIME  = JSSLOT_PRIVATE + 1;
-
-const uint32 DATE_RESERVED_SLOTS = 2;
-
 JSClass js_DateClass = {
     js_Date_str,
-    JSCLASS_HAS_RESERVED_SLOTS(DATE_RESERVED_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_FIXED_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   NULL,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 /* for use by date_parse */
 
@@ -1207,61 +1198,61 @@ date_now_tn(JSContext*)
  * Get UTC time from the date object. Returns false if the object is not
  * Date type.
  */
 static JSBool
 GetUTCTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
 {
     if (!JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
         return JS_FALSE;
-    *dp = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
+    *dp = *JSVAL_TO_DOUBLE(obj->getDateUTCTime());
     return JS_TRUE;
 }
 
 static void
 SetDateToNaN(JSContext *cx, JSObject *obj, jsval *vp = NULL)
 {
     JS_ASSERT(obj->getClass() == &js_DateClass);
 
-    obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue;
-    obj->fslots[JSSLOT_UTC_TIME] = cx->runtime->NaNValue;
+    obj->setDateLocalTime(cx->runtime->NaNValue);
+    obj->setDateUTCTime(cx->runtime->NaNValue);
     if (vp)
         *vp = cx->runtime->NaNValue;
 }
 
 /*
  * Set UTC time to a given time and invalidate cached local time.
  */
 static JSBool
 SetUTCTime(JSContext *cx, JSObject *obj, jsdouble t, jsval *vp = NULL)
 {
     JS_ASSERT(obj->getClass() == &js_DateClass);
 
-    obj->fslots[JSSLOT_LOCAL_TIME] = cx->runtime->NaNValue;
-    if (!js_NewDoubleInRootedValue(cx, t, &obj->fslots[JSSLOT_UTC_TIME]))
+    obj->setDateLocalTime(cx->runtime->NaNValue);
+    if (!js_NewDoubleInRootedValue(cx, t, obj->addressOfDateUTCTime()))
         return false;
     if (vp)
-        *vp = obj->fslots[JSSLOT_UTC_TIME];
+        *vp = obj->getDateUTCTime();
     return true;
 }
 
 /*
  * Get the local time, cache it if necessary. If UTC time is not finite
  * (e.g., NaN), the local time slot is set to the UTC time without conversion.
  */
 static JSBool
 GetAndCacheLocalTime(JSContext *cx, JSObject *obj, jsval *vp, jsdouble *dp)
 {
     if (!obj || !JS_InstanceOf(cx, obj, &js_DateClass, vp ? vp + 2 : NULL))
         return false;
 
-    jsval *slotp = &obj->fslots[JSSLOT_LOCAL_TIME];
+    jsval *slotp = obj->addressOfDateLocalTime();
     jsdouble result = *JSVAL_TO_DOUBLE(*slotp);
     if (JSDOUBLE_IS_NaN(result)) {
-        result = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
+        result = *JSVAL_TO_DOUBLE(obj->getDateUTCTime());
 
         /* if result is NaN, it couldn't be finite. */
         if (JSDOUBLE_IS_FINITE(result))
             result = LocalTime(result);
 
         if (!js_NewDoubleInRootedValue(cx, result, slotp))
             return false;
     }
@@ -2208,17 +2199,17 @@ date_toString(JSContext *cx, uintN argc,
     return date_format(cx, utctime, FORMATSPEC_FULL, vp);
 }
 
 #ifdef JS_TRACER
 static jsval FASTCALL
 date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
 {
     JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
-    jsdouble t = *JSVAL_TO_DOUBLE(obj->fslots[JSSLOT_UTC_TIME]);
+    jsdouble t = *JSVAL_TO_DOUBLE(obj->getDateUTCTime());
 
     JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
     jsval v;
     if (js_EqualStrings(str, number_str)) {
         if (!js_NewNumberInRootedValue(cx, t, &v))
             return JSVAL_ERROR_COOKIE;
     } else {
         if (!date_format(cx, t, FORMATSPEC_FULL, &v))
--- a/js/src/jsdate.h
+++ b/js/src/jsdate.h
@@ -43,16 +43,22 @@
 
 #ifndef jsdate_h___
 #define jsdate_h___
 
 JS_BEGIN_EXTERN_C
 
 extern JSClass js_DateClass;
 
+inline bool
+JSObject::isDate() const
+{
+    return getClass() == &js_DateClass;
+}
+
 extern JSObject *
 js_InitDateClass(JSContext *cx, JSObject *obj);
 
 /*
  * These functions provide a C interface to the date/time object
  */
 
 /*
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -361,17 +361,17 @@ js_GetPrimitiveThis(JSContext *cx, jsval
     jsval v;
     JSObject *obj;
 
     v = vp[1];
     if (JSVAL_IS_OBJECT(v)) {
         obj = JS_THIS_OBJECT(cx, vp);
         if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
             return JS_FALSE;
-        v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+        v = obj->getPrimitiveThis();
     }
     *thisvp = v;
     return JS_TRUE;
 }
 
 /* Some objects (e.g., With) delegate 'this' to another object. */
 static inline JSObject *
 CallThisObjectHook(JSContext *cx, JSObject *obj, jsval *argv)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -65,18 +65,20 @@
 #include "jsgc.h"
 #include "jsinterp.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsprf.h"
 #include "jsscope.h"
 #include "jsstr.h"
+#include "jsvector.h"
+
+#include "jsobjinlines.h"
 #include "jsstrinlines.h"
-#include "jsvector.h"
 
 using namespace js;
 
 #ifndef JS_HAVE_STDINT_H /* Native support is innocent until proven guilty. */
 
 JS_STATIC_ASSERT(uint8_t(-1) == UINT8_MAX);
 JS_STATIC_ASSERT(uint16_t(-1) == UINT16_MAX);
 JS_STATIC_ASSERT(uint32_t(-1) == UINT32_MAX);
@@ -283,17 +285,17 @@ Number(JSContext *cx, JSObject *obj, uin
             return JS_FALSE;
         v = argv[0];
     } else {
         v = JSVAL_ZERO;
     }
     if (!JS_IsConstructing(cx))
         *rval = v;
     else
-        obj->fslots[JSSLOT_PRIMITIVE_THIS] = v;
+        obj->setPrimitiveThis(v);
     return true;
 }
 
 #if JS_HAS_TOSOURCE
 static JSBool
 num_toSource(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval v;
@@ -521,17 +523,17 @@ num_valueOf(JSContext *cx, uintN argc, j
     v = vp[1];
     if (JSVAL_IS_NUMBER(v)) {
         *vp = v;
         return JS_TRUE;
     }
     obj = JS_THIS_OBJECT(cx, vp);
     if (!JS_InstanceOf(cx, obj, &js_NumberClass, vp + 2))
         return JS_FALSE;
-    *vp = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+    *vp = obj->getPrimitiveThis();
     return JS_TRUE;
 }
 
 
 #define MAX_PRECISION 100
 
 static JSBool
 num_to(JSContext *cx, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode,
@@ -775,17 +777,17 @@ js_InitNumberClass(JSContext *cx, JSObje
 
     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;
-    proto->fslots[JSSLOT_PRIMITIVE_THIS] = JSVAL_ZERO;
+    proto->setPrimitiveThis(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, rt->NaNValue, JS_PropertyStub, JS_PropertyStub,
                            JSPROP_PERMANENT | JSPROP_READONLY)) {
         return NULL;
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -45,16 +45,17 @@
 #include <float.h>
 #endif
 #ifdef SOLARIS
 #include <ieeefp.h>
 #endif
 
 #include "jsstdint.h"
 #include "jsstr.h"
+#include "jsobj.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.
@@ -181,16 +182,22 @@ extern void
 js_TraceRuntimeNumberState(JSTracer *trc);
 
 extern void
 js_FinishRuntimeNumberState(JSContext *cx);
 
 /* Initialize the Number class, returning its prototype object. */
 extern JSClass js_NumberClass;
 
+inline bool
+JSObject::isNumber() const
+{
+    return getClass() == &js_NumberClass;
+}
+
 extern "C" 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[];
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5346,17 +5346,17 @@ js_DefaultValue(JSContext *cx, JSObject 
                 jsval fval = pobj->lockedGetSlot(sprop->slot);
 
                 if (VALUE_IS_FUNCTION(cx, fval)) {
                     JSObject *funobj = JSVAL_TO_OBJECT(fval);
                     JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
 
                     if (FUN_FAST_NATIVE(fun) == js_str_toString) {
                         JS_UNLOCK_SCOPE(cx, scope);
-                        *vp = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+                        *vp = obj->getPrimitiveThis();
                         return JS_TRUE;
                     }
                 }
             }
             JS_UNLOCK_SCOPE(cx, scope);
         }
 
         /*
@@ -6086,17 +6086,17 @@ js_PrimitiveToObject(JSContext *cx, jsva
     };
 
     JS_ASSERT(!JSVAL_IS_OBJECT(*vp));
     JS_ASSERT(!JSVAL_IS_VOID(*vp));
     clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1];
     obj = NewObject(cx, clasp, NULL, NULL);
     if (!obj)
         return JS_FALSE;
-    obj->fslots[JSSLOT_PRIMITIVE_THIS] = *vp;
+    obj->setPrimitiveThis(*vp);
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 JSBool
 js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
 {
     JSObject *obj;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -43,16 +43,17 @@
 /*
  * 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 "jsapi.h"
 #include "jshash.h" /* Added by JSIFY */
 #include "jspubtd.h"
 #include "jsprvtd.h"
 
 namespace js { class AutoDescriptorArray; }
 
 /*
  * A representation of ECMA-262 ed. 5's internal property descriptor data
@@ -206,18 +207,16 @@ const uint32 JSSLOT_PARENT  = 1;
  * The first available slot to store generic value. For JSCLASS_HAS_PRIVATE
  * classes the slot stores a pointer to private data reinterpreted as jsval.
  * Such pointer is stored as is without an overhead of PRIVATE_TO_JSVAL
  * tagging and should be accessed using the (get|set)Private methods of
  * JSObject.
  */
 const uint32 JSSLOT_PRIVATE = 2;
 
-const uint32 JSSLOT_PRIMITIVE_THIS = JSSLOT_PRIVATE;
-
 const uintptr_t JSSLOT_CLASS_MASK_BITS = 3;
 
 /*
  * JSObject struct, with members sized to fit in 32 bytes on 32-bit targets,
  * 64 bytes on 64-bit systems. The JSFunction struct is an extension of this
  * struct allocated from a larger GC size-class.
  *
  * The classword member stores the JSClass pointer for this object, with the
@@ -386,16 +385,27 @@ struct JSObject {
 
     static jsval defaultPrivate(JSClass *clasp) {
         return (clasp->flags & JSCLASS_HAS_PRIVATE)
                ? JSVAL_NULL
                : JSVAL_VOID;
     }
 
     /*
+     * Primitive-specific getters and setters.
+     */
+
+  private:
+    static const uint32 JSSLOT_PRIMITIVE_THIS = JSSLOT_PRIVATE;
+
+  public:
+    inline jsval getPrimitiveThis() const;
+    inline void setPrimitiveThis(jsval pthis);
+
+    /*
      * Array-specific getters and setters (for both dense and slow arrays).
      */
 
   private:
     static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE;
     static const uint32 JSSLOT_ARRAY_COUNT  = JSSLOT_PRIVATE + 1;
     static const uint32 JSSLOT_ARRAY_UNUSED = JSSLOT_PRIVATE + 2;
 
@@ -445,16 +455,50 @@ struct JSObject {
     inline void setArgsLength(uint32 argc);
     inline void setArgsLengthOverridden();
     inline bool isArgsLengthOverridden();
 
     inline jsval getArgsCallee() const;
     inline void setArgsCallee(jsval callee);
 
     /*
+     * Date-specific getters and setters.
+     */
+
+  private:
+    // The second slot caches the local time;  it's initialized to NaN.
+    static const uint32 JSSLOT_DATE_UTC_TIME   = JSSLOT_PRIVATE;
+    static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1;
+
+  public:
+    static const uint32 DATE_FIXED_RESERVED_SLOTS = 2;
+
+    inline jsval getDateLocalTime() const;
+    inline jsval *addressOfDateLocalTime();
+    inline void setDateLocalTime(jsval pthis);
+
+    inline jsval getDateUTCTime() const;
+    inline jsval *addressOfDateUTCTime();
+    inline void setDateUTCTime(jsval pthis);
+
+    /*
+     * RegExp-specific getters and setters.
+     */
+
+  private:
+    static const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
+
+  public:
+    static const uint32 REGEXP_FIXED_RESERVED_SLOTS = 1;
+
+    inline jsval getRegExpLastIndex() const;
+    inline jsval *addressOfRegExpLastIndex();
+    inline void zeroRegExpLastIndex();
+
+    /*
      * Back to generic stuff.
      */
 
     bool isCallable();
 
     /* The map field is not initialized here and should be set separately. */
     void init(JSClass *clasp, JSObject *proto, JSObject *parent,
               jsval privateSlotValue) {
@@ -548,16 +592,21 @@ struct JSObject {
         if (map->ops->dropProperty)
             map->ops->dropProperty(cx, this, prop);
     }
 
     inline bool isArguments() const;
     inline bool isArray() const;
     inline bool isDenseArray() const;
     inline bool isSlowArray() const;
+    inline bool isNumber() const;
+    inline bool isBoolean() const;
+    inline bool isString() const;
+    inline bool isPrimitive() const;
+    inline bool isDate() const;
     inline bool isFunction() const;
     inline bool isRegExp() const;
     inline bool isXML() const;
 
     inline bool unbrand(JSContext *cx);
 };
 
 #define JSSLOT_START(clasp) (((clasp)->flags & JSCLASS_HAS_PRIVATE)           \
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -36,18 +36,20 @@
  * 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 jsobjinlines_h___
 #define jsobjinlines_h___
 
+#include "jsbool.h"
+#include "jsdate.h"
+#include "jsiter.h"
 #include "jsobj.h"
-#include "jsiter.h"
 #include "jsscope.h"
 
 #ifdef INCLUDE_MOZILLA_DTRACE
 #include "jsdtracef.h"
 #endif
 
 #include "jsscopeinlines.h"
 
@@ -83,43 +85,63 @@ JSObject::setSlotMT(JSContext *cx, uintN
         this->lockedSetSlot(slot, value);
     else
         js_SetSlotThreadSafe(cx, this, slot, value);
 #else
     this->lockedSetSlot(slot, value);
 #endif
 }
 
+inline bool
+JSObject::isPrimitive() const
+{
+    return isNumber() || isString() || isBoolean();
+}
+
+inline jsval
+JSObject::getPrimitiveThis() const
+{
+    JS_ASSERT(isPrimitive());
+    return fslots[JSSLOT_PRIMITIVE_THIS];
+}
+
+inline void 
+JSObject::setPrimitiveThis(jsval pthis)
+{
+    JS_ASSERT(isPrimitive());
+    fslots[JSSLOT_PRIMITIVE_THIS] = pthis;
+}
+
 inline void JSObject::staticAssertArrayLengthIsInPrivateSlot()
 {
     JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE);
 }
 
 inline uint32
 JSObject::getArrayLength() const
 {
     JS_ASSERT(isArray());
     return uint32(fslots[JSSLOT_ARRAY_LENGTH]);
 }
 
+inline void 
+JSObject::setArrayLength(uint32 length)
+{
+    JS_ASSERT(isArray());
+    fslots[JSSLOT_ARRAY_LENGTH] = length;
+}
+
 inline uint32 
 JSObject::getArrayCount() const
 {
     JS_ASSERT(isArray());
     return uint32(fslots[JSSLOT_ARRAY_COUNT]);
 }
 
 inline void 
-JSObject::setArrayLength(uint32 length)
-{
-    JS_ASSERT(isArray());
-    fslots[JSSLOT_ARRAY_LENGTH] = length;
-}
-
-inline void 
 JSObject::setArrayCount(uint32 count)
 {
     JS_ASSERT(isArray());
     fslots[JSSLOT_ARRAY_COUNT] = count;
 }
 
 inline void 
 JSObject::voidDenseArrayCount()
@@ -194,16 +216,79 @@ JSObject::getArgsCallee() const
 
 inline void 
 JSObject::setArgsCallee(jsval callee)
 {
     JS_ASSERT(isArguments());
     fslots[JSSLOT_ARGS_CALLEE] = callee;
 }
 
+inline jsval
+JSObject::getDateLocalTime() const
+{
+    JS_ASSERT(isDate());
+    return fslots[JSSLOT_DATE_LOCAL_TIME];
+}
+
+inline jsval *
+JSObject::addressOfDateLocalTime()
+{
+    JS_ASSERT(isDate());
+    return &fslots[JSSLOT_DATE_LOCAL_TIME];
+}
+
+inline void 
+JSObject::setDateLocalTime(jsval time)
+{
+    JS_ASSERT(isDate());
+    fslots[JSSLOT_DATE_LOCAL_TIME] = time;
+}
+
+inline jsval
+JSObject::getDateUTCTime() const
+{
+    JS_ASSERT(isDate());
+    return fslots[JSSLOT_DATE_UTC_TIME];
+}
+
+inline jsval *
+JSObject::addressOfDateUTCTime()
+{
+    JS_ASSERT(isDate());
+    return &fslots[JSSLOT_DATE_UTC_TIME];
+}
+
+inline void 
+JSObject::setDateUTCTime(jsval time)
+{
+    JS_ASSERT(isDate());
+    fslots[JSSLOT_DATE_UTC_TIME] = time;
+}
+
+inline jsval
+JSObject::getRegExpLastIndex() const
+{
+    JS_ASSERT(isRegExp());
+    return fslots[JSSLOT_REGEXP_LAST_INDEX];
+}
+
+inline jsval *
+JSObject::addressOfRegExpLastIndex()
+{
+    JS_ASSERT(isRegExp());
+    return &fslots[JSSLOT_REGEXP_LAST_INDEX];
+}
+
+inline void 
+JSObject::zeroRegExpLastIndex()
+{
+    JS_ASSERT(isRegExp());
+    fslots[JSSLOT_REGEXP_LAST_INDEX] = JSVAL_ZERO;
+}
+
 inline void
 JSObject::initSharingEmptyScope(JSClass *clasp, JSObject *proto, JSObject *parent,
                                 jsval privateSlotValue)
 {
     init(clasp, proto, parent, privateSlotValue);
 
     JSEmptyScope *emptyScope = proto->scope()->emptyScope;
     JS_ASSERT(emptyScope->clasp == clasp);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -450,17 +450,17 @@ Str(JSContext *cx, jsid id, JSObject *ho
 
     if (callReplacer && !CallReplacerFunction(cx, id, holder, scx, vp))
         return JS_FALSE;
 
     // catches string and number objects with no toJSON
     if (!JSVAL_IS_PRIMITIVE(*vp)) {
         JSClass *clasp = JSVAL_TO_OBJECT(*vp)->getClass();
         if (clasp == &js_StringClass || clasp == &js_NumberClass)
-            *vp = JSVAL_TO_OBJECT(*vp)->fslots[JSSLOT_PRIMITIVE_THIS];
+            *vp = JSVAL_TO_OBJECT(*vp)->getPrimitiveThis();
     }
 
     if (JSVAL_IS_STRING(*vp)) {
         const jschar *chars;
         size_t length;
         JSVAL_TO_STRING(*vp)->getCharsAndLength(chars, length);
         return write_string(cx, scx->cb, chars, length);
     }
@@ -516,17 +516,17 @@ static JSBool
 InitializeGap(JSContext *cx, jsval space, JSCharBuffer &cb)
 {
     AutoValueRooter gap(cx, space);
 
     if (!JSVAL_IS_PRIMITIVE(space)) {
         JSObject *obj = JSVAL_TO_OBJECT(space);
         JSClass *clasp = obj->getClass();
         if (clasp == &js_NumberClass || clasp == &js_StringClass)
-            *gap.addr() = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+            *gap.addr() = obj->getPrimitiveThis();
     }
 
     if (JSVAL_IS_STRING(gap.value())) {
         if (!js_ValueToCharBuffer(cx, gap.value(), cb))
             return JS_FALSE;
         if (cb.length() > 10)
             cb.resize(10);
     }
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -5069,42 +5069,21 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp
 
 out:
     JS_ARENA_RELEASE(&cx->regexpPool, mark);
     return ok;
 }
 
 /************************************************************************/
 
-static jsdouble
-GetRegExpLastIndex(JSObject *obj)
-{
-    JS_ASSERT(obj->getClass() == &js_RegExpClass);
-
-    jsval v = obj->fslots[JSSLOT_REGEXP_LAST_INDEX];
-    if (JSVAL_IS_INT(v))
-        return JSVAL_TO_INT(v);
-    JS_ASSERT(JSVAL_IS_DOUBLE(v));
-    return *JSVAL_TO_DOUBLE(v);
-}
-
-static jsval
-GetRegExpLastIndexValue(JSObject *obj)
-{
-    JS_ASSERT(obj->getClass() == &js_RegExpClass);
-    return obj->fslots[JSSLOT_REGEXP_LAST_INDEX];
-}
-
 static JSBool
 SetRegExpLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
 {
-    JS_ASSERT(obj->getClass() == &js_RegExpClass);
-
-    return JS_NewNumberValue(cx, lastIndex,
-                             &obj->fslots[JSSLOT_REGEXP_LAST_INDEX]);
+    JS_ASSERT(obj->isRegExp());
+    return JS_NewNumberValue(cx, lastIndex, obj->addressOfRegExpLastIndex());
 }
 
 static JSBool
 regexp_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
     jsint slot;
     JSRegExp *re;
 
@@ -5112,17 +5091,17 @@ regexp_getProperty(JSContext *cx, JSObje
         return JS_TRUE;
     while (obj->getClass() != &js_RegExpClass) {
         obj = obj->getProto();
         if (!obj)
             return JS_TRUE;
     }
     slot = JSVAL_TO_INT(id);
     if (slot == REGEXP_LAST_INDEX) {
-        *vp = GetRegExpLastIndexValue(obj);
+        *vp = obj->getRegExpLastIndex();
         return JS_TRUE;
     }
 
     JS_LOCK_OBJ(cx, obj);
     re = (JSRegExp *) obj->getPrivate();
     if (re) {
         switch (slot) {
           case REGEXP_SOURCE:
@@ -5434,17 +5413,17 @@ js_XDRRegExpObject(JSXDRState *xdr, JSOb
         if (!obj)
             return JS_FALSE;
         obj->clearParent();
         obj->clearProto();
         re = js_NewRegExp(xdr->cx, NULL, source, (uint8)flagsword, JS_FALSE);
         if (!re)
             return JS_FALSE;
         obj->setPrivate(re);
-        js_ClearRegExpLastIndex(obj);
+        obj->zeroRegExpLastIndex();
         *objp = obj;
     }
     return JS_TRUE;
 }
 
 #else  /* !JS_HAS_XDR */
 
 #define js_XDRRegExpObject NULL
@@ -5457,17 +5436,17 @@ regexp_trace(JSTracer *trc, JSObject *ob
     JSRegExp *re = (JSRegExp *) obj->getPrivate();
     if (re && re->source)
         JS_CALL_STRING_TRACER(trc, re->source, "source");
 }
 
 JSClass js_RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(REGEXP_CLASS_FIXED_RESERVED_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,    JS_PropertyStub,
     JS_PropertyStub,    JS_PropertyStub,
     JS_EnumerateStub,   JS_ResolveStub,
     JS_ConvertStub,     regexp_finalize,
     NULL,               NULL,
     regexp_call,        NULL,
     js_XDRRegExpObject, NULL,
@@ -5646,17 +5625,17 @@ regexp_compile_sub(JSContext *cx, JSObje
 
     re = js_NewRegExpOpt(cx, str, opt, JS_FALSE);
 created:
     if (!re)
         return JS_FALSE;
     JS_LOCK_OBJ(cx, obj);
     oldre = (JSRegExp *) obj->getPrivate();
     obj->setPrivate(re);
-    js_ClearRegExpLastIndex(obj);
+    obj->zeroRegExpLastIndex();
     JS_UNLOCK_OBJ(cx, obj);
     if (oldre)
         js_DestroyRegExp(cx, oldre);
     *rval = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 static JSBool
@@ -5686,19 +5665,27 @@ regexp_exec_sub(JSContext *cx, JSObject 
     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);
     sticky = (re->flags & JSREG_STICKY) != 0;
-    lastIndex = (re->flags & (JSREG_GLOB | JSREG_STICKY))
-                ? GetRegExpLastIndex(obj)
-                : 0;
+    if (re->flags & (JSREG_GLOB | JSREG_STICKY)) {
+        jsval v = obj->getRegExpLastIndex();
+        if (JSVAL_IS_INT(v)) {
+            lastIndex = JSVAL_TO_INT(v);
+        } else {
+            JS_ASSERT(JSVAL_IS_DOUBLE(v));
+            lastIndex = *JSVAL_TO_DOUBLE(v);
+        }
+    } else {
+        lastIndex = 0;
+    }
     JS_UNLOCK_OBJ(cx, obj);
 
     /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
     if (argc == 0) {
         str = cx->regExpStatics.input;
         if (!str) {
             const char *bytes = js_GetStringBytes(cx, re->source);
 
@@ -5719,25 +5706,25 @@ regexp_exec_sub(JSContext *cx, JSObject 
         if (!str) {
             ok = JS_FALSE;
             goto out;
         }
         argv[0] = STRING_TO_JSVAL(str);
     }
 
     if (lastIndex < 0 || str->length() < lastIndex) {
-        js_ClearRegExpLastIndex(obj);
+        obj->zeroRegExpLastIndex();
         *rval = JSVAL_NULL;
     } else {
         i = (size_t) lastIndex;
         ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
         if (ok &&
             ((re->flags & JSREG_GLOB) || (*rval != JSVAL_NULL && sticky))) {
             if (*rval == JSVAL_NULL)
-                js_ClearRegExpLastIndex(obj);
+                obj->zeroRegExpLastIndex();
             else
                 ok = SetRegExpLastIndex(cx, obj, i);
         }
     }
 
 out:
     DROP_REGEXP(cx, re);
     return ok;
@@ -5845,32 +5832,32 @@ js_NewRegExpObject(JSContext *cx, TokenS
     if (!re)
         return NULL;
     obj = NewObject(cx, &js_RegExpClass, NULL, NULL);
     if (!obj) {
         js_DestroyRegExp(cx, re);
         return NULL;
     }
     obj->setPrivate(re);
-    js_ClearRegExpLastIndex(obj);
+    obj->zeroRegExpLastIndex();
     return obj;
 }
 
 JSObject * JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
 {
     JS_ASSERT(obj->getClass() == &js_RegExpClass);
     JS_ASSERT(proto);
     JS_ASSERT(proto->getClass() == &js_RegExpClass);
     JSObject *clone = NewObjectWithGivenProto(cx, &js_RegExpClass, proto, NULL);
     if (!clone)
         return NULL;
     JSRegExp *re = static_cast<JSRegExp *>(obj->getPrivate());
     clone->setPrivate(re);
-    js_ClearRegExpLastIndex(clone);
+    clone->zeroRegExpLastIndex();
     HOLD_REGEXP(cx, re);
     return clone;
 }
 
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_CloneRegExpObject, CONTEXT, OBJECT, OBJECT, 0,
                      ACC_STORE_ANY)
 #endif
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -190,25 +190,15 @@ js_NewRegExpObject(JSContext *cx, js::To
                    const jschar *chars, size_t length, uintN flags);
 
 extern JSBool
 js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp);
 
 extern JS_FRIEND_API(JSObject *) JS_FASTCALL
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto);
 
-const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
-const uint32 REGEXP_CLASS_FIXED_RESERVED_SLOTS = 1;
-
-static inline void
-js_ClearRegExpLastIndex(JSObject *obj)
-{
-    JS_ASSERT(obj->getClass() == &js_RegExpClass);
-    obj->fslots[JSSLOT_REGEXP_LAST_INDEX] = JSVAL_ZERO;
-}
-
 /* Return whether the given character array contains RegExp meta-characters. */
 extern bool
 js_ContainsRegExpMetaChars(const jschar *chars, size_t length);
 
 JS_END_EXTERN_C
 
 #endif /* jsregexp_h___ */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -72,16 +72,18 @@
 #include "jsopcode.h"
 #include "jsregexp.h"
 #include "jsscope.h"
 #include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jsbit.h"
 #include "jsvector.h"
 #include "jsversion.h"
+
+#include "jsobjinlines.h"
 #include "jsstrinlines.h"
 
 using namespace js;
 
 #define JSSTRDEP_RECURSION_LIMIT        100
 
 JS_STATIC_ASSERT(size_t(JSString::MAX_LENGTH) <= size_t(JSVAL_INT_MAX));
 JS_STATIC_ASSERT(INT_FITS_IN_JSVAL(JSString::MAX_LENGTH));
@@ -548,17 +550,17 @@ static JSBool
 str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
     jsval v;
     JSString *str;
 
     if (id == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
         if (obj->getClass() == &js_StringClass) {
             /* Follow ECMA-262 by fetching intrinsic length of our string. */
-            v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+            v = obj->getPrimitiveThis();
             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;
         }
@@ -573,17 +575,17 @@ str_getProperty(JSContext *cx, JSObject 
 
 static JSBool
 str_enumerate(JSContext *cx, JSObject *obj)
 {
     jsval v;
     JSString *str, *str1;
     size_t i, length;
 
-    v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+    v = obj->getPrimitiveThis();
     JS_ASSERT(JSVAL_IS_STRING(v));
     str = JSVAL_TO_STRING(v);
 
     length = str->length();
     for (i = 0; i < length; i++) {
         str1 = js_NewDependentString(cx, str, i, 1);
         if (!str1)
             return JS_FALSE;
@@ -601,17 +603,17 @@ str_resolve(JSContext *cx, JSObject *obj
 {
     jsval v;
     JSString *str, *str1;
     jsint slot;
 
     if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING))
         return JS_TRUE;
 
-    v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+    v = obj->getPrimitiveThis();
     JS_ASSERT(JSVAL_IS_STRING(v));
     str = JSVAL_TO_STRING(v);
 
     slot = JSVAL_TO_INT(id);
     if ((size_t)slot < str->length()) {
         str1 = JSString::getUnitString(cx, str, size_t(slot));
         if (!str1)
             return JS_FALSE;
@@ -656,17 +658,17 @@ NormalizeThis(JSContext *cx, jsval *vp)
      * js_GetPrimitiveThis seems to do a bunch of work (like calls to
      * JS_THIS_OBJECT) which we don't need in the common case (where
      * vp[1] is a String object) here.  Note that vp[1] can still be a
      * primitive value at this point.
      */
     if (!JSVAL_IS_PRIMITIVE(vp[1])) {
         JSObject *obj = JSVAL_TO_OBJECT(vp[1]);
         if (obj->getClass() == &js_StringClass) {
-            vp[1] = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+            vp[1] = obj->getPrimitiveThis();
             return JSVAL_TO_STRING(vp[1]);
         }
     }
 
     str = js_ValueToString(cx, vp[1]);
     if (!str)
         return NULL;
     vp[1] = STRING_TO_JSVAL(str);
@@ -794,17 +796,17 @@ str_substring(JSContext *cx, uintN argc,
 }
 
 #ifdef JS_TRACER
 static JSString* FASTCALL
 String_p_toString(JSContext* cx, JSObject* obj)
 {
     if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL))
         return NULL;
-    jsval v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
+    jsval v = obj->getPrimitiveThis();
     JS_ASSERT(JSVAL_IS_STRING(v));
     return JSVAL_TO_STRING(v);
 }
 #endif
 
 JSString* JS_FASTCALL
 js_toLowerCase(JSContext *cx, JSString *str)
 {
@@ -1532,17 +1534,17 @@ enum MatchControlFlags {
 static bool
 DoMatch(JSContext *cx, jsval *vp, JSString *str, const RegExpGuard &g,
         DoMatchCallback callback, void *data, MatchControlFlags flags)
 {
     if (g.re()->flags & JSREG_GLOB) {
         /* global matching ('g') */
         bool testGlobal = flags & TEST_GLOBAL_BIT;
         if (g.reobj())
-            js_ClearRegExpLastIndex(g.reobj());
+            g.reobj()->zeroRegExpLastIndex();
         for (size_t count = 0, i = 0, length = str->length(); i <= length; ++count) {
             if (!js_ExecuteRegExp(cx, g.re(), str, &i, testGlobal, vp))
                 return false;
             if (!Matched(testGlobal, *vp))
                 break;
             if (!callback(cx, count, data))
                 return false;
             if (cx->regExpStatics.lastMatch.length == 0)
@@ -2951,17 +2953,17 @@ js_String(JSContext *cx, JSObject *obj, 
         argv[0] = STRING_TO_JSVAL(str);
     } else {
         str = cx->runtime->emptyString;
     }
     if (!JS_IsConstructing(cx)) {
         *rval = STRING_TO_JSVAL(str);
         return JS_TRUE;
     }
-    obj->fslots[JSSLOT_PRIMITIVE_THIS] = STRING_TO_JSVAL(str);
+    obj->setPrimitiveThis(STRING_TO_JSVAL(str));
     return JS_TRUE;
 }
 
 #ifdef JS_TRACER
 
 JSObject* FASTCALL
 js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
 {
@@ -3046,17 +3048,17 @@ js_InitStringClass(JSContext *cx, JSObje
     if (!JS_DefineFunctions(cx, obj, string_functions))
         return NULL;
 
     proto = JS_InitClass(cx, obj, NULL, &js_StringClass, js_String, 1,
                          NULL, string_methods,
                          NULL, string_static_methods);
     if (!proto)
         return NULL;
-    proto->fslots[JSSLOT_PRIMITIVE_THIS] = STRING_TO_JSVAL(cx->runtime->emptyString);
+    proto->setPrimitiveThis(STRING_TO_JSVAL(cx->runtime->emptyString));
     if (!js_DefineNativeProperty(cx, proto, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
                                  JSVAL_VOID, NULL, NULL,
                                  JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED, 0, 0,
                                  NULL)) {
         return JS_FALSE;
     }
 
     return proto;
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -48,16 +48,17 @@
  * string descriptor.  String descriptors are GC'ed, while their chars are
  * allocated from the malloc heap.
  */
 #include <ctype.h>
 #include "jspubtd.h"
 #include "jsprvtd.h"
 #include "jshashtable.h"
 #include "jslock.h"
+#include "jsobj.h"
 
 JS_BEGIN_EXTERN_C
 
 #define JSSTRING_BIT(n)             ((size_t)1 << (n))
 #define JSSTRING_BITMASK(n)         (JSSTRING_BIT(n) - 1)
 
 enum {
     UNIT_STRING_LIMIT        = 256U,
@@ -468,16 +469,22 @@ JS_ISSPACE(jschar c)
 #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 the String class, returning its prototype object. */
 extern JSClass js_StringClass;
 
+inline bool
+JSObject::isString() const
+{
+    return getClass() == &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[];