Backed out changeset 7b2b90efe57d -- the patch was landed against a tree with a lot of orange. This will hinder the orange resolution.
authorIgor Bukanov <igor@mir2.org>
Wed, 28 Jul 2010 14:36:06 +0200
changeset 48617 74ea8e2ed2ca2af08c094f1a3f32c44ef04e8475
parent 48616 7b2b90efe57d3975d38a07af55f04bade5abf091
child 48618 a422d93d397e06eafaf274047a9dff561686f538
push id14748
push userrsayre@mozilla.com
push dateSun, 01 Aug 2010 00:33:23 +0000
treeherdermozilla-central@f0df797bb2a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b3pre
backs out7b2b90efe57d3975d38a07af55f04bade5abf091
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset 7b2b90efe57d -- the patch was landed against a tree with a lot of orange. This will hinder the orange resolution.
caps/src/nsScriptSecurityManager.cpp
content/base/src/nsContentUtils.cpp
dom/base/nsDOMWindowUtils.cpp
js/ipc/ObjectWrapperParent.cpp
js/ipc/ObjectWrapperParent.h
js/jsd/jsd_val.c
js/src/jsapi-tests/testExtendedEq.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsbool.cpp
js/src/jsbuiltins.cpp
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jslock.h
js/src/jsmath.cpp
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsregexp.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstypedarray.cpp
js/src/jsvalue.h
js/src/jswrapper.cpp
js/src/jsxml.cpp
js/src/jsxml.h
js/src/shell/js.cpp
js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
js/src/xpconnect/src/XPCChromeObjectWrapper.cpp
js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
js/src/xpconnect/src/XPCNativeWrapper.cpp
js/src/xpconnect/src/XPCNativeWrapper.h
js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp
js/src/xpconnect/src/XPCWrapper.cpp
js/src/xpconnect/src/XPCWrapper.h
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcJSWeakReference.cpp
js/src/xpconnect/src/xpccomponents.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcquickstubs.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativejsops.cpp
js/src/xpconnect/src/xpcwrappednativeproto.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -2412,17 +2412,21 @@ nsScriptSecurityManager::doGetObjectPrin
 
     do {
         // Note: jsClass is set before this loop, and also at the
         // *end* of this loop.
 
         // NOTE: These class and equality hook checks better match
         // what IS_WRAPPER_CLASS() does in xpconnect!
         
-        if (jsClass->ext.equality == js::Valueify(sXPCWrappedNativeEqualityOps)) {
+        JSEqualityOp op =
+            (jsClass->flags & JSCLASS_IS_EXTENDED) ?
+            reinterpret_cast<const JSExtendedClass*>(jsClass)->equality :
+            nsnull;
+        if (op == sXPCWrappedNativeEqualityOps) {
             result = sXPConnect->GetPrincipal(aObj,
 #ifdef DEBUG
                                               aAllowShortCircuit
 #else
                                               PR_TRUE
 #endif
                                               );
             if (result) {
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -5698,18 +5698,21 @@ CloneSimpleValues(JSContext* cx,
   // Do we support FileList?
 
   // Function objects don't get cloned.
   if (JS_ObjectIsFunction(cx, obj)) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 
   // Security wrapped objects are not allowed either.
-  if (obj->getClass()->ext.wrappedObject)
+  JSClass* clasp = JS_GET_CLASS(cx, obj);
+  if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
+      ((JSExtendedClass*)clasp)->wrappedObject) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+  }
 
   // See if this JSObject is backed by some C++ object. If it is then we assume
   // that it is inappropriate to clone.
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
   nsContentUtils::XPConnect()->
     GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrapper));
   if (wrapper) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -67,18 +67,16 @@
 #include "nsLayoutUtils.h"
 #include "nsComputedDOMStyle.h"
 
 #if defined(MOZ_X11) && defined(MOZ_WIDGET_GTK2)
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
-#include "jsobj.h"
-
 static PRBool IsUniversalXPConnectCapable()
 {
   PRBool hasCap = PR_FALSE;
   nsresult rv = nsContentUtils::GetSecurityManager()->
                   IsCapabilityEnabled("UniversalXPConnect", &hasCap);
   NS_ENSURE_SUCCESS(rv, PR_FALSE);
   return hasCap;
 }
@@ -1344,20 +1342,24 @@ nsDOMWindowUtils::GetParent()
 
   // first argument must be an object
   if(JSVAL_IS_PRIMITIVE(argv[0]))
     return NS_ERROR_XPC_BAD_CONVERT_JS;
 
   JSObject *parent = JS_GetParent(cx, JSVAL_TO_OBJECT(argv[0]));
   *rval = OBJECT_TO_JSVAL(parent);
 
-  // Outerize if necessary.
+  // Outerize if necessary.  Embrace the ugliness!
   if (parent) {
-    if (JSObjectOp outerize = parent->getClass()->ext.outerObject)
-      *rval = OBJECT_TO_JSVAL(outerize(cx, parent));
+    JSClass* clasp = JS_GET_CLASS(cx, parent);
+    if (clasp->flags & JSCLASS_IS_EXTENDED) {
+      JSExtendedClass* xclasp = reinterpret_cast<JSExtendedClass*>(clasp);
+      if (JSObjectOp outerize = xclasp->outerObject)
+        *rval = OBJECT_TO_JSVAL(outerize(cx, parent));
+    }
   }
 
   cc->SetReturnValueWasSet(PR_TRUE);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetOuterWindowID(PRUint64 *aWindowID)
--- a/js/ipc/ObjectWrapperParent.cpp
+++ b/js/ipc/ObjectWrapperParent.cpp
@@ -164,42 +164,46 @@ with_error(JSContext* cx,
                RType rval,
                const char* error = NULL)
 {
     if (!JS_IsExceptionPending(cx))
         JS_ReportError(cx, error ? error : "Unspecified CPOW error");
     return rval;
 }
 
-const js::Class ObjectWrapperParent::sCPOW_JSClass = {
-      "CrossProcessObjectWrapper",
-      JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE |
+const JSExtendedClass ObjectWrapperParent::sCPOW_JSClass = {
+    // JSClass (JSExtendedClass.base) initialization
+    { "CrossProcessObjectWrapper",
+      JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_IS_EXTENDED |
       JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(sNumSlots),
-      js::Valueify(ObjectWrapperParent::CPOW_AddProperty),
-      js::Valueify(ObjectWrapperParent::CPOW_DelProperty),
-      js::Valueify(ObjectWrapperParent::CPOW_GetProperty),
-      js::Valueify(ObjectWrapperParent::CPOW_SetProperty),
+      ObjectWrapperParent::CPOW_AddProperty,
+      ObjectWrapperParent::CPOW_DelProperty,
+      ObjectWrapperParent::CPOW_GetProperty,
+      ObjectWrapperParent::CPOW_SetProperty,
       (JSEnumerateOp) ObjectWrapperParent::CPOW_NewEnumerate,
-      (JSResolveOp) ObjectWrapperParent::CPOW_NewResolve,
-      js::Valueify(ObjectWrapperParent::CPOW_Convert),
+        (JSResolveOp) ObjectWrapperParent::CPOW_NewResolve,
+      ObjectWrapperParent::CPOW_Convert,
       ObjectWrapperParent::CPOW_Finalize,
-      nsnull, // reserved1
+      nsnull, // getObjectOps
       nsnull, // checkAccess
-      js::Valueify(ObjectWrapperParent::CPOW_Call),
-      js::Valueify(ObjectWrapperParent::CPOW_Construct),
+      ObjectWrapperParent::CPOW_Call,
+      ObjectWrapperParent::CPOW_Construct,
       nsnull, // xdrObject
-      js::Valueify(ObjectWrapperParent::CPOW_HasInstance),
+      ObjectWrapperParent::CPOW_HasInstance,
       nsnull, // mark
-      {
-          js::Valueify(ObjectWrapperParent::CPOW_Equality),
-          nsnull, // outerObject
-          nsnull, // innerObject
-          nsnull, // iteratorObject
-          nsnull, // wrappedObject
-    }
+      nsnull, // reserveSlots
+    },
+
+    // JSExtendedClass initialization
+    ObjectWrapperParent::CPOW_Equality,
+    nsnull, // outerObject
+    nsnull, // innerObject
+    nsnull, // iterator
+    nsnull, // wrappedObject
+    JSCLASS_NO_RESERVED_MEMBERS
 };
 
 void
 ObjectWrapperParent::ActorDestroy(ActorDestroyReason)
 {
     if (mObj) {
         mObj->setPrivate(NULL);
         mObj = NULL;
@@ -211,28 +215,28 @@ ObjectWrapperParent::Manager()
 {
     PContextWrapperParent* pcwp = PObjectWrapperParent::Manager();
     return static_cast<ContextWrapperParent*>(pcwp);
 }
 
 JSObject*
 ObjectWrapperParent::GetJSObject(JSContext* cx) const
 {
-    js::Class *clasp = const_cast<js::Class *>(&ObjectWrapperParent::sCPOW_JSClass);
-    if (!mObj && (mObj = JS_NewObject(cx, js::Jsvalify(clasp), NULL, NULL))) {
+    JSClass* clasp = const_cast<JSClass*>(&ObjectWrapperParent::sCPOW_JSClass.base);
+    if (!mObj && (mObj = JS_NewObject(cx, clasp, NULL, NULL))) {
         JS_SetPrivate(cx, mObj, (void*)this);
         JS_SetReservedSlot(cx, mObj, sFlagsSlot, JSVAL_ZERO);
     }
     return mObj;
 }
 
 static ObjectWrapperParent*
 Unwrap(JSContext* cx, JSObject* obj)
 {
-    while (obj->getClass() != &ObjectWrapperParent::sCPOW_JSClass)
+    while (obj->getJSClass() != &ObjectWrapperParent::sCPOW_JSClass.base)
         if (!(obj = obj->getProto()))
             return NULL;
     
     ObjectWrapperParent* self =
         static_cast<ObjectWrapperParent*>(JS_GetPrivate(cx, obj));
 
     NS_ASSERTION(!self || self->GetJSObject(cx) == obj,
                  "Wrapper and wrapped object disagree?");
--- a/js/ipc/ObjectWrapperParent.h
+++ b/js/ipc/ObjectWrapperParent.h
@@ -38,17 +38,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_jsipc_ObjectWrapperParent_h__
 #define mozilla_jsipc_ObjectWrapperParent_h__
 
 #include "mozilla/jsipc/PObjectWrapperParent.h"
 #include "jsapi.h"
-#include "jsvalue.h"
 #include "nsAutoJSValHolder.h"
 
 namespace mozilla {
 namespace jsipc {
 
 class ContextWrapperParent;
 
 class OperationChecker {
@@ -71,17 +70,17 @@ public:
 
     jsval GetJSVal(JSContext* cx) const {
         return OBJECT_TO_JSVAL(GetJSObject(cx));
     }
 
     void CheckOperation(JSContext* cx,
                         OperationStatus* status);
 
-    static const js::Class sCPOW_JSClass;
+    static const JSExtendedClass sCPOW_JSClass;
 
 protected:
 
     void ActorDestroy(ActorDestroyReason why);
 
     ContextWrapperParent* Manager();
 
 private:
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -37,17 +37,30 @@
 
 /*
  * JavaScript Debugging support - Value and Property support
  */
 
 #include "jsd.h"
 #include "jsapi.h"
 #include "jspubtd.h"
-#include "jsprvtd.h"
+
+/*
+ * Lifted with slight modification from jsobj.h
+ */
+
+#define OBJ_TO_OUTER_OBJECT(cx, obj)                                \
+do {                                                                \
+    JSClass *clasp_ = JS_GetClass(cx, obj);                         \
+    if (clasp_->flags & JSCLASS_IS_EXTENDED) {                      \
+        JSExtendedClass *xclasp_ = (JSExtendedClass*) clasp_;       \
+        if (xclasp_->outerObject)                                   \
+            obj = xclasp_->outerObject(cx, obj);                    \
+    }                                                               \
+} while(0)
 
 #ifdef DEBUG
 void JSD_ASSERT_VALID_VALUE(JSDValue* jsdval)
 {
     JS_ASSERT(jsdval);
     JS_ASSERT(jsdval->nref > 0);
     if(!JS_CLIST_IS_EMPTY(&jsdval->props))
     {
@@ -296,17 +309,18 @@ jsd_DropValue(JSDContext* jsdc, JSDValue
 jsval
 jsd_GetValueWrappedJSVal(JSDContext* jsdc, JSDValue* jsdval)
 {
     JSObject* obj;
     JSContext* cx;
     jsval val = jsdval->val;
     if (!JSVAL_IS_PRIMITIVE(val)) {
         cx = JSD_GetDefaultJSContext(jsdc);
-        obj = js_ObjectToOuterObject(cx, JSVAL_TO_OBJECT(val));
+        obj = JSVAL_TO_OBJECT(val);
+        OBJ_TO_OUTER_OBJECT(cx, obj);
         if (!obj)
         {
             JS_ClearPendingException(cx);
             val = JSVAL_NULL;
         }
         else
             val = OBJECT_TO_JSVAL(obj);
     }
--- a/js/src/jsapi-tests/testExtendedEq.cpp
+++ b/js/src/jsapi-tests/testExtendedEq.cpp
@@ -1,56 +1,42 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sw=4 et tw=99:
  *
  * This tests user-specified (via JSExtendedClass) equality operations on
  * trace.
  */
 
 #include "tests.h"
-#include "jsobj.h"
 
 static JSBool
 my_Equality(JSContext *cx, JSObject *obj, const jsval *, JSBool *bp)
 {
     *bp = JS_TRUE;
     return JS_TRUE;
 }
 
-js::Class TestExtendedEq_JSClass = {
-    "TestExtendedEq",
-    0,
-    js::PropertyStub, /* addProperty */
-    js::PropertyStub, /* delProperty */
-    js::PropertyStub, /* getProperty */
-    js::PropertyStub, /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    NULL,           /* convert */
-    NULL,           /* finalize */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    {
-        js::Valueify(my_Equality),
-        NULL, /* outerObject    */
-        NULL, /* innerObject    */
-        NULL, /* iteratorObject */
-        NULL, /* wrappedObject  */
-    }
+JSExtendedClass TestExtendedEq_JSClass = {
+    { "TestExtendedEq",
+        JSCLASS_IS_EXTENDED,
+        JS_PropertyStub,    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
+        JS_EnumerateStub,   JS_ResolveStub,    NULL,              NULL,
+        NULL,               NULL,              NULL,              NULL,
+        NULL,               NULL,              NULL,              NULL
+    },
+    // JSExtendedClass initialization
+    my_Equality,
+    NULL, NULL, NULL, NULL, JSCLASS_NO_RESERVED_MEMBERS
 };
 
 BEGIN_TEST(testExtendedEq_bug530489)
 {
     JSClass *clasp = (JSClass *) &TestExtendedEq_JSClass;
 
+    JSObject *global = JS_GetGlobalObject(cx);
     CHECK(JS_InitClass(cx, global, global, clasp, NULL, 0, NULL, NULL, NULL, NULL));
 
     CHECK(JS_DefineObject(cx, global, "obj1", clasp, NULL, 0));
     CHECK(JS_DefineObject(cx, global, "obj2", clasp, NULL, 0));
 
     jsval v;
     EVAL("(function() { var r; for (var i = 0; i < 10; ++i) r = obj1 == obj2; return r; })()", &v);
     CHECK_SAME(v, JSVAL_TRUE);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1337,20 +1337,21 @@ JS_InitStandardClasses(JSContext *cx, JS
 #if JS_HAS_GENERATORS
            js_InitIteratorClasses(cx, obj) &&
 #endif
            js_InitDateClass(cx, obj) &&
            js_InitProxyClass(cx, obj);
 }
 
 #define CLASP(name)                 (&js_##name##Class)
-#define TYPED_ARRAY_CLASP(type)     (&TypedArray::fastClasses[TypedArray::type])
+#define XCLASP(name)                (&js_##name##Class.base)
 #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 EAGER_ATOM_AND_XCLASP(name) EAGER_CLASS_ATOM(name), XCLASP(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 */
     Class       *clasp;
 } JSStdName;
@@ -1386,18 +1387,18 @@ static JSStdName standard_class_atoms[] 
     {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_InitExceptionClasses,           EAGER_ATOM_AND_CLASP(Error)},
     {js_InitRegExpClass,                EAGER_ATOM_AND_CLASP(RegExp)},
 #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)},
+    {js_InitNamespaceClass,             EAGER_ATOM_AND_XCLASP(Namespace)},
+    {js_InitQNameClass,                 EAGER_ATOM_AND_XCLASP(QName)},
 #endif
 #if JS_HAS_GENERATORS
     {js_InitIteratorClasses,            EAGER_ATOM_AND_CLASP(StopIteration)},
 #endif
     {js_InitJSONClass,                  EAGER_ATOM_AND_CLASP(JSON)},
     {js_InitTypedArrayClasses,          EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::jsclass},
     {NULL,                              0, NULL, NULL}
 };
@@ -1442,32 +1443,31 @@ static JSStdName standard_class_names[] 
 #if JS_HAS_XML_SUPPORT
     {js_InitAnyNameClass,       EAGER_ATOM_AND_CLASP(AnyName)},
     {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)},
     {js_InitXMLClass,           LAZY_ATOM(XMLList), CLASP(XML)},
     {js_InitXMLClass,           LAZY_ATOM(isXMLName), CLASP(XML)},
 #endif
 
 #if JS_HAS_GENERATORS
-    {js_InitIteratorClasses,    EAGER_ATOM_AND_CLASP(Iterator)},
-    {js_InitIteratorClasses,    EAGER_ATOM_AND_CLASP(Generator)},
+    {js_InitIteratorClasses,    EAGER_ATOM_AND_XCLASP(Iterator)},
+    {js_InitIteratorClasses,    EAGER_ATOM_AND_XCLASP(Generator)},
 #endif
 
     /* Typed Arrays */
     {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(ArrayBuffer), &js::ArrayBuffer::jsclass},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int8Array),    TYPED_ARRAY_CLASP(TYPE_INT8)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint8Array),   TYPED_ARRAY_CLASP(TYPE_UINT8)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int16Array),   TYPED_ARRAY_CLASP(TYPE_INT16)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint16Array),  TYPED_ARRAY_CLASP(TYPE_UINT16)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int32Array),   TYPED_ARRAY_CLASP(TYPE_INT32)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint32Array),  TYPED_ARRAY_CLASP(TYPE_UINT32)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Float32Array), TYPED_ARRAY_CLASP(TYPE_FLOAT32)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Float64Array), TYPED_ARRAY_CLASP(TYPE_FLOAT64)},
-    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint8ClampedArray),
-                                TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int8Array), &TypedArray::fastClasses[TypedArray::TYPE_INT8]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint8Array), &TypedArray::fastClasses[TypedArray::TYPE_UINT8]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int16Array), &TypedArray::fastClasses[TypedArray::TYPE_INT16]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint16Array), &TypedArray::fastClasses[TypedArray::TYPE_UINT16]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int32Array), &TypedArray::fastClasses[TypedArray::TYPE_INT32]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint32Array), &TypedArray::fastClasses[TypedArray::TYPE_UINT32]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Float32Array), &TypedArray::fastClasses[TypedArray::TYPE_FLOAT32]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Float64Array), &TypedArray::fastClasses[TypedArray::TYPE_FLOAT64]},
+    {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint8ClampedArray), &TypedArray::fastClasses[TypedArray::TYPE_UINT8_CLAMPED]},
 
     {js_InitProxyClass,         EAGER_ATOM_AND_CLASP(Proxy)},
 
     {NULL,                      0, NULL, NULL}
 };
 
 static JSStdName object_prototype_names[] = {
     /* Object.prototype properties (global delegates to Object.prototype). */
@@ -3702,22 +3702,18 @@ JS_DeleteProperty(JSContext *cx, JSObjec
 }
 
 JS_PUBLIC_API(void)
 JS_ClearScope(JSContext *cx, JSObject *obj)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
-    JSFinalizeOp clearOp = obj->getOps()->clear;
-    if (clearOp)
-        clearOp(cx, obj);
-
-    if (obj->isNative())
-        js_ClearNative(cx, obj);
+    if (obj->map->ops->clear)
+        obj->map->ops->clear(cx, obj);
 
     /* Clear cached class objects on the global object. */
     if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
         int key;
 
         for (key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
             JS_SetReservedSlot(cx, obj, key, JSVAL_VOID);
     }
@@ -3780,31 +3776,20 @@ prop_iter_trace(JSTracer *trc, JSObject 
         MarkIdRange(trc, ida->length, ida->vector, "prop iter");
     }
 }
 
 static Class prop_iter_class = {
     "PropertyIterator",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
     JSCLASS_MARK_IS_TRACE,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    prop_iter_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(prop_iter_trace)
+    PropertyStub,     PropertyStub,    PropertyStub,    PropertyStub,
+    EnumerateStub,    ResolveStub,     ConvertStub,     prop_iter_finalize,
+    NULL,             NULL,            NULL,            NULL,
+    NULL,             NULL,            JS_CLASS_TRACE(prop_iter_trace), NULL
 };
 
 JS_PUBLIC_API(JSObject *)
 JS_NewPropertyIterator(JSContext *cx, JSObject *obj)
 {
     JSObject *iterobj;
     JSScope *scope;
     void *pdata;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1590,44 +1590,55 @@ JS_SetScriptStackQuota(JSContext *cx, si
 
 #define JS_DEFAULT_SCRIPT_STACK_QUOTA   ((size_t) 0x2000000)
 
 /************************************************************************/
 
 /*
  * Classes, objects, and properties.
  */
-typedef void (*JSClassInternal)();
 
 /* 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. */                             \
-    JSClassInternal     reserved0;                                            \
-    JSCheckAccessOp     checkAccess;                                          \
-    JSNative            call;                                                 \
-    JSNative            construct;                                            \
-    JSXDRObjectOp       xdrObject;                                            \
-    JSHasInstanceOp     hasInstance;                                          \
+    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;
-
-    JSClassInternal     reserved1;
-    void                *reserved[19];
+    void                (*reserved0)(void);
+};
+
+struct JSExtendedClass {
+    JSClass             base;
+    JSEqualityOp        equality;
+    JSObjectOp          outerObject;
+    JSObjectOp          innerObject;
+    JSIteratorOp        iteratorObject;
+    JSObjectOp          wrappedObject;          /* NB: infallible, null
+                                                   returns are treated as
+                                                   the original object */
+    void                (*reserved0)(void);
+    void                (*reserved1)(void);
+    void                (*reserved2)(void);
 };
 
 #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 *) */
 /* (1<<4) was JSCLASS_SHARE_ALL_PROPERTIES, now obsolete. See bug 527805. */
 #define JSCLASS_NEW_RESOLVE_GETS_START  (1<<5)  /* JSNewResolveOp gets starting
@@ -1650,23 +1661,25 @@ struct JSClass {
                                          << 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)
 
-#define JSCLASS_INTERNAL_FLAG1          (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
+/* 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))
 
 /* Indicates that JSClass.mark is a tracer with JSTraceOp type. */
 #define JSCLASS_MARK_IS_TRACE           (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
-#define JSCLASS_INTERNAL_FLAG2          (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
+
+#define JSCLASS_LAST_API_FLAG_SHIFT     (JSCLASS_HIGH_FLAGS_SHIFT+3)
 
 /*
  * 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.
  *
@@ -1686,18 +1699,18 @@ struct JSClass {
 #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) ((JSProtoKey)                         \
                                          (((clasp)->flags                     \
                                            >> JSCLASS_CACHED_PROTO_SHIFT)     \
                                           & JSCLASS_CACHED_PROTO_MASK))
 
 /* Initializer for unused members of statically initialized JSClass structs. */
-#define JSCLASS_NO_INTERNAL_MEMBERS     0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
-#define JSCLASS_NO_OPTIONAL_MEMBERS     0,0,0,0,0,0,0,JSCLASS_NO_INTERNAL_MEMBERS
+#define JSCLASS_NO_OPTIONAL_MEMBERS     0,0,0,0,0,0,0,0
+#define JSCLASS_NO_RESERVED_MEMBERS     0,0,0
 
 struct JSIdArray {
     jsint length;
     jsid  vector[1];    /* actually, length jsid words */
 };
 
 extern JS_PUBLIC_API(void)
 JS_DestroyIdArray(JSContext *cx, JSIdArray *ida);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -999,63 +999,59 @@ array_trace(JSTracer *trc, JSObject *obj
     }
 
     if (IS_GC_MARKING_TRACER(trc) && holes > MIN_SPARSE_INDEX && holes > capacity / 4 * 3) {
         /* This might fail, in which case we don't slowify it. */
         static_cast<GCMarker *>(trc)->arraysToSlowify.append(obj);
     }
 }
 
+extern JSObjectOps js_ArrayObjectOps;
+
+static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS);
+
+JSObjectOps js_ArrayObjectOps = {
+    &SharedArrayMap,
+    array_lookupProperty,
+    array_defineProperty,
+    array_getProperty,
+    array_setProperty,
+    array_getAttributes,
+    array_setAttributes,
+    array_deleteProperty,
+    js_Enumerate,
+    array_typeOf,
+    array_trace,
+    NULL, /* thisObject */
+    NULL  /* clear */
+};
+
+static JSObjectOps *
+array_getObjectOps(JSContext *cx, Class *clasp)
+{
+    return &js_ArrayObjectOps;
+}
+
 Class js_ArrayClass = {
     "Array",
-    Class::NON_NATIVE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::DENSE_ARRAY_FIXED_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    js_TryValueOf,
-    array_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    JS_NULL_CLASS_EXT,
-    {
-        array_lookupProperty,
-        array_defineProperty,
-        array_getProperty,
-        array_setProperty,
-        array_getAttributes,
-        array_setAttributes,
-        array_deleteProperty,
-        NULL,       /* enumerate      */
-        array_typeOf,
-        array_trace,
-        NULL,       /* thisObject     */
-        NULL,       /* clear          */
-    }
+    PropertyStub,       PropertyStub,    PropertyStub,         PropertyStub,
+    EnumerateStub,      ResolveStub,     js_TryValueOf,        array_finalize,
+    array_getObjectOps, NULL,            NULL,                 NULL,
+    NULL,               NULL,            NULL,                 NULL
 };
 
 Class js_SlowArrayClass = {
     "Array",
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
-    slowarray_addProperty,
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    js_TryValueOf
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Array),
+    slowarray_addProperty,  PropertyStub,   PropertyStub,      PropertyStub,
+    EnumerateStub,          ResolveStub,    js_TryValueOf,     NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 JSBool
 JSObject::makeDenseArraySlow(JSContext *cx)
 {
@@ -1071,17 +1067,17 @@ JSObject::makeDenseArraySlow(JSContext *
     if (arrayProto->getClass() == &js_ObjectClass) {
         /* obj is Array.prototype. */
         emptyShape = js_GenerateShape(cx, false);
     } else {
         /* arrayProto is Array.prototype. */
         JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass);
         emptyShape = arrayProto->scope()->emptyScope->shape;
     }
-    JSScope *scope = JSScope::create(cx, &js_SlowArrayClass, obj, emptyShape);
+    JSScope *scope = JSScope::create(cx, &js_ObjectOps, &js_SlowArrayClass, obj, emptyShape);
     if (!scope)
         return JS_FALSE;
 
     uint32 capacity = obj->getDenseArrayCapacity();
 
     /* For a brief moment the object has NULL dslots until we slowify it during construction. */
     if (obj->dslots)
         obj->dslots[-1].setPrivateUint32(JS_INITIAL_NSLOTS + capacity);
@@ -2990,17 +2986,17 @@ js_NewEmptyArray(JSContext* cx, JSObject
 
     JS_ASSERT(proto->isArray());
 
     JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
     /* Initialize all fields of JSObject. */
-    obj->map = const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative);
+    obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
     obj->init(&js_ArrayClass, proto, proto->getParent(), NullValue());
     obj->setArrayLength(len);
     obj->setDenseArrayCapacity(0);
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, INT32, 0,
                      nanojit::ACCSET_STORE_ANY)
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -51,28 +51,25 @@
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 #include "jsvector.h"
 
 #include "jsobjinlines.h"
 
+
 using namespace js;
 
 Class js_BooleanClass = {
     "Boolean",
     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,  PropertyStub,  PropertyStub,  PropertyStub,
+    EnumerateStub, ResolveStub,   ConvertStub,   NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 #if JS_HAS_TOSOURCE
 #include "jsprf.h"
 
 static JSBool
 bool_toSource(JSContext *cx, uintN argc, Value *vp)
 {
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -251,17 +251,17 @@ js_AddAtomProperty(JSContext* cx, JSObje
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddAtomProperty, CONTEXT, OBJECT, SCOPEPROP,
                      0, ACCSET_STORE_ANY)
 
 static JSBool
 HasProperty(JSContext* cx, JSObject* obj, jsid id)
 {
     // Check that we know how the lookup op will behave.
     for (JSObject* pobj = obj; pobj; pobj = pobj->getProto()) {
-        if (pobj->getOps()->lookupProperty)
+        if (pobj->map->ops->lookupProperty != js_LookupProperty)
             return JS_NEITHER;
         Class* clasp = pobj->getClass();
         if (clasp->resolve != JS_ResolveStub && clasp != &js_StringClass)
             return JS_NEITHER;
     }
 
     JSObject* obj2;
     JSProperty* prop;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -480,23 +480,19 @@ msFromTime(jsdouble t)
 /*
  * Other Support routines and definitions
  */
 
 Class js_DateClass = {
     js_Date_str,
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::DATE_FIXED_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Date),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,  PropertyStub,  PropertyStub,  PropertyStub,
+    EnumerateStub, ResolveStub,   ConvertStub,   NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 /* for use by date_parse */
 
 static const char* wtb[] = {
     "am", "pm",
     "monday", "tuesday", "wednesday", "thursday", "friday",
     "saturday", "sunday",
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -83,31 +83,20 @@ exn_enumerate(JSContext *cx, JSObject *o
 static JSBool
 exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
             JSObject **objp);
 
 Class js_ErrorClass = {
     js_Error_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_MARK_IS_TRACE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    exn_enumerate,
-    (JSResolveOp)exn_resolve,
-    ConvertStub,
-    exn_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    Exception,      /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(exn_trace)
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    exn_enumerate,    (JSResolveOp)exn_resolve, ConvertStub, exn_finalize,
+    NULL,             NULL,             NULL,             Exception,
+    NULL,             NULL,             JS_CLASS_TRACE(exn_trace), NULL
 };
 
 typedef struct JSStackTraceElem {
     JSString            *funName;
     size_t              argc;
     const char          *filename;
     uintN               ulineno;
 } JSStackTraceElem;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -676,51 +676,40 @@ args_or_call_trace(JSTracer *trc, JSObje
  * in a JSStackFrame with their corresponding property values in the frame's
  * arguments object.
  */
 Class js_ArgumentsClass = {
     "Arguments",
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    PropertyStub,   /* addProperty */
-    args_delProperty,
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    args_enumerate,
-    (JSResolveOp) args_resolve,
-    ConvertStub,
-    NULL,           /* finalize   */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(args_or_call_trace)
+    PropertyStub,       args_delProperty,
+    PropertyStub,       PropertyStub,
+    args_enumerate,     (JSResolveOp) args_resolve,
+    ConvertStub,        NULL,
+    NULL,               NULL,
+    NULL,               NULL,
+    NULL,               NULL,
+    JS_CLASS_TRACE(args_or_call_trace), NULL
 };
 
 const uint32 JSSLOT_CALLEE =                    JSSLOT_PRIVATE + 1;
 const uint32 JSSLOT_CALL_ARGUMENTS =            JSSLOT_PRIVATE + 2;
 const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS =  2;
 
 /*
  * A Declarative Environment object stores its active JSStackFrame pointer in
  * its private slot, just as Call and Arguments objects do.
  */
 Class js_DeclEnvClass = {
     js_Object_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,      NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 static JSBool
 CheckForEscapingClosure(JSContext *cx, JSObject *obj, Value *vp)
 {
     JS_ASSERT(obj->getClass() == &js_CallClass ||
               obj->getClass() == &js_DeclEnvClass);
 
@@ -1268,31 +1257,24 @@ call_resolve(JSContext *cx, JSObject *ob
     return JS_TRUE;
 }
 
 JS_PUBLIC_DATA(Class) js_CallClass = {
     "Call",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) |
     JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    call_enumerate,
-    (JSResolveOp)call_resolve,
-    NULL,           /* convert */
-    NULL,           /* finalize */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(args_or_call_trace)
+    PropertyStub,       PropertyStub,
+    PropertyStub,       PropertyStub,
+    call_enumerate,     (JSResolveOp)call_resolve,
+    NULL,               NULL,
+    NULL,               NULL,
+    NULL,               NULL,
+    NULL,               NULL,
+    JS_CLASS_TRACE(args_or_call_trace), NULL
 };
 
 bool
 JSStackFrame::getValidCalleeObject(JSContext *cx, Value *vp)
 {
     if (!fun) {
         *vp = argv ? argv[-2] : UndefinedValue();
         return true;
@@ -1918,31 +1900,24 @@ JSFunction::countInterpretedReservedSlot
  * does not bloat every instance, only those on which reserved slots are set,
  * and those on which ad-hoc properties are defined.
  */
 JS_PUBLIC_DATA(Class) js_FunctionClass = {
     js_Function_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::FUN_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    fun_enumerate,
-    (JSResolveOp)fun_resolve,
-    ConvertStub,
-    fun_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    js_XDRFunctionObject,
-    fun_hasInstance,
-    JS_CLASS_TRACE(fun_trace)
+    PropertyStub,     PropertyStub,
+    PropertyStub,     PropertyStub,
+    fun_enumerate,    (JSResolveOp)fun_resolve,
+    ConvertStub,      fun_finalize,
+    NULL,             NULL,
+    NULL,             NULL,
+    js_XDRFunctionObject, fun_hasInstance,
+    JS_CLASS_TRACE(fun_trace), NULL
 };
 
 namespace js {
 
 JSString *
 fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent)
 {
     if (!obj->isFunction()) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1850,18 +1850,17 @@ JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
 {
     switch (kind) {
       case JSTRACE_OBJECT: {
         /* If obj has no map, it must be a newborn. */
         JSObject *obj = (JSObject *) thing;
         if (!obj->map)
             break;
-        JSTraceOp op = obj->getOps()->trace;
-        (op ? op : js_TraceObject)(trc, obj);
+        obj->map->ops->trace(trc, obj);
         break;
       }
 
       case JSTRACE_STRING: {
         JSString *str = (JSString *) thing;
         if (str->isDependent())
             JS_CALL_STRING_TRACER(trc, str->dependentBase(), "base");
         else if (str->isRope()) {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -331,23 +331,19 @@ ComputeThisFromArgv(JSContext *cx, Value
 #if JS_HAS_NO_SUCH_METHOD
 
 const uint32 JSSLOT_FOUND_FUNCTION  = JSSLOT_PRIVATE;
 const uint32 JSSLOT_SAVED_ID        = JSSLOT_PRIVATE + 1;
 
 Class js_NoSuchMethodClass = {
     "NoSuchMethod",
     JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
+    PropertyStub,     PropertyStub,     PropertyStub,      PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,       NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
 /*
  * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
  * the base object, we search for the __noSuchMethod__ method in the base.
  * If it exists, we store the method and the property's id into an object of
  * NoSuchMethod class and store this object into the callee's stack slot.
  * Later, js_Invoke will recognise such an object and transfer control to
@@ -619,17 +615,17 @@ DoSlowCall(JSContext *cx, uintN argc, Va
     JSStackFrame *fp = cx->fp;
     JSObject *obj = fp->getThisObject(cx);
     if (!obj)
         return false;
     JS_ASSERT(ObjectValue(*obj) == fp->thisv);
 
     JSObject *callee = &JS_CALLEE(cx, vp).toObject();
     Class *clasp = callee->getClass();
-    JS_ASSERT(!(clasp->flags & Class::CALL_IS_FAST));
+    JS_ASSERT(!(clasp->flags & CLASS_CALL_IS_FAST));
     if (!clasp->call) {
         js_ReportIsNotFunction(cx, &vp[0], 0);
         return JS_FALSE;
     }
     AutoValueRooter rval(cx);
     JSBool ok = clasp->call(cx, obj, argc, JS_ARGV(cx, vp), rval.addr());
     if (ok)
         JS_SET_RVAL(cx, vp, rval.value());
@@ -722,17 +718,17 @@ Invoke(JSContext *cx, const InvokeArgsGu
     /* Try a call or construct native object op. */
     if (flags & JSINVOKE_CONSTRUCT) {
         if (!vp[1].isObjectOrNull()) {
             if (!js_PrimitiveToObject(cx, &vp[1]))
                 return false;
         }
         return InvokeCommon(cx, NULL, NULL, DoConstruct, args, flags);
     }
-    CallOp callOp = (clasp->flags & Class::CALL_IS_FAST) ? (CallOp) clasp->call : DoSlowCall;
+    CallOp callOp = (clasp->flags & CLASS_CALL_IS_FAST) ? (CallOp) clasp->call : DoSlowCall;
     return InvokeCommon(cx, NULL, NULL, callOp, args, flags);
 }
 
 extern JS_REQUIRES_STACK JS_FRIEND_API(bool)
 InvokeFriendAPI(JSContext *cx, const InvokeArgsGuard &args, uintN flags)
 {
     return Invoke(cx, args, flags);
 }
@@ -872,17 +868,17 @@ Execute(JSContext *cx, JSObject *chain, 
         if (!innerizedChain)
             return false;
         fp->scopeChain = innerizedChain;
 
         initialVarObj = (cx->options & JSOPTION_VAROBJFIX)
                         ? chain->getGlobal()
                         : chain;
     }
-    JS_ASSERT(!initialVarObj->getOps()->defineProperty);
+    JS_ASSERT(initialVarObj->map->ops->defineProperty == js_DefineProperty);
 
     fp->script = script;
     fp->imacpc = NULL;
     fp->rval.setUndefined();
     fp->blockChain = NULL;
 
     /* Initialize regs. */
     regs.pc = script->code;
@@ -1111,17 +1107,17 @@ TypeOfValue(JSContext *cx, const Value &
         return JSTYPE_NUMBER;
     if (v.isString())
         return JSTYPE_STRING;
     if (v.isNull())
         return JSTYPE_OBJECT;
     if (v.isUndefined())
         return JSTYPE_VOID;
     if (v.isObject())
-        return v.toObject().typeOf(cx);
+        return v.toObject().map->ops->typeOf(cx, &v.toObject());
     JS_ASSERT(v.isBoolean());
     return JSTYPE_BOOLEAN;
 }
 
 bool
 InstanceOfSlow(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
 {
     JS_ASSERT(!obj || obj->getClass() != clasp);
@@ -2023,31 +2019,31 @@ JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == 
 
 /*
  * Inline fast paths for iteration. js_IteratorMore and js_IteratorNext handle
  * all cases, but we inline the most frequently taken paths here.
  */
 static inline bool
 IteratorMore(JSContext *cx, JSObject *iterobj, bool *cond, Value *rval)
 {
-    if (iterobj->getClass() == &js_IteratorClass) {
+    if (iterobj->getClass() == &js_IteratorClass.base) {
         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
         *cond = (ni->props_cursor < ni->props_end);
     } else {
         if (!js_IteratorMore(cx, iterobj, rval))
             return false;
         *cond = rval->isTrue();
     }
     return true;
 }
 
 static inline bool
 IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
 {
-    if (iterobj->getClass() == &js_IteratorClass) {
+    if (iterobj->getClass() == &js_IteratorClass.base) {
         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
         JS_ASSERT(ni->props_cursor < ni->props_end);
         if (ni->isKeyIter()) {
             jsid id = *ni->currentKey();
             if (JSID_IS_ATOM(id)) {
                 rval->setString(JSID_TO_STRING(id));
                 ni->incKeyCursor();
                 return true;
@@ -3213,28 +3209,30 @@ END_CASE(JSOP_BITAND)
     if ((lval.isObject() && lval.toObject().isXML()) ||                       \
         (rval.isObject() && rval.toObject().isXML())) {                       \
         if (!js_TestXMLEquality(cx, lval, rval, &cond))                       \
             goto error;                                                       \
         cond = cond OP JS_TRUE;                                               \
     } else
 
 #define EXTENDED_EQUALITY_OP(OP)                                              \
-    if (EqualityOp eq = l->getClass()->ext.equality) {                        \
-        if (!eq(cx, l, &rval, &cond))                                         \
+    if (((clasp = l->getClass())->flags & JSCLASS_IS_EXTENDED) &&             \
+        ((ExtendedClass *)clasp)->equality) {                                 \
+        if (!((ExtendedClass *)clasp)->equality(cx, l, &rval, &cond))         \
             goto error;                                                       \
         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                                                            \
+        Class *clasp;                                                         \
         JSBool cond;                                                          \
         Value rval = regs.sp[-1];                                             \
         Value lval = regs.sp[-2];                                             \
         XML_EQUALITY_OP(OP)                                                   \
         if (SameType(lval, rval)) {                                           \
             if (lval.isString()) {                                            \
                 JSString *l = lval.toString(), *r = rval.toString();          \
                 cond = js_EqualStrings(l, r) OP JS_TRUE;                      \
@@ -4043,17 +4041,17 @@ BEGIN_CASE(JSOP_GETXPROP)
                     NATIVE_GET(cx, obj, obj2, sprop,
                                fp->imacpc ? JSGET_NO_METHOD_BARRIER : JSGET_METHOD_BARRIER,
                                &rval);
                 }
                 break;
             }
 
             jsid id = ATOM_TO_JSID(atom);
-            if (JS_LIKELY(!aobj->getOps()->getProperty)
+            if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
                 ? !js_GetPropertyHelper(cx, obj, id,
                                         (fp->imacpc ||
                                          regs.pc[JSOP_GETPROP_LENGTH + i] == JSOP_IFEQ)
                                         ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                                         : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER,
                                         &rval)
                 : !obj->getProperty(cx, id, &rval)) {
                 goto error;
@@ -4147,26 +4145,26 @@ BEGIN_CASE(JSOP_CALLPROP)
      * PropertyCache::test.
      */
     jsid id;
     id = ATOM_TO_JSID(atom);
 
     PUSH_NULL();
     if (lval.isObject()) {
         if (!js_GetMethod(cx, &objv.toObject(), id,
-                          JS_LIKELY(!aobj->getOps()->getProperty)
+                          JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)
                           ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                           : JSGET_NO_METHOD_BARRIER,
                           &rval)) {
             goto error;
         }
         regs.sp[-1] = objv;
         regs.sp[-2] = rval;
     } else {
-        JS_ASSERT(!objv.toObject().getOps()->getProperty);
+        JS_ASSERT(objv.toObject().map->ops->getProperty == js_GetProperty);
         if (!js_GetPropertyHelper(cx, &objv.toObject(), id,
                                   JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER,
                                   &rval)) {
             goto error;
         }
         regs.sp[-1] = lval;
         regs.sp[-2] = rval;
     }
@@ -4380,17 +4378,17 @@ BEGIN_CASE(JSOP_SETMETHOD)
             }
             if (sprop)
                 break;
         }
 
         if (!atom)
             LOAD_ATOM(0, atom);
         jsid id = ATOM_TO_JSID(atom);
-        if (entry && JS_LIKELY(!obj->getOps()->setProperty)) {
+        if (entry && JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
             uintN defineHow;
             if (op == JSOP_SETMETHOD)
                 defineHow = JSDNP_CACHE_RESULT | JSDNP_SET_METHOD;
             else if (op == JSOP_SETNAME)
                 defineHow = JSDNP_CACHE_RESULT | JSDNP_UNQUALIFIED;
             else
                 defineHow = JSDNP_CACHE_RESULT;
             if (!js_SetPropertyHelper(cx, obj, id, defineHow, &rval))
@@ -5354,17 +5352,17 @@ BEGIN_CASE(JSOP_DEFVAR)
     JSAtom *atom = atoms[index];
 
     /*
      * index is relative to atoms at this point but for global var
      * code below we need the absolute value.
      */
     index += atoms - script->atomMap.vector;
     JSObject *obj = fp->varobj(cx);
-    JS_ASSERT(!obj->getOps()->defineProperty);
+    JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
     uintN 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. */
     jsid id = ATOM_TO_JSID(atom);
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -80,41 +80,29 @@
 #include "jsstrinlines.h"
 
 using namespace js;
 
 static void iterator_finalize(JSContext *cx, JSObject *obj);
 static void iterator_trace(JSTracer *trc, JSObject *obj);
 static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
 
-Class js_IteratorClass = {
-    "Iterator",
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) | JSCLASS_MARK_IS_TRACE,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    iterator_finalize,
-    NULL,           /* reserved    */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(iterator_trace),
-    {
-        NULL,       /* equality       */
-        NULL,       /* outerObject    */
-        NULL,       /* innerObject    */
-        iterator_iterator,
-        NULL        /* wrappedObject  */
-    }
+ExtendedClass js_IteratorClass = {
+  { "Iterator",
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
+    JSCLASS_MARK_IS_TRACE |
+    JSCLASS_IS_EXTENDED,
+    PropertyStub,     PropertyStub,    PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,     ConvertStub,      iterator_finalize,
+    NULL,             NULL,            NULL,             NULL,
+    NULL,             NULL,            JS_CLASS_TRACE(iterator_trace), NULL },
+    NULL,             NULL,            NULL,             iterator_iterator,
+    NULL,
+    JSCLASS_NO_RESERVED_MEMBERS
 };
 
 void
 NativeIterator::mark(JSTracer *trc)
 {
     if (isKeyIter())
         MarkIdRange(trc, beginKey(), endKey(), "props");
     else
@@ -125,17 +113,17 @@ NativeIterator::mark(JSTracer *trc)
 
 /*
  * Shared code to close iterator's state either through an explicit call or
  * when GC detects that the iterator is no longer reachable.
  */
 static void
 iterator_finalize(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(obj->getClass() == &js_IteratorClass);
+    JS_ASSERT(obj->getClass() == &js_IteratorClass.base);
 
     /* Avoid double work if the iterator was closed by JSOP_ENDITER. */
     NativeIterator *ni = obj->getNativeIterator();
     if (ni) {
         cx->free(ni);
         obj->setNativeIterator(NULL);
     }
 }
@@ -315,17 +303,17 @@ Snapshot(JSContext *cx, JSObject *obj, u
     IdSet ht(cx);
     if (!ht.init(32))
         return NULL;
 
     JSObject *pobj = obj;
     do {
         Class *clasp = pobj->getClass();
         if (pobj->isNative() &&
-            !pobj->getOps()->enumerate &&
+            pobj->map->ops->enumerate == js_Enumerate &&
             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
             if (!clasp->enumerate(cx, pobj))
                 return false;
             if (!EnumerateNativeProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
                 return false;
         } else if (pobj->isDenseArray()) {
             if (!EnumerateDenseArrayProperties<EnumPolicy>(cx, obj, pobj, flags, ht, props))
                 return false;
@@ -456,21 +444,21 @@ NewIteratorObject(JSContext *cx, uintN f
          * are not stillborn, with the exception of "NoSuchMethod" internal
          * helper objects) expect it to have a non-null map pointer, so we
          * share an empty Enumerator scope in the runtime.
          */
         JSObject *obj = js_NewGCObject(cx);
         if (!obj)
             return false;
         obj->map = cx->runtime->emptyEnumeratorScope->hold();
-        obj->init(&js_IteratorClass, NULL, NULL, NullValue());
+        obj->init(&js_IteratorClass.base, NULL, NULL, NullValue());
         return obj;
     }
 
-    return NewBuiltinClassInstance(cx, &js_IteratorClass);
+    return NewBuiltinClassInstance(cx, &js_IteratorClass.base);
 }
 
 NativeIterator *
 NativeIterator::allocateKeyIterator(JSContext *cx, uint32 slength, const AutoIdVector &props)
 {
     size_t plength = props.length();
     NativeIterator *ni = (NativeIterator *)
         cx->malloc(sizeof(NativeIterator) + plength * sizeof(jsid) + slength * sizeof(uint32));
@@ -617,17 +605,17 @@ GetIterator(JSContext *cx, JSObject *obj
              * The iterator object for JSITER_ENUMERATE never escapes, so we
              * don't care for the proper parent/proto to be set. This also
              * allows us to re-use a previous iterator object that was freed
              * by JSOP_ENDITER.
              */
             JSObject *pobj = obj;
             do {
                 if (!pobj->isNative() ||
-                    obj->getOps()->enumerate ||
+                    obj->map->ops->enumerate != js_Enumerate ||
                     pobj->getClass()->enumerate != JS_EnumerateStub) {
                     shapes.clear();
                     goto miss;
                 }
                 uint32 shape = pobj->shape();
                 key = (key + (key << 16)) ^ shape;
                 if (!shapes.append(shape))
                     return false;
@@ -705,17 +693,17 @@ js_ThrowStopIteration(JSContext *cx)
 }
 
 static JSBool
 iterator_next(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj;
 
     obj = ComputeThisFromVp(cx, vp);
-    if (!InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
+    if (!InstanceOf(cx, obj, &js_IteratorClass.base, vp + 2))
         return false;
 
     if (!js_IteratorMore(cx, obj, vp))
         return false;
     if (!vp->toBoolean()) {
         js_ThrowStopIteration(cx);
         return false;
     }
@@ -767,41 +755,45 @@ js_ValueToIterator(JSContext *cx, uintN 
             obj = js_ValueToNonNullObject(cx, *vp);
             if (!obj)
                 return false;
         }
     }
 
     AutoObjectRooter tvr(cx, obj);
 
-    /* Enumerate Iterator.prototype directly. */
-    JSIteratorOp op = obj->getClass()->ext.iteratorObject;
-    if (op && (obj->getClass() != &js_IteratorClass || obj->getNativeIterator())) {
-        JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
-        if (!iterobj)
-            return false;
-        vp->setObject(*iterobj);
-        return true;
+    Class *clasp = obj->getClass();
+    ExtendedClass *xclasp;
+    if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
+        (xclasp = (ExtendedClass *) clasp)->iteratorObject) {
+        /* Enumerate Iterator.prototype directly. */
+        if (clasp != &js_IteratorClass.base || obj->getNativeIterator()) {
+            JSObject *iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
+            if (!iterobj)
+                return false;
+            vp->setObject(*iterobj);
+            return true;
+        }
     }
 
     return GetIterator(cx, obj, flags, vp);
 }
 
 #if JS_HAS_GENERATORS
 static JS_REQUIRES_STACK JSBool
 CloseGenerator(JSContext *cx, JSObject *genobj);
 #endif
 
 JS_FRIEND_API(JSBool)
 js_CloseIterator(JSContext *cx, JSObject *obj)
 {
     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
 
     Class *clasp = obj->getClass();
-    if (clasp == &js_IteratorClass) {
+    if (clasp == &js_IteratorClass.base) {
         /* Remove enumerators from the active list, which is a stack. */
         NativeIterator *ni = obj->getNativeIterator();
         if (ni->flags & JSITER_ENUMERATE) {
             JS_ASSERT(cx->enumerators == obj);
             cx->enumerators = ni->next;
         }
 
         /* Cache the iterator object if possible. */
@@ -811,17 +803,17 @@ js_CloseIterator(JSContext *cx, JSObject
             ni->props_cursor = ni->props_array;
             ni->next = *hp;
             *hp = obj;
         } else {
             iterator_finalize(cx, obj);
         }
     }
 #if JS_HAS_GENERATORS
-    else if (clasp == &js_GeneratorClass) {
+    else if (clasp == &js_GeneratorClass.base) {
         return CloseGenerator(cx, obj);
     }
 #endif
     return JS_TRUE;
 }
 
 /*
  * Suppress enumeration of deleted properties. We maintain a list of all active
@@ -896,17 +888,17 @@ js_SuppressDeletedProperty(JSContext *cx
     }
     return true;
 }
 
 JSBool
 js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
 {
     /* Fast path for native iterators */
-    if (iterobj->getClass() == &js_IteratorClass) {
+    if (iterobj->getClass() == &js_IteratorClass.base) {
         /*
          * Implement next directly as all the methods of native iterator are
          * read-only and permanent.
          */
         NativeIterator *ni = iterobj->getNativeIterator();
         rval->setBoolean(ni->props_cursor < ni->props_end);
         return true;
     }
@@ -940,17 +932,17 @@ js_IteratorMore(JSContext *cx, JSObject 
     rval->setBoolean(true);
     return true;
 }
 
 JSBool
 js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
 {
     /* Fast path for native iterators */
-    if (iterobj->getClass() == &js_IteratorClass) {
+    if (iterobj->getClass() == &js_IteratorClass.base) {
         /*
          * Implement next directly as all the methods of the native iterator are
          * read-only and permanent.
          */
         NativeIterator *ni = iterobj->getNativeIterator();
         JS_ASSERT(ni->props_cursor < ni->props_end);
         if (ni->isKeyIter()) {
             *rval = IdToValue(*ni->currentKey());
@@ -989,30 +981,24 @@ stopiter_hasInstance(JSContext *cx, JSOb
 {
     *bp = js_ValueIsStopIteration(*v);
     return JS_TRUE;
 }
 
 Class js_StopIterationClass = {
     js_StopIteration_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    NULL,           /* finalize    */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    stopiter_hasInstance
+    PropertyStub,     PropertyStub,
+    PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,
+    ConvertStub,      NULL,
+    NULL,             NULL,
+    NULL,             NULL,
+    NULL,             stopiter_hasInstance,
+    NULL,             NULL
 };
 
 #if JS_HAS_GENERATORS
 
 static void
 generator_finalize(JSContext *cx, JSObject *obj)
 {
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
@@ -1045,56 +1031,44 @@ generator_trace(JSTracer *trc, JSObject 
 
     JSStackFrame *fp = gen->getFloatingFrame();
     JS_ASSERT(gen->getLiveFrame() == fp);
     MarkValueRange(trc, gen->floatingStack, fp->argEnd(), "generator slots");
     js_TraceStackFrame(trc, fp);
     MarkValueRange(trc, fp->slots(), gen->savedRegs.sp, "generator slots");
 }
 
-Class js_GeneratorClass = {
-    js_Generator_str,
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
-    JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    generator_finalize,
-    NULL,           /* reserved    */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(generator_trace),
-    {
-        NULL,       /* equality       */
-        NULL,       /* outerObject    */
-        NULL,       /* innerObject    */
-        iterator_iterator,
-        NULL,       /* wrappedObject  */
-    }
+ExtendedClass js_GeneratorClass = {
+  { js_Generator_str,
+    JSCLASS_HAS_PRIVATE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
+    JSCLASS_IS_ANONYMOUS |
+    JSCLASS_MARK_IS_TRACE |
+    JSCLASS_IS_EXTENDED,
+    PropertyStub,     PropertyStub,    PropertyStub,    PropertyStub,
+    EnumerateStub,    ResolveStub,     ConvertStub,     generator_finalize,
+    NULL,             NULL,            NULL,            NULL,
+    NULL,             NULL,            JS_CLASS_TRACE(generator_trace), NULL },
+    NULL,             NULL,            NULL,            iterator_iterator,
+    NULL,
+    JSCLASS_NO_RESERVED_MEMBERS
 };
 
 /*
  * 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.
  */
 JS_REQUIRES_STACK JSObject *
 js_NewGenerator(JSContext *cx)
 {
-    JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass);
+    JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass.base);
     if (!obj)
         return NULL;
 
     /* Load and compute stack slot counts. */
     JSStackFrame *fp = cx->fp;
     uintN argc = fp->argc;
     uintN nargs = JS_MAX(argc, fp->fun->nargs);
     uintN vplen = 2 + nargs;
@@ -1326,17 +1300,17 @@ SendToGenerator(JSContext *cx, JSGenerat
      * Propagate the condition to the caller.
      */
     return JS_FALSE;
 }
 
 static JS_REQUIRES_STACK JSBool
 CloseGenerator(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(obj->getClass() == &js_GeneratorClass);
+    JS_ASSERT(obj->getClass() == &js_GeneratorClass.base);
 
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
     if (!gen) {
         /* Generator prototype object. */
         return JS_TRUE;
     }
 
     if (gen->state == JSGEN_CLOSED)
@@ -1350,17 +1324,17 @@ CloseGenerator(JSContext *cx, JSObject *
  */
 static JSBool
 generator_op(JSContext *cx, JSGeneratorOp op, Value *vp, uintN argc)
 {
     JSObject *obj;
     LeaveTrace(cx);
 
     obj = ComputeThisFromVp(cx, vp);
-    if (!InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
+    if (!InstanceOf(cx, obj, &js_GeneratorClass.base, vp + 2))
         return JS_FALSE;
 
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
     if (!gen) {
         /* This happens when obj is the generator prototype. See bug 352885. */
         goto closed_generator;
     }
 
@@ -1445,24 +1419,24 @@ js_InitIteratorClasses(JSContext *cx, JS
     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,
+    proto = js_InitClass(cx, obj, NULL, &js_IteratorClass.base, Iterator, 2,
                          NULL, iterator_methods, NULL, NULL);
     if (!proto)
         return NULL;
 
 #if JS_HAS_GENERATORS
     /* Initialize the generator internals if configured. */
-    if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
+    if (!js_InitClass(cx, obj, NULL, &js_GeneratorClass.base, NULL, 0,
                       NULL, generator_methods, NULL, NULL)) {
         return NULL;
     }
 #endif
 
     return js_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
                         NULL, NULL, NULL, NULL);
 }
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -244,19 +244,19 @@ js_LiveFrameIfGenerator(JSStackFrame *fp
 {
     if (fp->flags & JSFRAME_GENERATOR)
         return js_FloatingFrameToGenerator(fp)->getLiveFrame();
     return fp;
 }
 
 #endif
 
-extern js::Class js_GeneratorClass;
-extern js::Class js_IteratorClass;
-extern js::Class js_StopIterationClass;
+extern js::ExtendedClass js_GeneratorClass;
+extern js::ExtendedClass js_IteratorClass;
+extern js::Class         js_StopIterationClass;
 
 static inline bool
 js_ValueIsStopIteration(const js::Value &v)
 {
     return v.isObject() && v.toObject().getClass() == &js_StopIterationClass;
 }
 
 extern JSObject *
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -145,17 +145,19 @@ struct JSTitle {
 #define JS_UNLOCK(cx, tl)           js_Unlock(cx, tl)
 
 #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->isNative() returns true).  All uses of these macros in
- * the engine are predicated on obj->isNative or equivalent checks.
+ * the engine are predicated on obj->isNative or equivalent checks.  These uses
+ * are for optimizations above the JSObjectOps layer, under which object locks
+ * normally hide.
  */
 #define CX_OWNS_SCOPE_TITLE(cx,scope)   ((scope)->title.ownercx == (cx))
 
 #define JS_LOCK_OBJ(cx,obj)                                                   \
     JS_BEGIN_MACRO                                                            \
         JSObject *obj_ = (obj);                                               \
         if (!CX_OWNS_SCOPE_TITLE(cx, obj_->scope())) {                        \
             js_LockObj(cx, obj_);                                             \
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -92,23 +92,19 @@ static JSConstDoubleSpec math_constants[
     {M_SQRT2,   "SQRT2",        0, {0,0,0}},
     {M_SQRT1_2, "SQRT1_2",      0, {0,0,0}},
     {0,0,0,{0,0,0}}
 };
 
 Class js_MathClass = {
     js_Math_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,      NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 static JSBool
 math_abs(JSContext *cx, uintN argc, Value *vp)
 {
     jsdouble x, z;
 
     if (argc == 0) {
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -266,23 +266,19 @@ static JSFunctionSpec number_functions[]
     JS_TN(js_parseFloat_str,    num_parseFloat,      1,0, &num_parseFloat_trcinfo),
     JS_TN(js_parseInt_str,      num_parseInt,        2,0, &num_parseInt_trcinfo),
     JS_FS_END
 };
 
 Class js_NumberClass = {
     js_Number_str,
     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Number),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,      NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 static JSBool
 Number(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     if (argc != 0) {
         if (!ValueToNumber(cx, &argv[0]))
             return JS_FALSE;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -97,37 +97,40 @@
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 
-JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS);
+JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
+    NULL,
+    js_LookupProperty,
+    js_DefineProperty,
+    js_GetProperty,
+    js_SetProperty,
+    js_GetAttributes,
+    js_SetAttributes,
+    js_DeleteProperty,
+    js_Enumerate,
+    js_TypeOf,
+    js_TraceObject,
+    NULL,   /* thisObject */
+    js_Clear
+};
 
 Class js_ObjectClass = {
     js_Object_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,      NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
-JS_FRIEND_API(JSObject *)
-js_ObjectToOuterObject(JSContext *cx, JSObject *obj)
-{
-    OBJ_TO_OUTER_OBJECT(cx, obj);
-    return obj;
-}
-
 #if JS_HAS_OBJ_PROTO_PROP
 
 static JSBool
 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 
 static JSBool
 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 
@@ -909,31 +912,40 @@ js_CheckPrincipalsAccess(JSContext *cx, 
         }
     }
     return JS_TRUE;
 }
 
 JSObject *
 js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
 {
+    Class *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. */
-    inner = scopeobj;
     while (scopeobj) {
-        JSObjectOp op = scopeobj->getClass()->ext.innerObject;
-        if (op && op(cx, scopeobj) != scopeobj)
-            goto bad;
+        clasp = scopeobj->getClass();
+        if (clasp->flags & JSCLASS_IS_EXTENDED) {
+            xclasp = (JSExtendedClass*)clasp;
+            if (xclasp->innerObject &&
+                xclasp->innerObject(cx, scopeobj) != scopeobj) {
+                goto bad;
+            }
+        }
+
         scopeobj = scopeobj->getParent();
     }
 
     return inner;
 
 bad:
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                          JSMSG_BAD_INDIRECT_CALL, caller);
@@ -1350,17 +1362,17 @@ obj_unwatch(JSContext *cx, uintN argc, V
  */
 
 /* Proposed ECMA 15.2.4.5. */
 static JSBool
 obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     return obj &&
-           js_HasOwnPropertyHelper(cx, obj->getOps()->lookupProperty, argc, vp);
+           js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
 }
 
 JSBool
 js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
                         Value *vp)
 {
     jsid id;
     if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
@@ -1389,28 +1401,32 @@ js_HasOwnPropertyHelper(JSContext *cx, J
     return JS_TRUE;
 }
 
 JSBool
 js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
                   JSObject **objp, JSProperty **propp)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
-    if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
+    if (!lookup(cx, obj, id, objp, propp))
         return false;
     if (!*propp)
         return true;
 
     if (*objp == obj)
         return true;
 
+    JSExtendedClass *xclasp;
+    JSObject *outer;
     Class *clasp = (*objp)->getClass();
-    JSObject *outer = NULL;
-    if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
-        outer = op(cx, *objp);
+    if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
+        !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
+        outer = NULL;
+    } else {
+        outer = xclasp->outerObject(cx, *objp);
         if (!outer)
             return false;
     }
 
     if (outer != *objp) {
         if ((*objp)->isNative() && obj->getClass() == clasp) {
             /*
              * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
@@ -1686,17 +1702,17 @@ js_GetOwnPropertyDescriptor(JSContext *c
 {
     if (obj->isProxy()) {
         if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, vp))
             return false;
     }
 
     JSObject *pobj;
     JSProperty *prop;
-    if (!js_HasOwnProperty(cx, obj->getOps()->lookupProperty, obj, id, &pobj, &prop))
+    if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &pobj, &prop))
         return false;
     if (!prop) {
         vp->setUndefined();
         return true;
     }
 
     Value roots[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
     AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
@@ -1962,32 +1978,32 @@ Reject(JSContext *cx, JSObject *obj, JSP
 
 static JSBool
 DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
                        bool throwError, bool *rval)
 {
     /* 8.12.9 step 1. */
     JSProperty *current;
     JSObject *obj2;
-    JS_ASSERT(!obj->getOps()->lookupProperty);
-    if (!js_HasOwnProperty(cx, NULL, obj, desc.id, &obj2, &current))
+    JS_ASSERT(obj->map->ops->lookupProperty == js_LookupProperty);
+    if (!js_HasOwnProperty(cx, js_LookupProperty, obj, desc.id, &obj2, &current))
         return JS_FALSE;
 
-    JS_ASSERT(!obj->getOps()->defineProperty);
+    JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
 
     /* 8.12.9 steps 2-4. */
     JSScope *scope = obj->scope();
     if (!current) {
         if (scope->sealed())
             return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
 
         *rval = true;
 
         if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
-            JS_ASSERT(!obj->getOps()->defineProperty);
+            JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
             return js_DefineProperty(cx, obj, desc.id, &desc.value,
                                      PropertyStub, PropertyStub, desc.attrs);
         }
 
         JS_ASSERT(desc.isAccessorDescriptor());
 
         /*
          * Getters and setters are just like watchpoints from an access
@@ -2282,17 +2298,17 @@ DefinePropertyOnArray(JSContext *cx, JSO
 
 static JSBool
 DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwError,
                bool *rval)
 {
     if (obj->isArray())
         return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
 
-    if (obj->getOps()->lookupProperty) {
+    if (obj->map->ops->lookupProperty != js_LookupProperty) {
         if (obj->isProxy())
             return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
         return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
     }
 
     return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
 }
 
@@ -2563,17 +2579,18 @@ js_Object(JSContext *cx, JSObject *obj, 
 }
 
 #ifdef JS_TRACER
 
 JSObject*
 js_NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
                            const Value &privateSlotValue)
 {
-    JS_ASSERT(clasp->isNative());
+    JS_ASSERT(!clasp->getObjectOps);
+    JS_ASSERT(proto->map->ops == &js_ObjectOps);
 
     JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
     obj->initSharingEmptyScope(clasp, proto, proto->getParent(), privateSlotValue);
     return obj;
 }
@@ -2843,49 +2860,45 @@ with_TypeOf(JSContext *cx, JSObject *obj
 }
 
 static JSObject *
 with_ThisObject(JSContext *cx, JSObject *obj)
 {
     return obj->getWithThis();
 }
 
+JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
+    NULL,
+    with_LookupProperty,
+    js_DefineProperty,
+    with_GetProperty,
+    with_SetProperty,
+    with_GetAttributes,
+    with_SetAttributes,
+    with_DeleteProperty,
+    with_Enumerate,
+    with_TypeOf,
+    js_TraceObject,
+    with_ThisObject,
+    js_Clear
+};
+
+static JSObjectOps *
+with_getObjectOps(JSContext *cx, Class *clasp)
+{
+    return &js_WithObjectOps;
+}
+
 Class js_WithClass = {
     "With",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    NULL,           /* finalize */
-    NULL,           /* reserved    */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    JS_NULL_CLASS_EXT,
-    {
-        with_LookupProperty,
-        NULL,       /* defineProperty */
-        with_GetProperty,
-        with_SetProperty,
-        with_GetAttributes,
-        with_SetAttributes,
-        with_DeleteProperty,
-        with_Enumerate,
-        with_TypeOf,
-        NULL,       /* trace          */
-        with_ThisObject,
-        NULL,       /* clear          */
-    }
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,      NULL,
+    with_getObjectOps,
+    0,0,0,0,0,0,0
 };
 
 JS_REQUIRES_STACK JSObject *
 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
 {
     JSObject *obj;
 
     obj = js_NewGCObject(cx);
@@ -3222,23 +3235,19 @@ js_XDRBlockObject(JSXDRState *xdr, JSObj
     return true;
 }
 
 #endif
 
 Class js_BlockClass = {
     "Block",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,     PropertyStub,     PropertyStub,      PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,       NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 JSObject *
 js_InitObjectClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
                                    object_props, object_methods, NULL, object_static_methods);
     if (!proto)
@@ -4457,17 +4466,17 @@ js_FindPropertyHelper(JSContext *cx, jsi
 
     /* Scan entries on the scope chain that we can cache across. */
     entry = JS_NO_PROP_CACHE_FILL;
     obj = scopeChain;
     parent = obj->getParent();
     for (scopeIndex = 0;
          parent
          ? js_IsCacheableNonGlobalScope(obj)
-         : !obj->getOps()->lookupProperty;
+         : obj->map->ops->lookupProperty == js_LookupProperty;
          ++scopeIndex) {
         protoIndex =
             js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
                                        &pobj, &prop);
         if (protoIndex < 0)
             return NULL;
 
         if (prop) {
@@ -4838,29 +4847,26 @@ js_GetProperty(JSContext *cx, JSObject *
     return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp);
 }
 
 JSBool
 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp)
 {
     JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
 
-    PropertyIdOp op = obj->getOps()->getProperty;
-    if (!op) {
-#if JS_HAS_XML_SUPPORT
-        JS_ASSERT(!obj->isXML());
-#endif
+    if (obj->map->ops == &js_ObjectOps ||
+        obj->map->ops->getProperty == js_GetProperty) {
         return js_GetPropertyHelper(cx, obj, id, getHow, vp);
     }
     JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray());
 #if JS_HAS_XML_SUPPORT
     if (obj->isXML())
         return js_GetXMLMethod(cx, obj, id, vp);
 #endif
-    return op(cx, obj, id, vp);
+    return obj->getProperty(cx, id, vp);
 }
 
 JS_FRIEND_API(bool)
 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
 {
     JSStackFrame *const fp = js_GetTopStackFrame(cx);
     if (!fp)
         return true;
@@ -5370,17 +5376,17 @@ DefaultValue(JSContext *cx, JSObject *ob
         return JS_FALSE;
     }
     *vp = v;
     return JS_TRUE;
 }
 
 } /* namespace js */
 
-JS_FRIEND_API(JSBool)
+JSBool
 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
 {
     /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
     Class *clasp = obj->getClass();
     JSEnumerateOp enumerate = clasp->enumerate;
     if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
         JS_ASSERT(enumerate != JS_EnumerateStub);
         return ((NewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
@@ -5948,17 +5954,17 @@ js_TraceObject(JSTracer *trc, JSObject *
     for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) {
         const Value &v = obj->getSlot(i);
         JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
         MarkValueRaw(trc, v);
     }
 }
 
 void
-js_ClearNative(JSContext *cx, JSObject *obj)
+js_Clear(JSContext *cx, JSObject *obj)
 {
     JSScope *scope;
     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 sharing another object's scope).
      * NB: we do not clear any reserved slots lying below JSSLOT_FREE(clasp).
@@ -6037,19 +6043,22 @@ js_SetReservedSlot(JSContext *cx, JSObje
     GC_POKE(cx, JS_NULL);
     JS_UNLOCK_SCOPE(cx, scope);
     return true;
 }
 
 JSObject *
 JSObject::wrappedObject(JSContext *cx) const
 {
-    if (JSObjectOp op = getClass()->ext.wrappedObject) {
-        if (JSObject *obj = op(cx, const_cast<JSObject *>(this)))
-            return obj;
+    Class *clasp = getClass();
+    if (clasp->flags & JSCLASS_IS_EXTENDED) {
+        if (JSObjectOp wrappedObject = reinterpret_cast<JSExtendedClass *>(clasp)->wrappedObject) {
+            if (JSObject *obj = wrappedObject(cx, const_cast<JSObject *>(this)))
+                return obj;
+        }
     }
     return const_cast<JSObject *>(this);
 }
 
 JSObject *
 JSObject::getGlobal()
 {
     JSObject *obj = this;
@@ -6075,17 +6084,17 @@ JSObject::getCompartment(JSContext *cx)
 
     Class *clasp = obj->getClass();
     if (!(clasp->flags & JSCLASS_IS_GLOBAL)) {
         // The magic AnyName object is runtime-wide.
         if (clasp == &js_AnyNameClass)
             return cx->runtime->defaultCompartment;
 
         // The magic function namespace object is runtime-wide.
-        if (clasp == &js_NamespaceClass &&
+        if (clasp == &js_NamespaceClass.base &&
             obj->getNameURI() == ATOM_TO_JSVAL(cx->runtime->atomState.lazy.functionNamespaceURIAtom)) {
             return cx->runtime->defaultCompartment;
         }
 
         // Compile-time Function, Block, and RegExp objects are not parented.
         if (clasp == &js_FunctionClass || clasp == &js_BlockClass || clasp == &js_RegExpClass) {
             // This is a bogus answer, but it'll do for now.
             return cx->runtime->defaultCompartment;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -165,72 +165,83 @@ struct PropDesc {
     bool hasEnumerable : 1;
     bool hasConfigurable : 1;
 };
 
 namespace js {
 
 typedef Vector<PropDesc, 1> PropDescArray;
 
+/*
+ * Flag and type-safe cast helper to denote that Class::call is a fast native.
+ */
+const uint32 CLASS_CALL_IS_FAST = uint32(1) << (JSCLASS_LAST_API_FLAG_SHIFT + 1);
+
+inline Native CastCallOpAsNative(CallOp op)
+{
+    return reinterpret_cast<Native>(op);
+}
+
 } /* namespace js */
 
+/* For detailed comments on these function pointer types, see jsprvtd.h. */
+struct JSObjectOps {
+    /*
+     * Custom shared object map for non-native objects. For native objects
+     * this should be null indicating, that JSObject.map is an instance of
+     * JSScope.
+     */
+    const JSObjectMap   *objectMap;
+
+    /* Mandatory non-null function pointer members. */
+    JSLookupPropOp      lookupProperty;
+    js::DefinePropOp    defineProperty;
+    js::PropertyIdOp    getProperty;
+    js::PropertyIdOp    setProperty;
+    JSAttributesOp      getAttributes;
+    JSAttributesOp      setAttributes;
+    js::PropertyIdOp    deleteProperty;
+    js::NewEnumerateOp  enumerate;
+    JSTypeOfOp          typeOf;
+    JSTraceOp           trace;
+
+    /* Optionally non-null members start here. */
+    JSObjectOp          thisObject;
+    JSFinalizeOp        clear;
+
+    bool inline isNative() const;
+};
+
+extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
+extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;
+
+/*
+ * Test whether the ops is native. FIXME bug 492938: consider how it would
+ * affect the performance to do just the !objectMap check.
+ */
+inline bool
+JSObjectOps::isNative() const
+{
+    return JS_LIKELY(this == &js_ObjectOps) || !objectMap;
+}
+
 struct JSObjectMap {
-    static JS_FRIEND_DATA(const JSObjectMap) sharedNonNative;
-
+    const JSObjectOps * const   ops;    /* high level object operation vtable */
     uint32                      shape;  /* shape identifier */
 
-    explicit JSObjectMap(uint32 shape) : shape(shape) {}
+    explicit JSObjectMap(const JSObjectOps *ops, uint32 shape) : ops(ops), shape(shape) {}
 
     enum { SHAPELESS = 0xffffffff };
 
-    bool isNative() const { return this != &sharedNonNative; }
-
-  private:
+private:
     /* No copy or assignment semantics. */
     JSObjectMap(JSObjectMap &);
     void operator=(JSObjectMap &);
 };
 
-/*
- * Unlike js_DefineNativeProperty, 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 (*objp)->dropProperty(cx, *propp).
- */
-extern JS_FRIEND_API(JSBool)
-js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
-                  JSProperty **propp);
-
-extern JSBool
-js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value *value,
-                  js::PropertyOp getter, js::PropertyOp setter, uintN attrs);
-
-extern JSBool
-js_GetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
-
-extern JSBool
-js_SetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
-
-extern JSBool
-js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
-
-extern JSBool
-js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
-
-extern JSBool
-js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *rval);
-
-extern JS_FRIEND_API(JSBool)
-js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-             js::Value *statep, jsid *idp);
-
-extern JSType
-js_TypeOf(JSContext *cx, JSObject *obj);
-
 struct NativeIterator;
 
 const uint32 JS_INITIAL_NSLOTS = 4;
 
 const uint32 JSSLOT_PARENT  = 0;
 
 /*
  * The first available slot to store generic value. For JSCLASS_HAS_PRIVATE
@@ -286,36 +297,30 @@ struct JSObject {
     // TODO: this is needed to pad out fslots. alternatively, clasp could be
     // merged with flags and the padding removed, but I think the upcoming
     // removal of JSScope will change this all anyway so I will leave this
     // here for now.
     uint32      padding;
 #endif
     js::Value   fslots[JS_INITIAL_NSLOTS];  /* small number of fixed slots */
 
-    bool isNative() const {
-        return map->isNative();
-    }
+    bool isNative() const { return map->ops->isNative(); }
 
     js::Class *getClass() const {
         return clasp;
     }
 
     JSClass *getJSClass() const {
         return Jsvalify(clasp);
     }
 
     bool hasClass(const js::Class *c) const {
         return c == clasp;
     }
 
-    const js::ObjectOps *getOps() const {
-        return &getClass()->ops;
-    }
-
     inline JSScope *scope() const;
     inline uint32 shape() const;
 
     bool isDelegate() const {
         return (flags & jsuword(1)) != jsuword(0);
     }
 
     void setDelegate() {
@@ -677,72 +682,63 @@ struct JSObject {
                                       JSObject *parent,
                                       const js::Value &privateSlotValue);
 
     inline bool hasSlotsArray() const { return !!dslots; }
 
     /* This method can only be called when hasSlotsArray() returns true. */
     inline void freeSlotsArray(JSContext *cx);
 
-    JSBool lookupProperty(JSContext *cx, jsid id, JSObject **objp, JSProperty **propp) {
-        JSLookupPropOp op = getOps()->lookupProperty;
-        return (op ? op : js_LookupProperty)(cx, this, id, objp, propp);
+    JSBool lookupProperty(JSContext *cx, jsid id,
+                          JSObject **objp, JSProperty **propp) {
+        return map->ops->lookupProperty(cx, this, id, objp, propp);
     }
 
     JSBool defineProperty(JSContext *cx, jsid id, const js::Value &value,
                           js::PropertyOp getter = js::PropertyStub,
                           js::PropertyOp setter = js::PropertyStub,
                           uintN attrs = JSPROP_ENUMERATE) {
-        js::DefinePropOp op = getOps()->defineProperty;
-        return (op ? op : js_DefineProperty)(cx, this, id, &value, getter, setter, attrs);
+        return map->ops->defineProperty(cx, this, id, &value, getter, setter, attrs);
     }
 
     JSBool getProperty(JSContext *cx, jsid id, js::Value *vp) {
-        js::PropertyIdOp op = getOps()->getProperty;
-        return (op ? op : js_GetProperty)(cx, this, id, vp);
+        return map->ops->getProperty(cx, this, id, vp);
     }
 
     JSBool setProperty(JSContext *cx, jsid id, js::Value *vp) {
-        js::PropertyIdOp op = getOps()->setProperty;
-        return (op ? op : js_SetProperty)(cx, this, id, vp);
+        return map->ops->setProperty(cx, this, id, vp);
     }
 
     JSBool getAttributes(JSContext *cx, jsid id, uintN *attrsp) {
-        JSAttributesOp op = getOps()->getAttributes;
-        return (op ? op : js_GetAttributes)(cx, this, id, attrsp);
+        return map->ops->getAttributes(cx, this, id, attrsp);
     }
 
     JSBool setAttributes(JSContext *cx, jsid id, uintN *attrsp) {
-        JSAttributesOp op = getOps()->setAttributes;
-        return (op ? op : js_SetAttributes)(cx, this, id, attrsp);
+        return map->ops->setAttributes(cx, this, id, attrsp);
     }
 
     JSBool deleteProperty(JSContext *cx, jsid id, js::Value *rval) {
-        js::PropertyIdOp op = getOps()->deleteProperty;
-        return (op ? op : js_DeleteProperty)(cx, this, id, rval);
+        return map->ops->deleteProperty(cx, this, id, rval);
     }
 
-    JSBool enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp) {
-        js::NewEnumerateOp op = getOps()->enumerate;
-        return (op ? op : js_Enumerate)(cx, this, iterop, statep, idp);
+    JSBool enumerate(JSContext *cx, JSIterateOp op, js::Value *statep,
+                     jsid *idp) {
+        return map->ops->enumerate(cx, this, op, statep, idp);
     }
 
     JSType typeOf(JSContext *cx) {
-        JSTypeOfOp op = getOps()->typeOf;
-        return (op ? op : js_TypeOf)(cx, this);
+        return map->ops->typeOf(cx, this);
     }
 
     JSObject *wrappedObject(JSContext *cx) const;
 
     /* These four are time-optimized to avoid stub calls. */
     JSObject *thisObject(JSContext *cx) {
-        JSObjectOp op = getOps()->thisObject;
-        return op ? op(cx, this) : this;
+        return map->ops->thisObject ? map->ops->thisObject(cx, this) : this;
     }
-
     static bool thisObject(JSContext *cx, const js::Value &v, js::Value *vp);
 
     inline void dropProperty(JSContext *cx, JSProperty *prop);
 
     JS_FRIEND_API(JSCompartment *) getCompartment(JSContext *cx);
 
     void swap(JSObject *obj);
 
@@ -813,29 +809,37 @@ JS_STATIC_ASSERT(sizeof(JSObject) % JS_G
 #define CX_THREAD_IS_RUNNING_GC(cx)                                           \
     THREAD_IS_RUNNING_GC((cx)->runtime, (cx)->thread)
 
 #endif /* JS_THREADSAFE */
 
 inline void
 OBJ_TO_INNER_OBJECT(JSContext *cx, JSObject *&obj)
 {
-    if (JSObjectOp op = obj->getClass()->ext.innerObject)
-        obj = op(cx, obj);
+    js::Class *clasp = obj->getClass();
+    if (clasp->flags & JSCLASS_IS_EXTENDED) {
+        js::ExtendedClass *xclasp = (js::ExtendedClass *) clasp;
+        if (xclasp->innerObject)
+            obj = xclasp->innerObject(cx, obj);
+    }
 }
 
 /*
  * The following function has been copied to jsd/jsd_val.c. If making changes to
  * OBJ_TO_OUTER_OBJECT, please update jsd/jsd_val.c as well.
  */
 inline void
 OBJ_TO_OUTER_OBJECT(JSContext *cx, JSObject *&obj)
 {
-    if (JSObjectOp op = obj->getClass()->ext.outerObject)
-        obj = op(cx, obj);
+    js::Class *clasp = obj->getClass();
+    if (clasp->flags & JSCLASS_IS_EXTENDED) {
+        js::ExtendedClass *xclasp = (js::ExtendedClass *) clasp;
+        if (xclasp->outerObject)
+            obj = xclasp->outerObject(cx, obj);
+    }
 }
 
 class JSValueArray {
   public:
     jsval *array;
     size_t length;
 
     JSValueArray(jsval *v, size_t c) : array(v), length(c) {}
@@ -1067,22 +1071,24 @@ js_CheckForStringIndex(jsid id);
  * js_PurgeScopeChain does nothing if obj is not itself a prototype or parent
  * scope, else it reshapes the scope and prototype chains it links. It calls
  * js_PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
  * (i.e., obj has ever been on a prototype or parent chain).
  */
 extern void
 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id);
 
-inline void
+#ifdef __cplusplus /* Aargh, libgjs, bug 492720. */
+static JS_INLINE void
 js_PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
 {
     if (obj->isDelegate())
         js_PurgeScopeChainHelper(cx, obj, id);
 }
+#endif
 
 /*
  * 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,
                      js::PropertyOp getter, js::PropertyOp setter, uint32 slot,
@@ -1094,16 +1100,20 @@ js_AddNativeProperty(JSContext *cx, JSOb
  * or identical property.
  */
 extern JSScopeProperty *
 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
                              JSScopeProperty *sprop, uintN attrs, uintN mask,
                              js::PropertyOp getter, js::PropertyOp setter);
 
 extern JSBool
+js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value *value,
+                  js::PropertyOp getter, js::PropertyOp setter, uintN attrs);
+
+extern JSBool
 js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
                      const js::Value &descriptor, JSBool *bp);
 
 /*
  * Flags for the defineHow parameter of js_DefineNativeProperty.
  */
 const uintN JSDNP_CACHE_RESULT = 1; /* an interpreter call from JSOP_INITPROP */
 const uintN JSDNP_DONT_PURGE   = 2; /* suppress js_PurgeScopeChain */
@@ -1123,43 +1133,54 @@ const uintN JSDNP_UNQUALIFIED  = 8; /* U
  */
 extern JSBool
 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const js::Value &value,
                         js::PropertyOp getter, js::PropertyOp setter, uintN attrs,
                         uintN flags, intN shortid, JSProperty **propp,
                         uintN defineHow = 0);
 
 /*
+ * Unlike js_DefineNativeProperty, 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 (*objp)->dropProperty(cx, *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 and
  * returns the index along the prototype chain in which *propp was found, or
  * the last index if not found, or -1 on error.
  */
 extern int
 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                            JSObject **objp, JSProperty **propp);
 
 
 /*
  * We cache name lookup results only for the global object or for native
  * non-global objects without prototype or with prototype that never mutates,
  * see bug 462734 and bug 487039.
  */
-inline bool
+static inline bool
 js_IsCacheableNonGlobalScope(JSObject *obj)
 {
     extern JS_FRIEND_DATA(js::Class) js_CallClass;
     extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
     JS_ASSERT(obj->getParent());
 
     js::Class *clasp = obj->getClass();
     bool cacheable = (clasp == &js_CallClass ||
                       clasp == &js_BlockClass ||
                       clasp == &js_DeclEnvClass);
 
-    JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
+    JS_ASSERT_IF(cacheable, obj->map->ops->lookupProperty == js_LookupProperty);
     return cacheable;
 }
 
 /*
  * If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
  */
 extern js::PropertyCacheEntry *
 js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
@@ -1210,16 +1231,19 @@ extern JSBool
 js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added,
              js::Value *vp);
 
 extern JSBool
 js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
                      js::Value *vp);
 
 extern JSBool
+js_GetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
+
+extern JSBool
 js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, js::Value *vp);
 
 /*
  * Check whether it is OK to assign an undeclared property with name
  * propname of the global object in the current script on cx.  Reports
@@ -1228,34 +1252,57 @@ js_GetMethod(JSContext *cx, JSObject *ob
  */
 extern JS_FRIEND_API(bool)
 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname);
 
 extern JSBool
 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
                      js::Value *vp);
 
+extern JSBool
+js_SetProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
+
+extern JSBool
+js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
+
+extern JSBool
+js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp);
+
 /*
  * Change attributes for the given native property. The caller must ensure
  * that obj is locked and this function always unlocks obj on return.
  */
 extern JSBool
 js_SetNativeAttributes(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
                        uintN attrs);
 
+extern JSBool
+js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, js::Value *rval);
+
 namespace js {
 
 extern JSBool
 DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
 
+}
+
+extern JSBool
+js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
+             js::Value *statep, jsid *idp);
+
+namespace js {
+
 extern JSBool
 CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
             js::Value *vp, uintN *attrsp);
 
-} /* namespace js */
+}
+
+extern JSType
+js_TypeOf(JSContext *cx, JSObject *obj);
 
 extern bool
 js_IsDelegate(JSContext *cx, JSObject *obj, const js::Value &v);
 
 /*
  * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is
  * JSProto_Null, clasp must non-null.
  */
@@ -1300,17 +1347,17 @@ js_XDRObject(JSXDRState *xdr, JSObject *
 
 extern void
 js_TraceObject(JSTracer *trc, JSObject *obj);
 
 extern void
 js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize);
 
 extern void
-js_ClearNative(JSContext *cx, JSObject *obj);
+js_Clear(JSContext *cx, JSObject *obj);
 
 extern bool
 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, js::Value *vp);
 
 extern bool
 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const js::Value &v);
 
 /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -488,41 +488,41 @@ class AutoPropertyDescriptorRooter : pri
         setter = desc->setter;
         value = desc->value;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 };
 
 static inline bool
-InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto)
+InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto, JSObjectOps* ops)
 {
-    JS_ASSERT(clasp->isNative());
+    JS_ASSERT(ops->isNative());
     JS_ASSERT(proto == obj->getProto());
 
     /* Share proto's emptyScope only if obj is similar to proto. */
     JSScope *scope = NULL;
 
     if (proto && proto->isNative()) {
         JS_LOCK_OBJ(cx, proto);
         scope = proto->scope();
-        if (scope->canProvideEmptyScope(clasp)) {
+        if (scope->canProvideEmptyScope(ops, clasp)) {
             JSScope *emptyScope = scope->getEmptyScope(cx, clasp);
             JS_UNLOCK_SCOPE(cx, scope);
             if (!emptyScope)
                 goto bad;
             scope = emptyScope;
         } else {
             JS_UNLOCK_SCOPE(cx, scope);
             scope = NULL;
         }
     }
 
     if (!scope) {
-        scope = JSScope::create(cx, clasp, obj, js_GenerateShape(cx, false));
+        scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false));
         if (!scope)
             goto bad;
         uint32 freeslot = JSSLOT_FREE(clasp);
         JS_ASSERT(freeslot >= scope->freeslot);
         if (freeslot > JS_INITIAL_NSLOTS && !obj->allocSlots(cx, freeslot))
             goto bad;
         scope->freeslot = freeslot;
 #ifdef DEBUG
@@ -565,17 +565,17 @@ NewNativeClassInstance(JSContext *cx, Cl
         /*
          * Default parent to the parent of the prototype, which was set from
          * the parent of the prototype's constructor.
          */
         obj->init(clasp, proto, parent, JSObject::defaultPrivate(clasp));
 
         JS_LOCK_OBJ(cx, proto);
         JSScope *scope = proto->scope();
-        JS_ASSERT(scope->canProvideEmptyScope(clasp));
+        JS_ASSERT(scope->canProvideEmptyScope(&js_ObjectOps, clasp));
         scope = scope->getEmptyScope(cx, clasp);
         JS_UNLOCK_OBJ(cx, proto);
 
         if (!scope) {
             obj = NULL;
         } else {
             obj->map = scope;
         }
@@ -633,16 +633,21 @@ NewBuiltinClassInstance(JSContext *cx, C
  * proto->getParent() if proto is non-null (else to null). NB: only this helper
  * and NewObject can be used to construct full-sized JSFunction instances.
  */
 static inline JSObject *
 NewObjectWithGivenProto(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
 {
     DTrace::ObjectCreationScope objectCreationScope(cx, cx->fp, clasp);
 
+    /* Always call the class's getObjectOps hook if it has one. */
+    JSObjectOps *ops = clasp->getObjectOps
+                       ? clasp->getObjectOps(cx, clasp)
+                       : &js_ObjectOps;
+
     /*
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC. Functions have a
      * larger non-standard allocation size.
      */
     JSObject* obj;
     if (clasp == &js_FunctionClass) {
         obj = (JSObject*) js_NewGCFunction(cx);
@@ -662,23 +667,24 @@ NewObjectWithGivenProto(JSContext *cx, C
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
     obj->init(clasp,
               proto,
               (!parent && proto) ? proto->getParent() : parent,
               JSObject::defaultPrivate(clasp));
 
-    if (clasp->isNative()) {
-        if (!InitScopeForObject(cx, obj, clasp, proto)) {
+    if (ops->isNative()) {
+        if (!InitScopeForObject(cx, obj, clasp, proto, ops)) {
             obj = NULL;
             goto out;
         }
     } else {
-        obj->map = const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative);
+        JS_ASSERT(ops->objectMap->ops == ops);
+        obj->map = const_cast<JSObjectMap *>(ops->objectMap);
     }
 
 out:
     objectCreationScope.handleCreation(obj);
     return obj;
 }
 
 static inline JSProtoKey
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -94,23 +94,19 @@ struct JSONParser
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 Class js_JSONClass = {
     js_JSON_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,  PropertyStub,  PropertyStub,  PropertyStub,
+    EnumerateStub, ResolveStub,   ConvertStub,   NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 JSBool
 js_json_parse(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *s = NULL;
     Value *argv = vp + 2;
     AutoValueRooter reviver(cx);
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -900,57 +900,75 @@ proxy_TraceObject(JSTracer *trc, JSObjec
     obj->getProxyHandler()->trace(trc, obj);
     MarkValue(trc, obj->getProxyPrivate(), "private");
     if (obj->isFunctionProxy()) {
         MarkValue(trc, GetCall(obj), "call");
         MarkValue(trc, GetConstruct(obj), "construct");
     }
 }
 
+static JSType
+proxy_TypeOf_obj(JSContext *cx, JSObject *obj)
+{
+    return JSTYPE_OBJECT;
+}
+
 void
 proxy_Finalize(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isProxy());
     if (!obj->getSlot(JSSLOT_PROXY_HANDLER).isUndefined())
         obj->getProxyHandler()->finalize(cx, obj);
 }
 
+extern JSObjectOps js_ObjectProxyObjectOps;
+
+static const JSObjectMap SharedObjectProxyMap(&js_ObjectProxyObjectOps, JSObjectMap::SHAPELESS);
+
+JSObjectOps js_ObjectProxyObjectOps = {
+    &SharedObjectProxyMap,
+    proxy_LookupProperty,
+    proxy_DefineProperty,
+    proxy_GetProperty,
+    proxy_SetProperty,
+    proxy_GetAttributes,
+    proxy_SetAttributes,
+    proxy_DeleteProperty,
+    js_Enumerate,
+    proxy_TypeOf_obj,
+    proxy_TraceObject,
+    NULL,   /* thisObject */
+    proxy_Finalize
+};
+
+static JSObjectOps *
+obj_proxy_getObjectOps(JSContext *cx, Class *clasp)
+{
+    return &js_ObjectProxyObjectOps;
+}
+
 JS_FRIEND_API(Class) ObjectProxyClass = {
     "Proxy",
-    Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(2),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
+    JSCLASS_HAS_RESERVED_SLOTS(2),
+    PropertyStub,
+    PropertyStub,
+    PropertyStub,
+    PropertyStub,
     EnumerateStub,
     ResolveStub,
     ConvertStub,
-    NULL,           /* finalize */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    JS_NULL_CLASS_EXT,
-    {
-        proxy_LookupProperty,
-        proxy_DefineProperty,
-        proxy_GetProperty,
-        proxy_SetProperty,
-        proxy_GetAttributes,
-        proxy_SetAttributes,
-        proxy_DeleteProperty,
-        NULL,       /* enumerate       */
-        NULL,       /* typeof          */
-        proxy_TraceObject,
-        NULL,       /* thisObject      */
-        proxy_Finalize, /* clear */
-    }
+    NULL,
+    obj_proxy_getObjectOps,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
 };
 
 JSBool
 proxy_Call(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *proxy = &JS_CALLEE(cx, vp).toObject();
     JS_ASSERT(proxy->isProxy());
     return JSProxy::call(cx, proxy, argc, vp);
@@ -965,51 +983,63 @@ proxy_Construct(JSContext *cx, JSObject 
 }
 
 static JSType
 proxy_TypeOf_fun(JSContext *cx, JSObject *obj)
 {
     return JSTYPE_FUNCTION;
 }
 
+extern JSObjectOps js_FunctionProxyObjectOps;
+
+static const JSObjectMap SharedFunctionProxyMap(&js_FunctionProxyObjectOps, JSObjectMap::SHAPELESS);
+
 #define proxy_HasInstance js_FunctionClass.hasInstance
 
+JSObjectOps js_FunctionProxyObjectOps = {
+    &SharedFunctionProxyMap,
+    proxy_LookupProperty,
+    proxy_DefineProperty,
+    proxy_GetProperty,
+    proxy_SetProperty,
+    proxy_GetAttributes,
+    proxy_SetAttributes,
+    proxy_DeleteProperty,
+    js_Enumerate,
+    proxy_TypeOf_fun,
+    proxy_TraceObject,
+    NULL, /* thisObject */
+    NULL  /* clear */
+};
+
+static JSObjectOps *
+fun_proxy_getObjectOps(JSContext *cx, Class *clasp)
+{
+    return &js_FunctionProxyObjectOps;
+}
+
 JS_FRIEND_API(Class) FunctionProxyClass = {
     "Proxy",
-    Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(4) | Class::CALL_IS_FAST,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
+    JSCLASS_HAS_RESERVED_SLOTS(4) | CLASS_CALL_IS_FAST,
+    PropertyStub,
+    PropertyStub,
+    PropertyStub,
+    PropertyStub,
     EnumerateStub,
     ResolveStub,
     ConvertStub,
-    NULL,           /* finalize */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
+    NULL,
+    fun_proxy_getObjectOps,
+    NULL,
     CastCallOpAsNative(proxy_Call),
     proxy_Construct,
-    NULL,           /* xdrObject   */
+    NULL,
     proxy_HasInstance,
-    NULL,           /* mark */
-    JS_NULL_CLASS_EXT,
-    {
-        proxy_LookupProperty,
-        proxy_DefineProperty,
-        proxy_GetProperty,
-        proxy_SetProperty,
-        proxy_GetAttributes,
-        proxy_SetAttributes,
-        proxy_DeleteProperty,
-        NULL,       /* enumerate       */
-        proxy_TypeOf_fun,
-        proxy_TraceObject,
-        NULL,       /* thisObject      */
-        NULL,       /* clear           */
-    }
+    NULL,
+    NULL
 };
 
 JS_FRIEND_API(JSObject *)
 NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObject *proto,
                JSObject *parent, JSObject *call, JSObject *construct)
 {
     bool fun = call || construct;
     Class *clasp = fun ? &FunctionProxyClass : &ObjectProxyClass;
@@ -1208,28 +1238,20 @@ callable_Construct(JSContext *cx, JSObje
         return true;
     }
     return InternalCall(cx, obj, fval, argc, argv, rval);
 }
 
 Class CallableObjectClass = {
     "Function",
     JSCLASS_HAS_RESERVED_SLOTS(2),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    NULL,           /* finalize    */
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    callable_Call,
-    callable_Construct,
+    PropertyStub,           PropertyStub,    PropertyStub,    PropertyStub,
+    EnumerateStub,          ResolveStub,     ConvertStub,     NULL,
+    NULL,                   NULL,            callable_Call,   callable_Construct,
+    NULL,                   NULL,            NULL,            NULL
 };
 
 JS_FRIEND_API(JSBool)
 FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
 {
     AutoValueRooter tvr(cx);
     if (!JSProxy::fix(cx, proxy, tvr.addr()))
         return false;
@@ -1277,23 +1299,19 @@ FixProxy(JSContext *cx, JSObject *proxy,
     return true;
 }
 
 }
 
 Class js_ProxyClass = {
     "Proxy",
     JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,           PropertyStub,    PropertyStub,    PropertyStub,
+    EnumerateStub,          ResolveStub,     ConvertStub,     NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JSObject *obj)
 {
     JSObject *module = NewObject(cx, &js_ProxyClass, NULL, obj);
     if (!module)
         return NULL;
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -341,41 +341,20 @@ typedef JSBool
  * The type of ops->call. Same argument types as JSFastNative, but a different
  * contract. A JSCallOp expects a dummy stack frame with the caller's
  * scopeChain.
  */
 typedef JSBool
 (* JSCallOp)(JSContext *cx, uintN argc, jsval *vp);
 
 /*
- * A generic type for functions mapping an object to another object, or null
- * if an error or exception was thrown on cx.
- */
-typedef JSObject *
-(* JSObjectOp)(JSContext *cx, JSObject *obj);
-
-/*
- * Hook that creates an iterator object for a given object. Returns the
- * iterator object or null if an error or exception was thrown on cx.
- */
-typedef JSObject *
-(* JSIteratorOp)(JSContext *cx, JSObject *obj, JSBool keysonly);
-
-/*
  * The following determines whether JS_EncodeCharacters and JS_DecodeBytes
  * treat char[] as utf-8 or simply as bytes that need to be inflated/deflated.
  */
 #ifdef JS_C_STRINGS_ARE_UTF8
 # define js_CStringsAreUTF8 JS_TRUE
 #else
 extern JSBool js_CStringsAreUTF8;
 #endif
 
-/*
- * Hack to expose obj->getOps()->outer to the C implementation of the debugger
- * interface.
- */
-extern JS_FRIEND_API(JSObject *)
-js_ObjectToOuterObject(JSContext *cx, JSObject *obj);
-
 JS_END_EXTERN_C
 
 #endif /* jsprvtd_h___ */
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -102,17 +102,17 @@ typedef enum 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;
 
-/* js_CheckAccess mode enumeration. */
+/* 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 */
 
                                 /*
                                  * enum value #2 formerly called JSACC_IMPORT,
                                  * gap preserved for ABI compatibility.
                                  */
@@ -140,26 +140,28 @@ typedef enum JSIterateOp {
     JSENUMERATE_NEXT,
 
     /* Destroy iterator state. */
     JSENUMERATE_DESTROY
 } 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 JSTracer          JSTracer;
 typedef struct JSIdArray         JSIdArray;
 typedef struct JSPropertyDescriptor JSPropertyDescriptor;
 typedef struct JSPropertySpec    JSPropertySpec;
 typedef struct JSObjectMap       JSObjectMap;
+typedef struct JSObjectOps       JSObjectOps;
 typedef struct JSRuntime         JSRuntime;
 typedef struct JSScript          JSScript;
 typedef struct JSStackFrame      JSStackFrame;
 typedef struct JSXDRState        JSXDRState;
 typedef struct JSExceptionState  JSExceptionState;
 typedef struct JSLocaleCallbacks JSLocaleCallbacks;
 typedef struct JSSecurityCallbacks JSSecurityCallbacks;
 typedef struct JSONParser        JSONParser;
@@ -297,16 +299,39 @@ typedef void
 /*
  * Used by JS_AddExternalStringFinalizer and JS_RemoveExternalStringFinalizer
  * to extend and reduce the set of string types finalized by the GC.
  */
 typedef void
 (* 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. 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.
+ */
+typedef JSObjectOps *
+(* 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.
  */
 typedef JSBool
 (* JSCheckAccessOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                     jsval *vp);
@@ -388,19 +413,36 @@ typedef void
 
 /*
  * DEBUG only callback that JSTraceOp implementation can provide to return
  * a string describing the reference traced with JS_CallTracer.
  */
 typedef void
 (* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
 
+/* JSExtendedClass function pointer typedefs. */
+
 typedef JSBool
 (* JSEqualityOp)(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
 
+/*
+ * 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 *
+(* JSObjectOp)(JSContext *cx, JSObject *obj);
+
+/*
+ * Hook that creates an iterator object for a given object. Returns the
+ * iterator object or null if an error or exception was thrown on cx.
+ */
+typedef JSObject *
+(* JSIteratorOp)(JSContext *cx, JSObject *obj, JSBool keysonly);
+
 /* Typedef for native functions called by the JS VM. */
 
 typedef JSBool
 (* JSNative)(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
              jsval *rval);
 
 /* See jsapi.h, the JS_CALLEE, JS_THIS, etc. macros. */
 typedef JSBool
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -5412,31 +5412,24 @@ regexp_trace(JSTracer *trc, JSObject *ob
         JS_CALL_STRING_TRACER(trc, re->source, "source");
 }
 
 Class js_RegExpClass = {
     js_RegExp_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::REGEXP_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    regexp_enumerate,
-    reinterpret_cast<JSResolveOp>(regexp_resolve),
-    ConvertStub,
-    regexp_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    regexp_call,
-    NULL,           /* construct   */
-    js_XDRRegExpObject,
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(regexp_trace)
+    PropertyStub,       PropertyStub,
+    PropertyStub,       PropertyStub,
+    regexp_enumerate,   reinterpret_cast<JSResolveOp>(regexp_resolve),
+    ConvertStub,        regexp_finalize,
+    NULL,               NULL,
+    regexp_call,        NULL,
+    js_XDRRegExpObject, NULL,
+    JS_CLASS_TRACE(regexp_trace), 0
 };
 
 static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0};
 
 JSBool
 js_regexp_toString(JSContext *cx, JSObject *obj, Value *vp)
 {
     JSRegExp *re;
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -94,17 +94,17 @@ js_GenerateShape(JSContext *cx, bool gcL
 JSScope *
 js_GetMutableScope(JSContext *cx, JSObject *obj)
 {
     JSScope *scope = obj->scope();
     JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
     if (!scope->isSharedEmpty())
         return scope;
 
-    JSScope *newscope = JSScope::create(cx, obj->getClass(), obj, scope->shape);
+    JSScope *newscope = JSScope::create(cx, scope->ops, obj->getClass(), obj, scope->shape);
     if (!newscope)
         return NULL;
 
     /* The newly allocated scope is single-threaded and, as such, is locked. */
     JS_ASSERT(CX_OWNS_SCOPE_TITLE(cx, newscope));
     JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope));
     obj->map = newscope;
 
@@ -206,38 +206,41 @@ JSScope::createTable(JSContext *cx, bool
     for (sprop = lastProp; sprop; sprop = sprop->parent) {
         spp = search(sprop->id, true);
         SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
     }
     return true;
 }
 
 JSScope *
-JSScope::create(JSContext *cx, Class *clasp, JSObject *obj, uint32 shape)
+JSScope::create(JSContext *cx, const JSObjectOps *ops, Class *clasp,
+                JSObject *obj, uint32 shape)
 {
+    JS_ASSERT(ops->isNative());
     JS_ASSERT(obj);
 
-    JSScope *scope = cx->create<JSScope>(obj);
+    JSScope *scope = cx->create<JSScope>(ops, obj);
     if (!scope)
         return NULL;
 
     scope->freeslot = JSSLOT_START(clasp);
     scope->flags = cx->runtime->gcRegenShapesScopeFlag;
     scope->initMinimal(cx, shape);
 
 #ifdef JS_THREADSAFE
     js_InitTitle(cx, &scope->title);
 #endif
     JS_RUNTIME_METER(cx->runtime, liveScopes);
     JS_RUNTIME_METER(cx->runtime, totalScopes);
     return scope;
 }
 
-JSEmptyScope::JSEmptyScope(JSContext *cx, Class *clasp)
-    : JSScope(NULL), clasp(clasp)
+JSEmptyScope::JSEmptyScope(JSContext *cx, const JSObjectOps *ops,
+                           Class *clasp)
+    : JSScope(ops, NULL), clasp(clasp)
 {
     /*
      * This scope holds a reference to the new empty scope. Our only caller,
      * getEmptyScope, also promises to incref on behalf of its caller.
      */
     nrefs = 2;
     freeslot = JSSLOT_START(clasp);
     flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag;
@@ -282,24 +285,24 @@ JSScope::destroy(JSContext *cx)
 bool
 JSScope::initRuntimeState(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
 
 #define SCOPE(Name) rt->empty##Name##Scope
 #define CLASP(Name) &js_##Name##Class
 
-#define INIT_EMPTY_SCOPE(Name,NAME)                                           \
-    INIT_EMPTY_SCOPE_WITH_CLASS(Name, NAME, CLASP(Name))
+#define INIT_EMPTY_SCOPE(Name,NAME,ops)                                       \
+    INIT_EMPTY_SCOPE_WITH_CLASS(Name, NAME, ops, CLASP(Name))
 
-#define INIT_EMPTY_SCOPE_WITH_CLASS(Name,NAME,clasp)                          \
-    INIT_EMPTY_SCOPE_WITH_FREESLOT(Name, NAME, clasp, JSSLOT_FREE(clasp))
+#define INIT_EMPTY_SCOPE_WITH_CLASS(Name,NAME,ops,clasp)                      \
+    INIT_EMPTY_SCOPE_WITH_FREESLOT(Name, NAME, ops, clasp, JSSLOT_FREE(clasp))
 
-#define INIT_EMPTY_SCOPE_WITH_FREESLOT(Name,NAME,clasp,slot)                  \
-    SCOPE(Name) = cx->create<JSEmptyScope>(cx, clasp);                        \
+#define INIT_EMPTY_SCOPE_WITH_FREESLOT(Name,NAME,ops,clasp,slot)              \
+    SCOPE(Name) = cx->create<JSEmptyScope>(cx, ops, clasp);                   \
     if (!SCOPE(Name))                                                         \
         return false;                                                         \
     JS_ASSERT(SCOPE(Name)->shape == JSScope::EMPTY_##NAME##_SHAPE);           \
     JS_ASSERT(SCOPE(Name)->nrefs == 2);                                       \
     SCOPE(Name)->nrefs = 1;                                                   \
     SCOPE(Name)->freeslot = slot
 
     /*
@@ -315,39 +318,39 @@ JSScope::initRuntimeState(JSContext *cx)
      * is less than freeslot to succeed. As the shared emptyArgumentsScope is
      * never mutated, it's safe to pretend to have all the slots possible.
      *
      * Note how the fast paths in jsinterp.cpp for JSOP_LENGTH and JSOP_GETELEM
      * bypass resolution of scope properties for length and element indices on
      * arguments objects. This helps ensure that any arguments object needing
      * its own mutable scope (with unique shape) is a rare event.
      */
-    INIT_EMPTY_SCOPE_WITH_FREESLOT(Arguments, ARGUMENTS, CLASP(Arguments),
+    INIT_EMPTY_SCOPE_WITH_FREESLOT(Arguments, ARGUMENTS, &js_ObjectOps, CLASP(Arguments),
                                    JS_INITIAL_NSLOTS + JS_ARGS_LENGTH_MAX);
 
-    INIT_EMPTY_SCOPE(Block, BLOCK);
+    INIT_EMPTY_SCOPE(Block, BLOCK, &js_ObjectOps);
 
     /*
      * Initialize the shared scope for all empty Call objects so gets for args
      * and vars do not force the creation of a mutable scope for the particular
      * call object being accessed.
      *
      * See comment above for rt->emptyArgumentsScope->freeslot initialization.
      */
-    INIT_EMPTY_SCOPE_WITH_FREESLOT(Call, CALL, CLASP(Call),
+    INIT_EMPTY_SCOPE_WITH_FREESLOT(Call, CALL, &js_ObjectOps, CLASP(Call),
                                    JS_INITIAL_NSLOTS + JSFunction::MAX_ARGS_AND_VARS);
 
     /* A DeclEnv object holds the name binding for a named function expression. */
-    INIT_EMPTY_SCOPE(DeclEnv, DECL_ENV);
+    INIT_EMPTY_SCOPE(DeclEnv, DECL_ENV, &js_ObjectOps);
 
     /* Non-escaping native enumerator objects share this empty scope. */
-    INIT_EMPTY_SCOPE_WITH_CLASS(Enumerator, ENUMERATOR, &js_IteratorClass);
+    INIT_EMPTY_SCOPE_WITH_CLASS(Enumerator, ENUMERATOR, &js_ObjectOps, &js_IteratorClass.base);
 
     /* Same drill for With objects. */
-    INIT_EMPTY_SCOPE_WITH_CLASS(With, WITH, &js_WithClass);
+    INIT_EMPTY_SCOPE(With, WITH, &js_WithObjectOps);
 
 #undef SCOPE
 #undef CLASP
 #undef INIT_EMPTY_SCOPE
 #undef INIT_EMPTY_SCOPE_WITH_CLASS
 #undef INIT_EMPTY_SCOPE_WITH_FREESLOT
 
     return true;
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -289,36 +289,37 @@ struct JSScope : public JSObjectMap
 
     JSScopeProperty *addPropertyHelper(JSContext *cx, jsid id,
                                        js::PropertyOp getter, js::PropertyOp setter,
                                        uint32 slot, uintN attrs,
                                        uintN flags, intN shortid,
                                        JSScopeProperty **spp);
 
   public:
-    JSScope(JSObject *obj)
-      : JSObjectMap(0), object(obj) {}
+    JSScope(const JSObjectOps *ops, JSObject *obj)
+      : JSObjectMap(ops, 0), object(obj) {}
 
     /* Create a mutable, owned, empty scope. */
-    static JSScope *create(JSContext *cx, js::Class *clasp, JSObject *obj, uint32 shape);
+    static JSScope *create(JSContext *cx, const JSObjectOps *ops,
+                           js::Class *clasp, JSObject *obj, uint32 shape);
 
     void destroy(JSContext *cx);
 
     /*
      * Return an immutable, shareable, empty scope with the same ops as this
      * and the same freeslot as this had when empty.
      *
      * If |this| is the scope of an object |proto|, the resulting scope can be
      * used as the scope of a new object whose prototype is |proto|.
      */
     inline JSEmptyScope *getEmptyScope(JSContext *cx, js::Class *clasp);
 
     inline bool ensureEmptyScope(JSContext *cx, js::Class *clasp);
 
-    inline bool canProvideEmptyScope(js::Class *clasp);
+    inline bool canProvideEmptyScope(JSObjectOps *ops, js::Class *clasp);
 
     JSScopeProperty *lookup(jsid id);
 
     inline bool hasProperty(jsid id) { return lookup(id) != NULL; }
     inline bool hasProperty(JSScopeProperty *sprop);
 
     /* Add a property whose id is not yet in this scope. */
     JSScopeProperty *addProperty(JSContext *cx, jsid id,
@@ -522,17 +523,17 @@ struct JSScope : public JSObjectMap
     };
 };
 
 struct JSEmptyScope : public JSScope
 {
     js::Class * const clasp;
     jsrefcount        nrefs;              /* count of all referencing objects */
 
-    JSEmptyScope(JSContext *cx, js::Class *clasp);
+    JSEmptyScope(JSContext *cx, const JSObjectOps *ops, js::Class *clasp);
 
     JSEmptyScope *hold() {
         /* The method is only called for already held objects. */
         JS_ASSERT(nrefs >= 1);
         JS_ATOMIC_INCREMENT(&nrefs);
         return this;
     }
 
@@ -954,25 +955,25 @@ JSScope::search(jsid id, bool adding)
         return spp;
     }
     return searchTable(id, adding);
 }
 
 #undef METER
 
 inline bool
-JSScope::canProvideEmptyScope(js::Class *clasp)
+JSScope::canProvideEmptyScope(JSObjectOps *ops, js::Class *clasp)
 {
     /*
      * An empty scope cannot provide another empty scope, or wrongful two-level
      * prototype shape sharing ensues -- see bug 497789.
      */
     if (!object)
         return false;
-    return !emptyScope || emptyScope->clasp == clasp;
+    return this->ops == ops && (!emptyScope || emptyScope->clasp == clasp);
 }
 
 inline bool
 JSScopeProperty::isSharedPermanent() const
 {
     return (~attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
 }
 
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -48,17 +48,17 @@
 
 #include "jscntxtinlines.h"
 
 inline JSEmptyScope *
 JSScope::createEmptyScope(JSContext *cx, js::Class *clasp)
 {
     JS_ASSERT(!isSharedEmpty());
     JS_ASSERT(!emptyScope);
-    emptyScope = cx->create<JSEmptyScope>(cx, clasp);
+    emptyScope = cx->create<JSEmptyScope>(cx, ops, clasp);
     return emptyScope;
 }
 
 inline JSEmptyScope *
 JSScope::getEmptyScope(JSContext *cx, js::Class *clasp)
 {
     if (emptyScope) {
         JS_ASSERT(clasp == emptyScope->clasp);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -408,31 +408,20 @@ script_trace(JSTracer *trc, JSObject *ob
     if (script)
         js_TraceScript(trc, script);
 }
 
 Class js_ScriptClass = {
     "Script",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    script_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(script_trace)
+    PropertyStub,     PropertyStub,     PropertyStub,     PropertyStub,
+    EnumerateStub,    ResolveStub,      ConvertStub,      script_finalize,
+    NULL,             NULL,             NULL,             NULL,/*XXXbe xdr*/
+    NULL,             NULL,             JS_CLASS_TRACE(script_trace), NULL
 };
 
 /*
  * Shared script filename management.
  */
 static int
 js_compare_strings(const void *k1, const void *k2)
 {
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -801,23 +801,19 @@ str_resolve(JSContext *cx, JSObject *obj
     }
     return JS_TRUE;
 }
 
 Class js_StringClass = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_NEW_RESOLVE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_String),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    str_getProperty,
-    PropertyStub,   /* setProperty */
-    str_enumerate,
-    (JSResolveOp)str_resolve,
-    ConvertStub
+    PropertyStub,    PropertyStub,     str_getProperty,     PropertyStub,
+    str_enumerate,   (JSResolveOp)str_resolve, ConvertStub, NULL,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 #define NORMALIZE_THIS(cx,vp,str)                                             \
     JS_BEGIN_MACRO                                                            \
         if (vp[1].isString()) {                                               \
             str = vp[1].toString();                                           \
         } else {                                                              \
             str = NormalizeThis(cx, vp);                                      \
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2906,17 +2906,17 @@ NativeToValue(JSContext* cx, Value& v, J
       }
       case JSVAL_TYPE_STRORNULL:
         debug_only_printf(LC_TMTracer, "nullablestr<%p> ", v.isNull() ? NULL : (void *)v.toString());
         break;
       case JSVAL_TYPE_OBJORNULL:
         debug_only_printf(LC_TMTracer, "nullablestr<%p> ", v.isNull() ? NULL : (void *)&v.toObject());
         break;
       case JSVAL_TYPE_BOXED:
-        debug_only_printf(LC_TMTracer, "box<%llx> ", (unsigned long long)v.asRawBits());
+        debug_only_printf(LC_TMTracer, "box<%llx> ", v.asRawBits());
         break;
       default:
         JS_NOT_REACHED("unexpected type");
         break;
     }
 #endif
 }
 
@@ -8967,17 +8967,18 @@ TraceRecorder::equalityHelper(Value& l, 
      */
 
     if (getPromotedType(l) == getPromotedType(r)) {
         if (l.isUndefined() || l.isNull()) {
             cond = true;
             if (l.isNull())
                 op = LIR_eqp;
         } else if (l.isObject()) {
-            if (l.toObject().getClass()->ext.equality)
+            Class *clasp = l.toObject().getClass();
+            if ((clasp->flags & JSCLASS_IS_EXTENDED) && ((JSExtendedClass*) clasp)->equality)
                 RETURN_STOP_A("Can't trace extended class equality operator");
             op = LIR_eqp;
             cond = (l == r);
         } else if (l.isBoolean()) {
             JS_ASSERT(r.isBoolean());
             cond = (l == r);
         } else if (l.isString()) {
             args[0] = r_ins, args[1] = l_ins;
@@ -9377,16 +9378,18 @@ void
 TraceRecorder::forgetGuardedShapes()
 {
 #if defined DEBUG_notme && defined XP_UNIX
     dumpGuardedShapes("forget-all");
 #endif
     guardedShapeTable.clear();
 }
 
+JS_STATIC_ASSERT(offsetof(JSObjectOps, objectMap) == 0);
+
 inline LIns*
 TraceRecorder::map(LIns* obj_ins)
 {
     return addName(lir->insLoad(LIR_ldp, obj_ins, (int) offsetof(JSObject, map), ACCSET_OTHER), "map");
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2, PCVal& pcval)
@@ -11377,17 +11380,17 @@ TraceRecorder::callNative(uintN argc, JS
         JS_ASSERT(((jsuword) clasp & 3) == 0);
 
         // Abort on |new Function|. js_NewInstance would allocate a regular-
         // sized JSObject, not a Function-sized one. (The Function ctor would
         // deep-bail anyway but let's not go there.)
         if (clasp == &js_FunctionClass)
             RETURN_STOP("new Function");
 
-        if (!clasp->isNative())
+        if (clasp->getObjectOps)
             RETURN_STOP("new with non-native ops");
 
         args[0] = INS_CONSTOBJ(funobj);
         args[1] = INS_CONSTPTR(clasp);
         args[2] = cx_ins;
         newobj_ins = lir->insCall(&js_NewInstance_ci, args);
         guard(false, lir->insEqP_0(newobj_ins), OOM_EXIT);
         this_ins = newobj_ins;
@@ -11805,17 +11808,17 @@ TraceRecorder::record_JSOP_GETPROP()
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_SETPROP()
 {
     Value& l = stackval(-2);
     if (l.isPrimitive())
         RETURN_STOP_A("primitive this for SETPROP");
 
     JSObject* obj = &l.toObject();
-    if (obj->getOps()->setProperty)
+    if (obj->map->ops->setProperty != js_SetProperty)
         RETURN_STOP_A("non-native JSObjectOps::setProperty");
     return ARECORD_CONTINUE;
 }
 
 /* Emit a specialized, inlined copy of js_NativeSet. */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
                          const Value &v, LIns* v_ins)
@@ -13508,17 +13511,17 @@ TraceRecorder::prop(JSObject* obj, LIns*
 {
     /*
      * Insist that obj have js_SetProperty as its set object-op. This suffices
      * to prevent a rogue obj from being used on-trace (loaded via obj_ins),
      * because we will guard on shape (or else global object identity) and any
      * object not having the same op must have a different class, and therefore
      * must differ in its shape (or not be the global object).
      */
-    if (!obj->isDenseArray() && obj->getOps()->getProperty)
+    if (!obj->isDenseArray() && obj->map->ops->getProperty != js_GetProperty)
         RETURN_STOP_A("non-dense-array, non-native JSObjectOps::getProperty");
 
     JS_ASSERT((slotp && v_insp && !outp) || (!slotp && !v_insp && outp));
 
     /*
      * Property cache ensures that we are dealing with an existing property,
      * and guards the shape for us.
      */
@@ -14191,34 +14194,34 @@ TraceRecorder::record_JSOP_MOREITER()
     RETURN_IF_XML_A(iterobj_val);
 
     JSObject* iterobj = &iterobj_val.toObject();
     LIns* iterobj_ins = get(&iterobj_val);
     bool cond;
     LIns* cond_ins;
 
     /* JSOP_FOR* already guards on this, but in certain rare cases we might record misformed loop traces. */
-    if (iterobj->hasClass(&js_IteratorClass)) {
-        guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL);
+    if (iterobj->hasClass(&js_IteratorClass.base)) {
+        guardClass(iterobj_ins, &js_IteratorClass.base, snapshot(BRANCH_EXIT), LOAD_NORMAL);
         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
         void *cursor = ni->props_cursor;
         void *end = ni->props_end;
 
         LIns *ni_ins = stobj_get_const_private_ptr(iterobj_ins);
         LIns *cursor_ins =
             addName(lir->insLoad(LIR_ldp, ni_ins,
                                  offsetof(NativeIterator, props_cursor), ACCSET_OTHER), "cursor");
         LIns *end_ins = addName(lir->insLoad(LIR_ldp, ni_ins, offsetof(NativeIterator, props_end),
                                 ACCSET_OTHER), "end");
 
         /* Figure out whether the native iterator contains more values. */
         cond = cursor < end;
         cond_ins = lir->ins2(LIR_ltp, cursor_ins, end_ins);
     } else {
-        guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL);
+        guardNotClass(iterobj_ins, &js_IteratorClass.base, snapshot(BRANCH_EXIT), LOAD_NORMAL);
 
         enterDeepBailCall();
 
         LIns* vp_ins = lir->insAlloc(sizeof(Value));
         LIns* args[] = { vp_ins, iterobj_ins, cx_ins };
         LIns* ok_ins = lir->insCall(&IteratorMore_ci, args);
 
         /*
@@ -14306,18 +14309,18 @@ TraceRecorder::storeMagic(JSWhyMagic why
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::unboxNextValue(LIns* &v_ins)
 {
     Value &iterobj_val = stackval(-1);
     JSObject *iterobj = &iterobj_val.toObject();
     LIns* iterobj_ins = get(&iterobj_val);
 
-    if (iterobj->hasClass(&js_IteratorClass)) {
-        guardClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL);
+    if (iterobj->hasClass(&js_IteratorClass.base)) {
+        guardClass(iterobj_ins, &js_IteratorClass.base, snapshot(BRANCH_EXIT), LOAD_NORMAL);
         NativeIterator *ni = (NativeIterator *) iterobj->getPrivate();
 
         LIns *ni_ins = stobj_get_const_private_ptr(iterobj_ins);
         LIns *cursor_ins = addName(lir->insLoad(LIR_ldp, ni_ins, offsetof(NativeIterator, props_cursor), ACCSET_OTHER), "cursor");
 
         /* Emit code to stringify the id if necessary. */
         if (!(((NativeIterator *) iterobj->getPrivate())->flags & JSITER_FOREACH)) {
             /* Read the next id from the iterator. */
@@ -14347,20 +14350,19 @@ TraceRecorder::unboxNextValue(LIns* &v_i
             /* Read the next value from the iterator. */
             Value v = *ni->currentValue();
             v_ins = unbox_value(v, cursor_ins, 0, snapshot(BRANCH_EXIT));
 
             /* Increment the cursor by one Value and store it back. */
             cursor_ins = lir->ins2(LIR_addp, cursor_ins, INS_CONSTWORD(sizeof(Value)));
         }
 
-        lir->insStore(LIR_stp, cursor_ins, ni_ins, offsetof(NativeIterator, props_cursor),
-                      ACCSET_OTHER);
-    } else {
-        guardNotClass(iterobj_ins, &js_IteratorClass, snapshot(BRANCH_EXIT), LOAD_NORMAL);
+        lir->insStore(LIR_stp, cursor_ins, ni_ins, offsetof(NativeIterator, props_cursor), ACCSET_OTHER);
+    } else {
+        guardNotClass(iterobj_ins, &js_IteratorClass.base, snapshot(BRANCH_EXIT), LOAD_NORMAL);
 
         v_ins = unbox_value(cx->iterValue, cx_ins, offsetof(JSContext, iterValue),
                             snapshot(BRANCH_EXIT));
         storeMagic(JS_NO_ITER_VALUE, cx_ins, offsetof(JSContext, iterValue), ACCSET_OTHER);
     }
 
     return ARECORD_CONTINUE;
 }
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -488,28 +488,36 @@ class TypedArrayTemplate
   : public TypedArray
 {
   public:
     typedef TypedArrayTemplate<NativeType> ThisTypeArray;
     static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); }
     static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
     static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); }
 
+    static JSObjectOps fastObjectOps;
+    static JSObjectMap fastObjectMap;
+
     static JSFunctionSpec jsfuncs[];
 
     static inline Class *slowClass()
     {
         return &TypedArray::slowClasses[ArrayTypeID()];
     }
 
     static inline Class *fastClass()
     {
         return &TypedArray::fastClasses[ArrayTypeID()];
     }
 
+    static JSObjectOps *getObjectOps(JSContext *cx, Class *clasp)
+    {
+        return &fastObjectOps;
+    }
+
     static JSBool
     obj_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
     {
         ThisTypeArray *tarray = ThisTypeArray::fromJSObject(obj);
         JS_ASSERT(tarray);
 
         if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
             vp->setNumber(tarray->length);
@@ -913,17 +921,17 @@ class TypedArrayTemplate
 
     // helper used by both the constructor and Slice()
     static void
     makeFastWithPrivate(JSContext *cx, JSObject *obj, ThisTypeArray *tarray)
     {
         JS_ASSERT(obj->getClass() == slowClass());
         obj->setPrivate(tarray);
         obj->clasp = fastClass();
-        obj->map = const_cast<JSObjectMap *>(&JSObjectMap::sharedNonNative);
+        obj->map = &fastObjectMap;
     }
 
   public:
     TypedArrayTemplate() { }
 
     bool
     init(JSContext *cx, uint32 len)
     {
@@ -1251,24 +1259,19 @@ TypedArrayTemplate<double>::copyIndexToV
 
 /*
  * ArrayBuffer (base)
  */
 
 Class ArrayBuffer::jsclass = {
     "ArrayBuffer",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    ArrayBuffer::class_finalize,
+    PropertyStub,     PropertyStub,   PropertyStub,   PropertyStub,
+    EnumerateStub,    ResolveStub,    ConvertStub,    ArrayBuffer::class_finalize,
+    JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
 JSPropertySpec ArrayBuffer::jsprops[] = {
     { "byteLength",
       -1, JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY,
       Jsvalify(ArrayBuffer::prop_getByteLength), Jsvalify(ArrayBuffer::prop_getByteLength) },
     {0,0,0,0,0}
 };
@@ -1293,69 +1296,56 @@ JSPropertySpec TypedArray::jsprops[] = {
     {0,0,0,0,0}
 };
 
 /*
  * TypedArray boilerplate
  */
 
 #define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                  \
+template<> JSObjectMap _typedArray::fastObjectMap(&_typedArray::fastObjectOps, \
+                                                  JSObjectMap::SHAPELESS);     \
+template<> JSObjectOps _typedArray::fastObjectOps = {                          \
+    &_typedArray::fastObjectMap,                                               \
+    _typedArray::obj_lookupProperty,                                           \
+    _typedArray::obj_defineProperty,                                           \
+    _typedArray::obj_getProperty,                                              \
+    _typedArray::obj_setProperty,                                              \
+    _typedArray::obj_getAttributes,                                            \
+    _typedArray::obj_setAttributes,                                            \
+    _typedArray::obj_deleteProperty,                                           \
+    _typedArray::obj_enumerate,                                                \
+    _typedArray::obj_typeOf,                                                   \
+    _typedArray::obj_trace,                                                    \
+    NULL,   /* thisObject */                                                   \
+    NULL    /* clear */                                                        \
+};                                                                             \
 template<> JSFunctionSpec _typedArray::jsfuncs[] = {                           \
     JS_FN("slice", _typedArray::fun_slice, 2, 0),                              \
     JS_FS_END                                                                  \
 }
 
 #define IMPL_TYPED_ARRAY_SLOW_CLASS(_typedArray)                               \
 {                                                                              \
     #_typedArray,                                                              \
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray),     \
-    PropertyStub,   /* addProperty */                                          \
-    PropertyStub,   /* delProperty */                                          \
-    PropertyStub,   /* getProperty */                                          \
-    PropertyStub,   /* setProperty */                                          \
-    EnumerateStub,                                                             \
-    ResolveStub,                                                               \
-    ConvertStub,                                                               \
-    FinalizeStub                                                               \
+    PropertyStub, PropertyStub, PropertyStub, PropertyStub,                    \
+    EnumerateStub, ResolveStub, ConvertStub, FinalizeStub,                     \
+    JSCLASS_NO_OPTIONAL_MEMBERS                                                \
 }
 
 #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray)                               \
 {                                                                              \
     #_typedArray,                                                              \
-    Class::NON_NATIVE | JSCLASS_HAS_PRIVATE,                                   \
-    PropertyStub,   /* addProperty */                                          \
-    PropertyStub,   /* delProperty */                                          \
-    PropertyStub,   /* getProperty */                                          \
-    PropertyStub,   /* setProperty */                                          \
-    EnumerateStub,                                                             \
-    ResolveStub,                                                               \
-    ConvertStub,                                                               \
+    JSCLASS_HAS_PRIVATE,                                                       \
+    PropertyStub, PropertyStub, PropertyStub, PropertyStub,                    \
+    EnumerateStub, ResolveStub, ConvertStub,                                   \
     _typedArray::class_finalize,                                               \
-    NULL,           /* reserved0   */                                          \
-    NULL,           /* checkAccess */                                          \
-    NULL,           /* call        */                                          \
-    NULL,           /* construct   */                                          \
-    NULL,           /* xdrObject   */                                          \
-    NULL,           /* hasInstance */                                          \
-    NULL,           /* mark        */                                          \
-    JS_NULL_CLASS_EXT,                                                         \
-    {                                                                          \
-        _typedArray::obj_lookupProperty,                                       \
-        _typedArray::obj_defineProperty,                                       \
-        _typedArray::obj_getProperty,                                          \
-        _typedArray::obj_setProperty,                                          \
-        _typedArray::obj_getAttributes,                                        \
-        _typedArray::obj_setAttributes,                                        \
-        _typedArray::obj_deleteProperty,                                       \
-        _typedArray::obj_enumerate,                                            \
-        _typedArray::obj_typeOf,                                               \
-        _typedArray::obj_trace,                                                \
-        NULL,       /* thisObject      */                                      \
-        NULL,       /* clear           */                                      \
-    }                                                                          \
+    _typedArray::getObjectOps, NULL, NULL, NULL,                               \
+    NULL, NULL, NULL, NULL                                                     \
 }
 
 #define INIT_TYPED_ARRAY_CLASS(_typedArray,_type)                              \
 do {                                                                           \
     proto = js_InitClass(cx, obj, NULL,                                        \
                          &TypedArray::slowClasses[TypedArray::_type],          \
                          _typedArray::class_constructor, 3,                    \
                          _typedArray::jsprops,                                 \
--- a/js/src/jsvalue.h
+++ b/js/src/jsvalue.h
@@ -774,16 +774,18 @@ typedef JSBool
 typedef JSBool
 (* NewEnumerateOp)(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                    Value *statep, jsid *idp);
 typedef JSBool
 (* HasInstanceOp)(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp);
 typedef JSBool
 (* CheckAccessOp)(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                   Value *vp);
+typedef JSObjectOps *
+(* GetObjectOps)(JSContext *cx, Class *clasp);
 typedef JSBool
 (* EqualityOp)(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp);
 typedef JSBool
 (* DefinePropOp)(JSContext *cx, JSObject *obj, jsid id, const Value *value,
                  PropertyOp getter, PropertyOp setter, uintN attrs);
 typedef JSBool
 (* PropertyIdOp)(JSContext *cx, JSObject *obj, jsid id, Value *vp);
 typedef JSBool
@@ -798,135 +800,101 @@ static inline JSPropertyOp      Jsvalify
 static inline ConvertOp         Valueify(JSConvertOp f)       { return (ConvertOp)f; }
 static inline JSConvertOp       Jsvalify(ConvertOp f)         { return (JSConvertOp)f; }
 static inline NewEnumerateOp    Valueify(JSNewEnumerateOp f)  { return (NewEnumerateOp)f; }
 static inline JSNewEnumerateOp  Jsvalify(NewEnumerateOp f)    { return (JSNewEnumerateOp)f; }
 static inline HasInstanceOp     Valueify(JSHasInstanceOp f)   { return (HasInstanceOp)f; }
 static inline JSHasInstanceOp   Jsvalify(HasInstanceOp f)     { return (JSHasInstanceOp)f; }
 static inline CheckAccessOp     Valueify(JSCheckAccessOp f)   { return (CheckAccessOp)f; }
 static inline JSCheckAccessOp   Jsvalify(CheckAccessOp f)     { return (JSCheckAccessOp)f; }
+static inline GetObjectOps      Valueify(JSGetObjectOps f)    { return (GetObjectOps)f; }
+static inline JSGetObjectOps    Jsvalify(GetObjectOps f)      { return (JSGetObjectOps)f; }
 static inline EqualityOp        Valueify(JSEqualityOp f);     /* Same type as JSHasInstanceOp */
 static inline JSEqualityOp      Jsvalify(EqualityOp f);       /* Same type as HasInstanceOp */
 static inline DefinePropOp      Valueify(JSDefinePropOp f)    { return (DefinePropOp)f; }
 static inline JSDefinePropOp    Jsvalify(DefinePropOp f)      { return (JSDefinePropOp)f; }
 static inline PropertyIdOp      Valueify(JSPropertyIdOp f);   /* Same type as JSPropertyOp */
 static inline JSPropertyIdOp    Jsvalify(PropertyIdOp f);     /* Same type as PropertyOp */
 static inline CallOp            Valueify(JSCallOp f);         /* Same type as JSFastNative */
 static inline JSCallOp          Jsvalify(CallOp f);           /* Same type as FastNative */
 
 static const PropertyOp    PropertyStub  = (PropertyOp)JS_PropertyStub;
 static const JSEnumerateOp EnumerateStub = JS_EnumerateStub;
 static const JSResolveOp   ResolveStub   = JS_ResolveStub;
 static const ConvertOp     ConvertStub   = (ConvertOp)JS_ConvertStub;
 static const JSFinalizeOp  FinalizeStub  = JS_FinalizeStub;
 
-#define JS_CLASS_MEMBERS                                                      \
-    const char          *name;                                                \
-    uint32              flags;                                                \
-                                                                              \
-    /* Mandatory non-null function pointer members. */                        \
-    PropertyOp          addProperty;                                          \
-    PropertyOp          delProperty;                                          \
-    PropertyOp          getProperty;                                          \
-    PropertyOp          setProperty;                                          \
-    JSEnumerateOp       enumerate;                                            \
-    JSResolveOp         resolve;                                              \
-    ConvertOp           convert;                                              \
-    JSFinalizeOp        finalize;                                             \
-                                                                              \
-    /* Optionally non-null members start here. */                             \
-    JSClassInternal     reserved0;                                            \
-    CheckAccessOp       checkAccess;                                          \
-    Native              call;                                                 \
-    Native              construct;                                            \
-    JSXDRObjectOp       xdrObject;                                            \
-    HasInstanceOp       hasInstance;                                          \
-    JSMarkOp            mark
-
-
-/*
- * The helper struct to measure the size of JS_CLASS_MEMBERS to know how much
- * we have to padd js::Class to match the size of JSClass;
- */
-struct ClassSizeMeasurement {
-    JS_CLASS_MEMBERS;
-};
-
-struct ClassExtension {
-    EqualityOp          equality;
-    JSObjectOp          outerObject;
-    JSObjectOp          innerObject;
-    JSIteratorOp        iteratorObject;
-    JSObjectOp          wrappedObject;  /* NB: infallible, null returns are
-                                           treated as the original object */
-};
-
-#define JS_NULL_CLASS_EXT   {NULL,NULL,NULL,NULL,NULL}
+struct Class {
+    const char          *name;
+    uint32              flags;
 
-struct ObjectOps {
-    JSLookupPropOp      lookupProperty;
-    js::DefinePropOp    defineProperty;
-    js::PropertyIdOp    getProperty;
-    js::PropertyIdOp    setProperty;
-    JSAttributesOp      getAttributes;
-    JSAttributesOp      setAttributes;
-    js::PropertyIdOp    deleteProperty;
-    js::NewEnumerateOp  enumerate;
-    JSTypeOfOp          typeOf;
-    JSTraceOp           trace;
-    JSObjectOp          thisObject;
-    JSFinalizeOp        clear;
-};
-
-#define JS_NULL_OBJECT_OPS  {NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,NULL,NULL}
+    /* Mandatory non-null function pointer members. */
+    PropertyOp          addProperty;
+    PropertyOp          delProperty;
+    PropertyOp          getProperty;
+    PropertyOp          setProperty;
+    JSEnumerateOp       enumerate;
+    JSResolveOp         resolve;
+    ConvertOp           convert;
+    JSFinalizeOp        finalize;
 
-struct Class {
-    JS_CLASS_MEMBERS;
-    ClassExtension      ext;
-    ObjectOps           ops;
-    uint8               pad[sizeof(JSClass) - sizeof(ClassSizeMeasurement) -
-                            sizeof(ClassExtension) - sizeof(ObjectOps)];
-
-    /* Flag indicating that Class::call is a fast native. */
-    static const uint32 CALL_IS_FAST = JSCLASS_INTERNAL_FLAG1;
-
-    /* Class is not native and its map is not a scope. */
-    static const uint32 NON_NATIVE = JSCLASS_INTERNAL_FLAG2;
-
-    bool isNative() const {
-        return !(flags & NON_NATIVE);
-    }
+    /* Optionally non-null members start here. */
+    GetObjectOps        getObjectOps;
+    CheckAccessOp       checkAccess;
+    Native              call;
+    Native              construct;
+    JSXDRObjectOp       xdrObject;
+    HasInstanceOp       hasInstance;
+    JSMarkOp            mark;
+    void                (*reserved0)(void);
 };
-
-/* Helper to initialize Class::call when Class::CALL_IS_FAST. */
-inline Native
-CastCallOpAsNative(CallOp op)
-{
-    return reinterpret_cast<Native>(op);
-}
-
 JS_STATIC_ASSERT(offsetof(JSClass, name) == offsetof(Class, name));
 JS_STATIC_ASSERT(offsetof(JSClass, flags) == offsetof(Class, flags));
 JS_STATIC_ASSERT(offsetof(JSClass, addProperty) == offsetof(Class, addProperty));
 JS_STATIC_ASSERT(offsetof(JSClass, delProperty) == offsetof(Class, delProperty));
 JS_STATIC_ASSERT(offsetof(JSClass, getProperty) == offsetof(Class, getProperty));
 JS_STATIC_ASSERT(offsetof(JSClass, setProperty) == offsetof(Class, setProperty));
 JS_STATIC_ASSERT(offsetof(JSClass, enumerate) == offsetof(Class, enumerate));
 JS_STATIC_ASSERT(offsetof(JSClass, resolve) == offsetof(Class, resolve));
 JS_STATIC_ASSERT(offsetof(JSClass, convert) == offsetof(Class, convert));
 JS_STATIC_ASSERT(offsetof(JSClass, finalize) == offsetof(Class, finalize));
-JS_STATIC_ASSERT(offsetof(JSClass, reserved0) == offsetof(Class, reserved0));
+JS_STATIC_ASSERT(offsetof(JSClass, getObjectOps) == offsetof(Class, getObjectOps));
 JS_STATIC_ASSERT(offsetof(JSClass, checkAccess) == offsetof(Class, checkAccess));
 JS_STATIC_ASSERT(offsetof(JSClass, call) == offsetof(Class, call));
 JS_STATIC_ASSERT(offsetof(JSClass, construct) == offsetof(Class, construct));
 JS_STATIC_ASSERT(offsetof(JSClass, xdrObject) == offsetof(Class, xdrObject));
 JS_STATIC_ASSERT(offsetof(JSClass, hasInstance) == offsetof(Class, hasInstance));
 JS_STATIC_ASSERT(offsetof(JSClass, mark) == offsetof(Class, mark));
+JS_STATIC_ASSERT(offsetof(JSClass, reserved0) == offsetof(Class, reserved0));
 JS_STATIC_ASSERT(sizeof(JSClass) == sizeof(Class));
 
+struct ExtendedClass {
+    Class               base;
+    EqualityOp          equality;
+    JSObjectOp          outerObject;
+    JSObjectOp          innerObject;
+    JSIteratorOp        iteratorObject;
+    JSObjectOp          wrappedObject;          /* NB: infallible, null
+                                                   returns are treated as
+                                                   the original object */
+    void                (*reserved0)(void);
+    void                (*reserved1)(void);
+    void                (*reserved2)(void);
+};
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, base) == offsetof(ExtendedClass, base));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, equality) == offsetof(ExtendedClass, equality));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, outerObject) == offsetof(ExtendedClass, outerObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, innerObject) == offsetof(ExtendedClass, innerObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, iteratorObject) == offsetof(ExtendedClass, iteratorObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, wrappedObject) == offsetof(ExtendedClass, wrappedObject));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, reserved0) == offsetof(ExtendedClass, reserved0));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, reserved1) == offsetof(ExtendedClass, reserved1));
+JS_STATIC_ASSERT(offsetof(JSExtendedClass, reserved2) == offsetof(ExtendedClass, reserved2));
+JS_STATIC_ASSERT(sizeof(JSExtendedClass) == sizeof(ExtendedClass));
+
 struct PropertyDescriptor {
     JSObject     *obj;
     uintN        attrs;
     PropertyOp   getter;
     PropertyOp   setter;
     Value        value;
     uintN        shortid;
 };
@@ -935,16 +903,18 @@ JS_STATIC_ASSERT(offsetof(JSPropertyDesc
 JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, getter) == offsetof(PropertyDescriptor, getter));
 JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, setter) == offsetof(PropertyDescriptor, setter));
 JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, value) == offsetof(PropertyDescriptor, value));
 JS_STATIC_ASSERT(offsetof(JSPropertyDescriptor, shortid) == offsetof(PropertyDescriptor, shortid));
 JS_STATIC_ASSERT(sizeof(JSPropertyDescriptor) == sizeof(PropertyDescriptor));
 
 static JS_ALWAYS_INLINE JSClass *              Jsvalify(Class *c)                { return (JSClass *)c; }
 static JS_ALWAYS_INLINE Class *                Valueify(JSClass *c)              { return (Class *)c; }
+static JS_ALWAYS_INLINE JSExtendedClass *      Jsvalify(ExtendedClass *c)        { return (JSExtendedClass *)c; }
+static JS_ALWAYS_INLINE ExtendedClass *        Valueify(JSExtendedClass *c)      { return (ExtendedClass *)c; }
 static JS_ALWAYS_INLINE JSPropertyDescriptor * Jsvalify(PropertyDescriptor *p) { return (JSPropertyDescriptor *) p; }
 static JS_ALWAYS_INLINE PropertyDescriptor *   Valueify(JSPropertyDescriptor *p) { return (PropertyDescriptor *) p; }
 
 /******************************************************************************/
 
 /*
  * In some cases (quickstubs) we want to take a value in whatever manner is
  * appropriate for the architecture and normalize to a const js::Value &. On
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -679,17 +679,17 @@ JSCrossCompartmentWrapper::enumerateOwn(
  * We can reify non-escaping iterator objects instead of having to wrap them. This
  * allows fast iteration over objects across a compartment boundary.
  */
 static bool
 CanReify(Value *vp)
 {
     JSObject *obj;
     return vp->isObject() &&
-           (obj = &vp->toObject())->getClass() == &js_IteratorClass &&
+           (obj = &vp->toObject())->getClass() == &js_IteratorClass.base &&
            (obj->getNativeIterator()->flags & JSITER_ENUMERATE);
 }
 
 static bool
 Reify(JSContext *cx, JSCompartment *origin, Value *vp)
 {
     JSObject *iterObj = &vp->toObject();
     NativeIterator *ni = iterObj->getNativeIterator();
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -185,17 +185,17 @@ GetLocalName(const JSObject *obj)
     return JSVAL_TO_STRING(v);
 }
 
 static JSBool
 IsDeclared(const JSObject *obj)
 {
     jsval v;
 
-    JS_ASSERT(obj->getClass() == &js_NamespaceClass);
+    JS_ASSERT(obj->getClass() == &js_NamespaceClass.base);
     v = obj->getNamespaceDeclared();
     JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
     return v == JSVAL_TRUE;
 }
 
 static JSBool
 xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
               jsval *rval)
@@ -219,67 +219,51 @@ AppendString(JSCharBuffer &cb, JSString 
         code;                                                                  \
         return true;                                                           \
     }
 
 /*
  * Namespace class and library functions.
  */
 DEFINE_GETTER(NamePrefix_getter,
-              if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNamePrefix())
+              if (obj->getClass() == &js_NamespaceClass.base) *vp = obj->getNamePrefix())
 DEFINE_GETTER(NameURI_getter,
-              if (obj->getClass() == &js_NamespaceClass) *vp = obj->getNameURI())
+              if (obj->getClass() == &js_NamespaceClass.base) *vp = obj->getNameURI())
 
 static void
 namespace_finalize(JSContext *cx, JSObject *obj)
 {
     if (cx->runtime->functionNamespaceObject == obj)
         cx->runtime->functionNamespaceObject = NULL;
 }
 
 static JSBool
 namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
 {
     JSObject *obj2;
 
     JS_ASSERT(v->isObjectOrNull());
     obj2 = v->toObjectOrNull();
-    *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass)
+    *bp = (!obj2 || obj2->getClass() != &js_NamespaceClass.base)
           ? JS_FALSE
           : js_EqualStrings(GetURI(obj), GetURI(obj2));
     return JS_TRUE;
 }
 
-JS_FRIEND_DATA(Class) js_NamespaceClass = {
-    "Namespace",
-    JSCLASS_CONSTRUCT_PROTOTYPE |
+JS_FRIEND_DATA(ExtendedClass) js_NamespaceClass = {
+  { "Namespace",
+    JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    namespace_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    {
-        namespace_equality,
-        NULL,       /* outerObject    */
-        NULL,       /* innerObject    */
-        NULL,       /* iteratorObject */
-        NULL,       /* wrappedObject  */
-    }
+    PropertyStub,      PropertyStub,      PropertyStub,      PropertyStub,
+    EnumerateStub,     ResolveStub,       ConvertStub,       namespace_finalize,
+    NULL,              NULL,              NULL,              NULL,
+    NULL,              NULL,              NULL,              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, 0, NAMESPACE_ATTRS, NamePrefix_getter, 0},
     {js_uri_str,    0, NAMESPACE_ATTRS, NameURI_getter,    0},
@@ -287,33 +271,33 @@ static JSPropertySpec namespace_props[] 
 };
 
 static JSBool
 namespace_toString(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
 
     obj = JS_THIS_OBJECT(cx, vp);
-    if (!JS_InstanceOf(cx, obj, Jsvalify(&js_NamespaceClass), vp + 2))
+    if (!JS_InstanceOf(cx, obj, Jsvalify(&js_NamespaceClass.base), vp + 2))
         return JS_FALSE;
     *vp = obj->getNameURI();
     return JS_TRUE;
 }
 
 static JSFunctionSpec namespace_methods[] = {
     JS_FN(js_toString_str,  namespace_toString,        0,0),
     JS_FS_END
 };
 
 static JSObject *
 NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
 {
     JSObject *obj;
 
-    obj = NewBuiltinClassInstance(cx, &js_NamespaceClass);
+    obj = NewBuiltinClassInstance(cx, &js_NamespaceClass.base);
     if (!obj)
         return JS_FALSE;
     JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getNameURI()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
     if (prefix)
         obj->setNamePrefix(STRING_TO_JSVAL(prefix));
     if (uri)
@@ -323,20 +307,20 @@ NewXMLNamespace(JSContext *cx, JSString 
     METER(xml_stats.xmlnamespace);
     return obj;
 }
 
 /*
  * QName class and library functions.
  */
 DEFINE_GETTER(QNameNameURI_getter,
-              if (obj->getClass() == &js_QNameClass)
+              if (obj->getClass() == &js_QNameClass.base)
                   *vp = JSVAL_IS_VOID(obj->getNameURI()) ? JSVAL_NULL : obj->getNameURI())
 DEFINE_GETTER(QNameLocalName_getter,
-              if (obj->getClass() == &js_QNameClass)
+              if (obj->getClass() == &js_QNameClass.base)
                   *vp = obj->getQNameLocalName())
 
 static void
 anyname_finalize(JSContext* cx, JSObject* obj)
 {
     /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
     if (cx->runtime->anynameObject == obj)
         cx->runtime->anynameObject = NULL;
@@ -356,87 +340,65 @@ qname_identity(JSObject *qna, JSObject *
 }
 
 static JSBool
 qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp)
 {
     JSObject *obj2;
 
     obj2 = v->toObjectOrNull();
-    *bp = (!obj2 || obj2->getClass() != &js_QNameClass)
+    *bp = (!obj2 || obj2->getClass() != &js_QNameClass.base)
           ? JS_FALSE
           : qname_identity(qn, obj2);
     return JS_TRUE;
 }
 
-JS_FRIEND_DATA(Class) js_QNameClass = {
-    "QName",
-    JSCLASS_CONSTRUCT_PROTOTYPE |
+JS_FRIEND_DATA(ExtendedClass) js_QNameClass = {
+  { "QName",
+    JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    FinalizeStub,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    NULL,           /* mark        */
-    {
-        qname_equality,
-        NULL,       /* outerObject    */
-        NULL,       /* innerObject    */
-        NULL,       /* iteratorObject */
-        NULL,       /* wrappedObject  */
-    }
+    PropertyStub,      PropertyStub,      PropertyStub,      PropertyStub,
+    EnumerateStub,     ResolveStub,       ConvertStub,       NULL,
+    NULL,              NULL,              NULL,              NULL,
+    NULL,              NULL,              NULL,              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(Class) js_AttributeNameClass = {
     js_AttributeName_str,
     JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub
+    PropertyStub,      PropertyStub,      PropertyStub,      PropertyStub,
+    EnumerateStub,     ResolveStub,       ConvertStub,       NULL,
+    NULL,              NULL,              NULL,              NULL,
+    NULL,              NULL,              NULL,              NULL
 };
 
 JS_FRIEND_DATA(Class) js_AnyNameClass = {
     js_AnyName_str,
     JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_FIXED_RESERVED_SLOTS) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    anyname_finalize
+    PropertyStub,      PropertyStub,      PropertyStub,      PropertyStub,
+    EnumerateStub,     ResolveStub,       ConvertStub,       anyname_finalize,
+    NULL,              NULL,              NULL,              NULL,
+    NULL,              NULL,              NULL,              NULL
 };
 
-#define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
+#define QNAME_ATTRS                                                           \
+    (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
 
 static JSPropertySpec qname_props[] = {
     {js_uri_str,       0, QNAME_ATTRS, QNameNameURI_getter,   0},
     {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0},
     {0,0,0,0,0}
 };
 
 static JSBool
@@ -449,17 +411,17 @@ qname_toString(JSContext *cx, uintN argc
     jschar *chars;
 
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
         return JS_FALSE;
     clasp = obj->getClass();
     if (clasp != &js_AttributeNameClass &&
         clasp != &js_AnyNameClass &&
-        !JS_InstanceOf(cx, obj, Jsvalify(&js_QNameClass), vp + 2)) {
+        !JS_InstanceOf(cx, obj, Jsvalify(&js_QNameClass.base), vp + 2)) {
             return JS_FALSE;
     }
 
     uri = GetURI(obj);
     if (!uri) {
         /* No uri means wildcard qualifier. */
         str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
     } else if (uri->empty()) {
@@ -512,17 +474,17 @@ InitXMLQName(JSObject *obj, JSString *ur
     if (prefix)
         obj->setNamePrefix(STRING_TO_JSVAL(prefix));
     if (localName)
         obj->setQNameLocalName(STRING_TO_JSVAL(localName));
 }
 
 static JSObject *
 NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
-            Class *clasp = &js_QNameClass)
+            Class *clasp = &js_QNameClass.base)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, clasp);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isQName());
     InitXMLQName(obj, uri, prefix, localName);
     METER(xml_stats.qname);
     return obj;
@@ -540,17 +502,17 @@ js_ConstructXMLQNameObject(JSContext *cx
      */
     if (nsval.isObject() &&
         nsval.toObject().getClass() == &js_AnyNameClass) {
         argv[0].setNull();
     } else {
         argv[0] = nsval;
     }
     argv[1] = lnval;
-    return js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, argv);
+    return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
 }
 
 static JSBool
 IsXMLName(const jschar *cp, size_t n)
 {
     JSBool rv;
     jschar c;
 
@@ -614,30 +576,30 @@ NamespaceHelper(JSContext *cx, JSObject 
 #endif
     if (argc <= 0) {
         urival = JSVAL_VOID;
     } else {
         urival = argv[argc > 1];
         if (!JSVAL_IS_PRIMITIVE(urival)) {
             uriobj = JSVAL_TO_OBJECT(urival);
             clasp = uriobj->getClass();
-            isNamespace = (clasp == &js_NamespaceClass);
-            isQName = (clasp == &js_QNameClass);
+            isNamespace = (clasp == &js_NamespaceClass.base);
+            isQName = (clasp == &js_QNameClass.base);
         }
     }
 
     if (!obj) {
         /* Namespace called as function. */
         if (argc == 1 && isNamespace) {
             /* Namespace called with one Namespace argument is identity. */
             *rval = urival;
             return JS_TRUE;
         }
 
-        obj = NewBuiltinClassInstance(cx, &js_NamespaceClass);
+        obj = NewBuiltinClassInstance(cx, &js_NamespaceClass.base);
         if (!obj)
             return JS_FALSE;
         *rval = OBJECT_TO_JSVAL(obj);
     }
     METER(xml_stats.xmlnamespace);
 
     empty = cx->runtime->emptyString;
     obj->setNamePrefix(STRING_TO_JSVAL(empty));
@@ -710,26 +672,26 @@ QNameHelper(JSContext *cx, JSObject *obj
             jsval *argv, jsval *rval)
 {
     jsval nameval, nsval;
     JSBool isQName, isNamespace;
     JSObject *qn;
     JSString *uri, *prefix, *name;
     JSObject *obj2;
 
-    JS_ASSERT(clasp == &js_QNameClass ||
+    JS_ASSERT(clasp == &js_QNameClass.base ||
               clasp == &js_AttributeNameClass);
     if (argc <= 0) {
         nameval = JSVAL_VOID;
         isQName = JS_FALSE;
     } else {
         nameval = argv[argc > 1];
         isQName =
             !JSVAL_IS_PRIMITIVE(nameval) &&
-            JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass;
+            JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass.base;
     }
 
     if (!obj) {
         /* QName called as function. */
         if (argc == 1 && isQName) {
             /* QName called with one QName argument is identity. */
             *rval = nameval;
             return JS_TRUE;
@@ -775,17 +737,17 @@ QNameHelper(JSContext *cx, JSObject *obj
         nsval = argv[0];
     } else if (IS_STAR(name)) {
         nsval = JSVAL_NULL;
     } else {
         if (!js_GetDefaultXMLNamespace(cx, &nsval))
             return JS_FALSE;
         JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
         JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
-                  &js_NamespaceClass);
+                  &js_NamespaceClass.base);
     }
 
     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
@@ -793,18 +755,18 @@ QNameHelper(JSContext *cx, JSObject *obj
          * 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)) {
             obj2 = JSVAL_TO_OBJECT(nsval);
             clasp = obj2->getClass();
-            isNamespace = (clasp == &js_NamespaceClass);
-            isQName = (clasp == &js_QNameClass);
+            isNamespace = (clasp == &js_NamespaceClass.base);
+            isQName = (clasp == &js_QNameClass.base);
         }
 #ifdef __GNUC__         /* suppress bogus gcc warnings */
         else obj2 = NULL;
 #endif
 
         if (isNamespace) {
             uri = GetURI(obj2);
             prefix = GetPrefix(obj2);
@@ -827,17 +789,17 @@ out:
     InitXMLQName(obj, uri, prefix, name);
     return JS_TRUE;
 }
 
 static JSBool
 QName(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     return QNameHelper(cx, cx->isConstructing() ? obj : NULL,
-                       &js_QNameClass, argc, Jsvalify(argv), Jsvalify(rval));
+                       &js_QNameClass.base, argc, Jsvalify(argv), Jsvalify(rval));
 }
 
 static JSBool
 AttributeName(JSContext *cx, JSObject *obj, uintN argc, Value *argv,
               Value *rval)
 {
     return QNameHelper(cx, cx->isConstructing() ? obj : NULL,
                        &js_AttributeNameClass, argc, Jsvalify(argv), Jsvalify(rval));
@@ -2192,17 +2154,17 @@ GetNamespace(JSContext *cx, JSObject *qn
             }
         }
     }
 
     /* If we didn't match, make a new namespace from qn. */
     if (!match) {
         argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
         argv[1] = STRING_TO_JSVAL(uri);
-        ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL,
+        ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
                                 2, Valueify(argv));
         if (!ns)
             return NULL;
         match = ns;
     }
     return match;
 }
 
@@ -2747,17 +2709,17 @@ ToAttributeName(JSContext *cx, jsval v)
             return NULL;
         }
 
         obj = JSVAL_TO_OBJECT(v);
         clasp = obj->getClass();
         if (clasp == &js_AttributeNameClass)
             return obj;
 
-        if (clasp == &js_QNameClass) {
+        if (clasp == &js_QNameClass.base) {
             qn = obj;
             uri = GetURI(qn);
             prefix = GetPrefix(qn);
             name = GetLocalName(qn);
         } else {
             if (clasp == &js_AnyNameClass) {
                 name = ATOM_TO_STRING(cx->runtime->atomState.starAtom);
             } else {
@@ -2796,17 +2758,17 @@ IsFunctionQName(JSContext *cx, JSObject 
     }
     *funidp = JSID_VOID;
     return JS_TRUE;
 }
 
 JSBool
 js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp)
 {
-    if (obj->getClass() == &js_QNameClass)
+    if (obj->getClass() == &js_QNameClass.base)
         return IsFunctionQName(cx, obj, funidp);
     *funidp = JSID_VOID;
     return JS_TRUE;
 }
 
 static JSObject *
 ToXMLName(JSContext *cx, jsval v, jsid *funidp)
 {
@@ -2821,17 +2783,17 @@ ToXMLName(JSContext *cx, jsval v, jsid *
     } else {
         if (JSVAL_IS_PRIMITIVE(v)) {
             ReportBadXMLName(cx, Valueify(v));
             return NULL;
         }
 
         obj = JSVAL_TO_OBJECT(v);
         clasp = obj->getClass();
-        if (clasp == &js_AttributeNameClass || clasp == &js_QNameClass)
+        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, Valueify(v));
         if (!name)
             return NULL;
@@ -2862,17 +2824,17 @@ ToXMLName(JSContext *cx, jsval v, jsid *
         if (!name)
             return NULL;
         *funidp = JSID_VOID;
         return ToAttributeName(cx, STRING_TO_JSVAL(name));
     }
 
 construct:
     v = STRING_TO_JSVAL(name);
-    obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v));
+    obj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, Valueify(&v));
     if (!obj)
         return NULL;
 
 out:
     if (!IsFunctionQName(cx, obj, funidp))
         return NULL;
     return obj;
 
@@ -4634,23 +4596,23 @@ xml_trace_vector(JSTracer *trc, JSXML **
         if (xml) {
             JS_SET_TRACING_INDEX(trc, "xml_vector", i);
             Mark(trc, xml, JSTRACE_XML);
         }
     }
 }
 
 /*
- * XML objects are native. Thus xml_lookupProperty must return a valid
- * JSScopeProperty pointer parameter via *propp to signify "property found".
- * Since the only call to xml_lookupProperty is via JSObject::lookupProperty,
- * and then only from 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 is being accessed or when
- * "name in xml" is called.
+ * js_XMLObjectOps.newObjectMap is null, so XML objects appear to be native.
+ * Thus xml_lookupProperty must return a valid JSScopeProperty pointer
+ * parameter via *propp to signify "property found".  Since the only call to
+ * xml_lookupProperty is via JSObject::lookupProperty, and then only from
+ * 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 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
@@ -5063,49 +5025,47 @@ js_ConcatenateXML(JSContext *cx, JSObjec
         goto out;
 
     vp->setObject(*listobj);
 out:
     js_LeaveLocalRootScopeWithResult(cx, *vp);
     return ok;
 }
 
+/* Use NULL for objectMap so XML objects satisfy obj->isNative() tests. */
+JS_FRIEND_DATA(JSObjectOps) js_XMLObjectOps = {
+    NULL,
+    xml_lookupProperty,
+    xml_defineProperty,
+    xml_getProperty,
+    xml_setProperty,
+    xml_getAttributes,
+    xml_setAttributes,
+    xml_deleteProperty,
+    xml_enumerate,
+    xml_typeOf,
+    js_TraceObject,
+    NULL,   /* thisObject */
+    xml_clear
+};
+
+static JSObjectOps *
+xml_getObjectOps(JSContext *cx, Class *clasp)
+{
+    return &js_XMLObjectOps;
+}
+
 JS_FRIEND_DATA(Class) js_XMLClass = {
     js_XML_str,
-    JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    xml_convert,
-    xml_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    xml_hasInstance,
-    JS_CLASS_TRACE(xml_trace),
-    JS_NULL_CLASS_EXT,
-    {
-        xml_lookupProperty,
-        xml_defineProperty,
-        xml_getProperty,
-        xml_setProperty,
-        xml_getAttributes,
-        xml_setAttributes,
-        xml_deleteProperty,
-        xml_enumerate,
-        xml_typeOf,
-        NULL,       /* trace */
-        NULL,       /* thisObject     */
-        xml_clear
-    }
+    JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
+    JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
+    PropertyStub,      PropertyStub,      PropertyStub,      PropertyStub,
+    EnumerateStub,     ResolveStub,       xml_convert,       xml_finalize,
+    xml_getObjectOps,  NULL,              NULL,              NULL,
+    NULL,              xml_hasInstance,   JS_CLASS_TRACE(xml_trace), NULL
 };
 
 static JSXML *
 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
 {
     JSXML *xml;
     JSFunction *fun;
     char numBuf[12];
@@ -6289,17 +6249,17 @@ xml_replace(JSContext *cx, uintN argc, j
     if (!xml)
         return JS_FALSE;
 
     if (argc == 0 || !js_IdValIsIndex(vp[2], &index)) {
         /*
          * Call function QName per spec, not ToXMLName, to avoid attribute
          * names.
          */
-        if (!QNameHelper(cx, NULL, &js_QNameClass, argc == 0 ? -1 : 1,
+        if (!QNameHelper(cx, NULL, &js_QNameClass.base, argc == 0 ? -1 : 1,
                          vp + 2, vp)) {
             return JS_FALSE;
         }
         JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
         nameqn = JSVAL_TO_OBJECT(*vp);
 
         i = xml->xml_kids.length;
         index = XML_NOT_FOUND;
@@ -6352,17 +6312,17 @@ xml_setLocalName(JSContext *cx, uintN ar
     if (!JSXML_HAS_NAME(xml))
         return JS_TRUE;
 
     if (argc == 0) {
         namestr = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
     } else {
         name = vp[2];
         if (!JSVAL_IS_PRIMITIVE(name) &&
-            JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) {
+            JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass.base) {
             nameqn = JSVAL_TO_OBJECT(name);
             namestr = GetLocalName(nameqn);
         } else {
             if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2]))
                 return JS_FALSE;
             name = vp[2];
             namestr = JSVAL_TO_STRING(name);
         }
@@ -6389,23 +6349,23 @@ xml_setName(JSContext *cx, uintN argc, j
     if (!JSXML_HAS_NAME(xml))
         return JS_TRUE;
 
     if (argc == 0) {
         name = ATOM_TO_JSVAL(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
     } else {
         name = vp[2];
         if (!JSVAL_IS_PRIMITIVE(name) &&
-            JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass &&
+            JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass.base &&
             !GetURI(nameqn = JSVAL_TO_OBJECT(name))) {
             name = vp[2] = nameqn->getQNameLocalName();
         }
     }
 
-    nameqn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&name));
+    nameqn = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1, Valueify(&name));
     if (!nameqn)
         return JS_FALSE;
 
     /* ECMA-357 13.4.4.35 Step 4. */
     if (xml->xml_class == JSXML_CLASS_PROCESSING_INSTRUCTION)
         nameqn->setNameURI(STRING_TO_JSVAL(cx->runtime->emptyString));
 
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
@@ -6494,26 +6454,26 @@ xml_setNamespace(JSContext *cx, uintN ar
     NON_LIST_XML_METHOD_PROLOG;
     if (!JSXML_HAS_NAME(xml))
         return JS_TRUE;
 
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
     if (!xml)
         return JS_FALSE;
 
-    ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj,
+    ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj,
                             argc == 0 ? 0 : 1, Valueify(vp + 2));
     if (!ns)
         return JS_FALSE;
     vp[0] = OBJECT_TO_JSVAL(ns);
     ns->setNamespaceDeclared(JSVAL_TRUE);
 
     qnargv[0] = vp[2] = OBJECT_TO_JSVAL(ns);
     qnargv[1] = OBJECT_TO_JSVAL(xml->name);
-    qn = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 2, Valueify(qnargv));
+    qn = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, Valueify(qnargv));
     if (!qn)
         return JS_FALSE;
 
     xml->name = qn;
 
     /*
      * Erratum: the spec fails to update the governing in-scope namespaces.
      * See the erratum noted in xml_setName, above.
@@ -7030,24 +6990,24 @@ js_GetXMLObject(JSContext *cx, JSXML *xm
         return NULL;
     xml->object = obj;
     return obj;
 }
 
 JSObject *
 js_InitNamespaceClass(JSContext *cx, JSObject *obj)
 {
-    return js_InitClass(cx, obj, NULL, &js_NamespaceClass, Namespace, 2,
+    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, QName, 2,
+    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);
@@ -7233,17 +7193,17 @@ js_GetDefaultXMLNamespace(JSContext *cx,
             return JS_FALSE;
         if (!JSVAL_IS_PRIMITIVE(v)) {
             *vp = v;
             return JS_TRUE;
         }
         obj = tmp;
     }
 
-    ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, obj, 0, NULL);
+    ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, obj, 0, NULL);
     if (!ns)
         return JS_FALSE;
     v = OBJECT_TO_JSVAL(ns);
     if (!obj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, Valueify(v),
                              PropertyStub, PropertyStub, JSPROP_PERMANENT)) {
         return JS_FALSE;
     }
     *vp = v;
@@ -7254,17 +7214,17 @@ JSBool
 js_SetDefaultXMLNamespace(JSContext *cx, const Value &v)
 {
     Value argv[2];
     JSObject *ns, *varobj;
     JSStackFrame *fp;
 
     argv[0].setString(cx->runtime->emptyString);
     argv[1] = v;
-    ns = js_ConstructObject(cx, &js_NamespaceClass, NULL, NULL, 2, argv);
+    ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL, 2, argv);
     if (!ns)
         return JS_FALSE;
 
     fp = js_GetTopStackFrame(cx);
     varobj = fp->varobj(cx);
     if (!varobj->defineProperty(cx, JS_DEFAULT_XML_NAMESPACE_ID, ObjectValue(*ns),
                                 PropertyStub, PropertyStub, JSPROP_PERMANENT)) {
         return JS_FALSE;
@@ -7420,23 +7380,23 @@ js_FindXMLProperty(JSContext *cx, const 
     JSBool found;
     JSProperty *prop;
     const char *printable;
 
     JS_ASSERT(nameval.isObject());
     nameobj = &nameval.toObject();
     if (nameobj->getClass() == &js_AnyNameClass) {
         v = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
-        nameobj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1,
+        nameobj = js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 1,
                                      Valueify(&v));
         if (!nameobj)
             return JS_FALSE;
     } else {
         JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
-                  nameobj->getClass() == &js_QNameClass);
+                  nameobj->getClass() == &js_QNameClass.base);
     }
 
     qn = nameobj;
     if (!IsFunctionQName(cx, qn, &funid))
         return JS_FALSE;
 
     obj = js_GetTopStackFrame(cx)->scopeChain;
     do {
@@ -7601,31 +7561,20 @@ xmlfilter_finalize(JSContext *cx, JSObje
         return;
 
     cx->destroy(filter);
 }
 
 Class js_XMLFilterClass = {
     "XMLFilter",
     JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
-    PropertyStub,   /* addProperty */
-    PropertyStub,   /* delProperty */
-    PropertyStub,   /* getProperty */
-    PropertyStub,   /* setProperty */
-    EnumerateStub,
-    ResolveStub,
-    ConvertStub,
-    xmlfilter_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    JS_CLASS_TRACE(xmlfilter_trace)
+    PropertyStub,      PropertyStub,    PropertyStub,      PropertyStub,
+    EnumerateStub,     ResolveStub,     ConvertStub,       xmlfilter_finalize,
+    NULL,              NULL,            NULL,              NULL,
+    NULL,              NULL,            JS_CLASS_TRACE(xmlfilter_trace), NULL
 };
 
 JSBool
 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
 {
     jsval *sp;
     JSObject *obj, *filterobj, *resobj, *kidobj;
     JSXML *xml, *list;
--- a/js/src/jsxml.h
+++ b/js/src/jsxml.h
@@ -211,54 +211,55 @@ extern void
 js_FinalizeXML(JSContext *cx, JSXML *xml);
 
 extern JSObject *
 js_NewXMLObject(JSContext *cx, JSXMLClass xml_class);
 
 extern JSObject *
 js_GetXMLObject(JSContext *cx, JSXML *xml);
 
-extern JS_FRIEND_DATA(js::Class) js_XMLClass;
-extern JS_FRIEND_DATA(js::Class) js_NamespaceClass;
-extern JS_FRIEND_DATA(js::Class) js_QNameClass;
-extern JS_FRIEND_DATA(js::Class) js_AttributeNameClass;
-extern JS_FRIEND_DATA(js::Class) js_AnyNameClass;
-extern js::Class                 js_XMLFilterClass;
+extern JS_FRIEND_DATA(JSObjectOps)       js_XMLObjectOps;
+extern JS_FRIEND_DATA(js::Class)         js_XMLClass;
+extern JS_FRIEND_DATA(js::ExtendedClass) js_NamespaceClass;
+extern JS_FRIEND_DATA(js::ExtendedClass) js_QNameClass;
+extern JS_FRIEND_DATA(js::Class)         js_AttributeNameClass;
+extern JS_FRIEND_DATA(js::Class)         js_AnyNameClass;
+extern js::Class                         js_XMLFilterClass;
 
 /*
  * Methods to test whether an object or a value is of type "xml" (per typeof).
  */
 inline bool
 JSObject::isXML() const
 {
-    return getClass() == &js_XMLClass;
+    return map->ops == &js_XMLObjectOps;
 }
 
 inline bool
 JSObject::isXMLId() const
 {
     js::Class *clasp = getClass();
-    return clasp == &js_QNameClass ||
+    return clasp == &js_QNameClass.base ||
            clasp == &js_AttributeNameClass ||
            clasp == &js_AnyNameClass;
 }
 
 #define VALUE_IS_XML(v)      (!JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v)->isXML())
 
 inline bool
 JSObject::isNamespace() const
 {
-    return getClass() == &js_NamespaceClass;
+    return getClass() == &js_NamespaceClass.base;
 }
 
 inline bool
 JSObject::isQName() const
 {
     js::Class* clasp = getClass();
-    return clasp == &js_QNameClass ||
+    return clasp == &js_QNameClass.base ||
            clasp == &js_AttributeNameClass ||
            clasp == &js_AnyNameClass;
 }
 
 static inline bool
 IsXML(const js::Value &v)
 {
     return v.isObject() && v.toObject().isXML();
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1388,17 +1388,17 @@ ValueToScript(JSContext *cx, jsval v)
     JSFunction *fun;
 
     if (!JSVAL_IS_PRIMITIVE(v)) {
         JSObject *obj = JSVAL_TO_OBJECT(v);
         JSClass *clasp = JS_GET_CLASS(cx, obj);
 
         if (clasp == Jsvalify(&js_ScriptClass)) {
             script = (JSScript *) JS_GetPrivate(cx, obj);
-        } else if (clasp == Jsvalify(&js_GeneratorClass)) {
+        } else if (clasp == Jsvalify(&js_GeneratorClass.base)) {
             JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
             fun = gen->getFloatingFrame()->fun;
             script = FUN_SCRIPT(fun);
         }
     }
 
     if (!script) {
         fun = JS_ValueToFunction(cx, v);
@@ -2738,16 +2738,28 @@ static JSObject *
 split_thisObject(JSContext *cx, JSObject *obj)
 {
     OBJ_TO_OUTER_OBJECT(cx, obj);
     if (!obj)
         return NULL;
     return obj;
 }
 
+static JSObjectOps split_objectops;
+
+static JSObjectOps *
+split_getObjectOps(JSContext *cx, JSClass *clasp)
+{
+    if (!split_objectops.thisObject) {
+        memcpy(&split_objectops, &js_ObjectOps, sizeof split_objectops);
+        split_objectops.thisObject = split_thisObject;
+    }
+
+    return &split_objectops;
+}
 
 static JSBool
 split_equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
 
 static JSObject *
 split_innerObject(JSContext *cx, JSObject *obj)
 {
     ComplexObject *cpx;
@@ -2755,66 +2767,40 @@ split_innerObject(JSContext *cx, JSObjec
     cpx = (ComplexObject *) JS_GetPrivate(cx, obj);
     if (cpx->frozen) {
         JS_ASSERT(!cpx->isInner);
         return obj;
     }
     return !cpx->isInner ? cpx->inner : obj;
 }
 
-static Class split_global_class = {
-    "split_global",
-    JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE | JSCLASS_GLOBAL_FLAGS,
-    Valueify(split_addProperty),
-    Valueify(split_delProperty),
-    Valueify(split_getProperty),
-    Valueify(split_setProperty),
+static JSExtendedClass split_global_class = {
+    {"split_global",
+    JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE |
+    JSCLASS_GLOBAL_FLAGS | JSCLASS_IS_EXTENDED,
+    split_addProperty, split_delProperty,
+    split_getProperty, split_setProperty,
     (JSEnumerateOp)split_enumerate,
     (JSResolveOp)split_resolve,
-    ConvertStub,
-    split_finalize,
-    NULL,           /* reserved0   */
-    NULL,           /* checkAccess */
-    NULL,           /* call        */
-    NULL,           /* construct   */
-    NULL,           /* xdrObject   */
-    NULL,           /* hasInstance */
-    split_mark,
-    {
-        Valueify(split_equality),
-        split_outerObject,
-        split_innerObject,
-        NULL, /* iteratorObject */
-        NULL, /* wrappedObject  */
-    },
-    {
-        NULL, /* lookupProperty */
-        NULL, /* defineProperty */
-        NULL, /* getProperty    */
-        NULL, /* setProperty    */
-        NULL, /* getAttributes  */
-        NULL, /* setAttributes  */
-        NULL, /* deleteProperty */
-        NULL, /* enumerate      */
-        NULL, /* typeOf         */
-        NULL, /* trace          */
-        split_thisObject,
-        NULL, /* clear          */
-    },
+    JS_ConvertStub, split_finalize,
+    split_getObjectOps, NULL, NULL, NULL, NULL, NULL,
+    split_mark, NULL},
+    split_equality, split_outerObject, split_innerObject,
+    NULL, NULL, NULL, NULL, NULL
 };
 
 static JSBool
 split_equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp)
 {
     *bp = JS_FALSE;
     if (JSVAL_IS_PRIMITIVE(*v))
         return JS_TRUE;
 
     JSObject *obj2 = JSVAL_TO_OBJECT(*v);
-    if (obj2->getClass() != &split_global_class)
+    if (JS_GET_CLASS(cx, obj2) != &split_global_class.base)
         return JS_TRUE;
 
     ComplexObject *cpx = (ComplexObject *) JS_GetPrivate(cx, obj2);
     JS_ASSERT(!cpx->isInner);
 
     ComplexObject *ourCpx = (ComplexObject *) JS_GetPrivate(cx, obj);
     JS_ASSERT(!ourCpx->isInner);
 
@@ -2831,17 +2817,17 @@ split_create_outer(JSContext *cx)
     cpx = (ComplexObject *) JS_malloc(cx, sizeof *obj);
     if (!cpx)
         return NULL;
     cpx->isInner = JS_FALSE;
     cpx->frozen = JS_TRUE;
     cpx->inner = NULL;
     cpx->outer = NULL;
 
-    obj = JS_NewGlobalObject(cx, Jsvalify(&split_global_class));
+    obj = JS_NewGlobalObject(cx, &split_global_class.base);
     if (!obj || !JS_SetParent(cx, obj, NULL)) {
         JS_free(cx, cpx);
         return NULL;
     }
 
     if (!JS_SetPrivate(cx, obj, cpx)) {
         JS_free(cx, cpx);
         return NULL;
@@ -2851,44 +2837,44 @@ split_create_outer(JSContext *cx)
 }
 
 static JSObject *
 split_create_inner(JSContext *cx, JSObject *outer)
 {
     ComplexObject *cpx, *outercpx;
     JSObject *obj;
 
-    JS_ASSERT(outer->getClass() == &split_global_class);
+    JS_ASSERT(JS_GET_CLASS(cx, outer) == &split_global_class.base);
 
     cpx = (ComplexObject *) JS_malloc(cx, sizeof *cpx);
     if (!cpx)
         return NULL;
     cpx->isInner = JS_TRUE;
     cpx->frozen = JS_FALSE;
     cpx->inner = NULL;
     cpx->outer = outer;
 
-    obj = JS_NewGlobalObject(cx, Jsvalify(&split_global_class));
+    obj = JS_NewGlobalObject(cx, &split_global_class.base);
     if (!obj || !JS_SetParent(cx, obj, NULL) || !JS_SetPrivate(cx, obj, cpx)) {
         JS_free(cx, cpx);
         return NULL;
     }
 
     outercpx = (ComplexObject *) JS_GetPrivate(cx, outer);
     outercpx->inner = obj;
     outercpx->frozen = JS_FALSE;
 
     return obj;
 }
 
 static ComplexObject *
 split_get_private(JSContext *cx, JSObject *obj)
 {
     do {
-        if (obj->getClass() == &split_global_class)
+        if (JS_GET_CLASS(cx, obj) == &split_global_class.base)
             return (ComplexObject *) JS_GetPrivate(cx, obj);
         obj = JS_GetParent(cx, obj);
     } while (obj);
 
     return NULL;
 }
 
 static JSBool
@@ -3663,18 +3649,22 @@ Parent(JSContext *cx, uintN argc, jsval 
         return JS_FALSE;
     }
 
     JSObject *parent = JS_GetParent(cx, JSVAL_TO_OBJECT(v));
     *vp = OBJECT_TO_JSVAL(parent);
 
     /* Outerize if necessary.  Embrace the ugliness! */
     if (parent) {
-        if (JSObjectOp op = parent->getClass()->ext.outerObject)
-            *vp = OBJECT_TO_JSVAL(op(cx, parent));
+        JSClass *clasp = JS_GET_CLASS(cx, parent);
+        if (clasp->flags & JSCLASS_IS_EXTENDED) {
+            JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp);
+            if (JSObjectOp outerize = xclasp->outerObject)
+                *vp = OBJECT_TO_JSVAL(outerize(cx, parent));
+        }
     }
 
     return JS_TRUE;
 }
 
 #ifdef XP_UNIX
 
 #include <fcntl.h>
--- a/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -54,17 +54,16 @@
 #include "nsDependentString.h"
 #include "nsAutoPtr.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsIFileURL.h"
 
 #include "jsapi.h"
 #include "jsdbgapi.h"
-#include "jsobj.h"
 
 #include "mozilla/FunctionTimer.h"
 
 /* load() error msgs, XXX localize? */
 #define LOAD_ERROR_NOSERVICE "Error creating IO Service."
 #define LOAD_ERROR_NOURI "Error creating URI (invalid URL scheme?)"
 #define LOAD_ERROR_NOSCHEME "Failed to get URI scheme.  This is bad."
 #define LOAD_ERROR_URI_NOT_LOCAL "Trying to load a non-local URI."
@@ -200,23 +199,28 @@ mozJSSubScriptLoader::LoadSubScript (con
         }
 #ifdef DEBUG_rginda
         fprintf (stderr, "\n");
 #endif  
     }
 
     // Innerize the target_obj so that we compile the loaded script in the
     // correct (inner) scope.
-    if (JSObjectOp op = target_obj->getClass()->ext.innerObject)
+    JSClass *target_class = JS_GET_CLASS(cx, target_obj);
+    if (target_class->flags & JSCLASS_IS_EXTENDED)
     {
-        target_obj = op(cx, target_obj);
-        if (!target_obj) return NS_ERROR_FAILURE;
+        JSExtendedClass *extended = (JSExtendedClass*)target_class;
+        if (extended->innerObject)
+        {
+            target_obj = extended->innerObject(cx, target_obj);
+            if (!target_obj) return NS_ERROR_FAILURE;
 #ifdef DEBUG_rginda
-        fprintf (stderr, "Final global: %p\n", target_obj);
+            fprintf (stderr, "Final global: %p\n", target_obj);
 #endif
+        }
     }
 
     /* load up the url.  From here on, failures are reflected as ``custom''
      * js exceptions */
     PRInt32   len = -1;
     PRUint32  readcount = 0;  // Total amount of data read
     PRUint32  lastReadCount = 0;  // Amount of data read in last Read() call
     nsAutoArrayPtr<char> buf;
--- a/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp
+++ b/js/src/xpconnect/src/XPCChromeObjectWrapper.cpp
@@ -246,55 +246,49 @@ XPC_COW_WrappedObject(JSContext *cx, JSO
 
 static JSBool
 WrapFunction(JSContext *cx, JSObject *scope, JSObject *funobj, jsval *vp);
 
 using namespace XPCWrapper;
 
 namespace ChromeObjectWrapper {
 
-js::Class COWClass = {
-    "ChromeObjectWrapper",
-    JSCLASS_NEW_RESOLVE |
+JSExtendedClass COWClass = {
+  // JSClass (JSExtendedClass.base) initialization
+  { "ChromeObjectWrapper",
+    JSCLASS_NEW_RESOLVE | JSCLASS_IS_EXTENDED |
     JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots + 1),
-    js::Valueify(XPC_COW_AddProperty),
-    js::Valueify(XPC_COW_DelProperty),
-    js::Valueify(XPC_COW_GetProperty),
-    js::Valueify(XPC_COW_SetProperty),
-    XPC_COW_Enumerate,
-    (JSResolveOp)XPC_COW_NewResolve,
-    js::Valueify(XPC_COW_Convert),
-    JS_FinalizeStub,
-    nsnull,   // reserved0
-    js::Valueify(XPC_COW_CheckAccess),
-    nsnull,   // call
-    nsnull,   // construct
-    nsnull,   // xdrObject
-    nsnull,   // hasInstance
-    nsnull,   // mark
+    XPC_COW_AddProperty, XPC_COW_DelProperty,
+    XPC_COW_GetProperty, XPC_COW_SetProperty,
+    XPC_COW_Enumerate,   (JSResolveOp)XPC_COW_NewResolve,
+    XPC_COW_Convert,     JS_FinalizeStub,
+    nsnull,              XPC_COW_CheckAccess,
+    nsnull,              nsnull,
+    nsnull,              nsnull,
+    nsnull,              nsnull
+  },
 
-    // ClassExtension
-    {
-      js::Valueify(XPC_COW_Equality),
-      nsnull, // outerObject
-      nsnull, // innerObject
-      XPC_COW_Iterator,
-      XPC_COW_WrappedObject
-    }
+  // JSExtendedClass initialization
+  XPC_COW_Equality,
+  nsnull,             // outerObject
+  nsnull,             // innerObject
+  XPC_COW_Iterator,
+  XPC_COW_WrappedObject,
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
 JSBool
 WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp)
 {
   if (JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v))) {
     return WrapFunction(cx, parent, JSVAL_TO_OBJECT(v), vp);
   }
 
   JSObject *wrapperObj =
-    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&COWClass), NULL, parent);
+    JS_NewObjectWithGivenProto(cx, &COWClass.base, NULL, parent);
   if (!wrapperObj) {
     return JS_FALSE;
   }
 
   *vp = OBJECT_TO_JSVAL(wrapperObj);
 
   js::AutoValueRooter exposedProps(cx);
 
@@ -324,36 +318,41 @@ ThrowException(nsresult rv, JSContext *c
 }
 
 // Like GetWrappedObject, but works on other types of wrappers, too.
 // See also JSObject::wrappedObject in jsobj.cpp.
 // TODO Move to XPCWrapper?
 static inline JSObject *
 GetWrappedJSObject(JSContext *cx, JSObject *obj)
 {
-  JSObjectOp op = obj->getClass()->ext.wrappedObject;
-  if (!op) {
+  JSClass *clasp = obj->getJSClass();
+  if (!(clasp->flags & JSCLASS_IS_EXTENDED)) {
+    return obj;
+  }
+
+  JSExtendedClass *xclasp = (JSExtendedClass *)clasp;
+  if (!xclasp->wrappedObject) {
     if (XPCNativeWrapper::IsNativeWrapper(obj)) {
       XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj);
       return wn ? wn->GetFlatJSObject() : nsnull;
     }
 
     return obj;
   }
 
-  return op(cx, obj);
+  return xclasp->wrappedObject(cx, obj);
 }
 
 // Get the (possibly nonexistent) COW off of an object
 // TODO Move to XPCWrapper and share with other wrappers.
 static inline
 JSObject *
 GetWrapper(JSObject *obj)
 {
-  while (obj->getClass() != &COWClass) {
+  while (obj->getJSClass() != &COWClass.base) {
     obj = obj->getProto();
     if (!obj) {
       break;
     }
   }
 
   return obj;
 }
@@ -744,17 +743,18 @@ XPC_COW_Equality(JSContext *cx, JSObject
     *bp = JS_FALSE;
     return JS_TRUE;
   }
 
   XPCWrappedNative *me = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
   obj = me->GetFlatJSObject();
   test = other->GetFlatJSObject();
   jsval testVal = OBJECT_TO_JSVAL(test);
-  return js::Jsvalify(obj->getClass()->ext.equality)(cx, obj, &testVal, bp);
+  return ((JSExtendedClass *)obj->getJSClass())->
+    equality(cx, obj, &testVal, bp);
 }
 
 static JSObject *
 XPC_COW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
 {
   JSObject *wrappedObj = GetWrappedObject(cx, obj);
   if (!wrappedObj) {
     ThrowException(NS_ERROR_INVALID_ARG, cx);
--- a/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
+++ b/js/src/xpconnect/src/XPCCrossOriginWrapper.cpp
@@ -120,17 +120,17 @@ ThrowException(nsresult ex, JSContext *c
   return JS_FALSE;
 }
 
 // Get the (possibly nonexistent) XOW off of an object
 static inline
 JSObject *
 GetWrapper(JSObject *obj)
 {
-  while (obj->getClass() != &XPCCrossOriginWrapper::XOWClass) {
+  while (obj->getJSClass() != &XPCCrossOriginWrapper::XOWClass.base) {
     obj = obj->getProto();
     if (!obj) {
       break;
     }
   }
 
   return obj;
 }
@@ -151,44 +151,38 @@ XPC_XOW_FunctionWrapper(JSContext *cx, J
 static const PRUint32 FLAG_IS_UXPC_OBJECT = XPCWrapper::LAST_FLAG << 1;
 
 // This flag is set on objects that have to clear their wrapped native's XOW
 // cache when they get finalized.
 static const PRUint32 FLAG_IS_CACHED = XPCWrapper::LAST_FLAG << 2;
 
 namespace XPCCrossOriginWrapper {
 
-js::Class XOWClass = {
-    "XPCCrossOriginWrapper",
-    JSCLASS_NEW_RESOLVE |
+JSExtendedClass XOWClass = {
+  // JSClass (JSExtendedClass.base) initialization
+  { "XPCCrossOriginWrapper",
+    JSCLASS_NEW_RESOLVE | JSCLASS_IS_EXTENDED |
     JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots + 2),
-    js::Valueify(XPC_XOW_AddProperty),
-    js::Valueify(XPC_XOW_DelProperty),
-    js::Valueify(XPC_XOW_GetProperty),
-    js::Valueify(XPC_XOW_SetProperty),
-    XPC_XOW_Enumerate,
-    (JSResolveOp)XPC_XOW_NewResolve,
-    js::Valueify(XPC_XOW_Convert),
-    XPC_XOW_Finalize,
-    nsnull,   // reserved0 
-    js::Valueify(XPC_XOW_CheckAccess),
-    js::Valueify(XPC_XOW_Call),
-    js::Valueify(XPC_XOW_Construct),
-    nsnull,   // xdrObject
-    js::Valueify(XPC_XOW_HasInstance),
-    nsnull,   // mark
+    XPC_XOW_AddProperty, XPC_XOW_DelProperty,
+    XPC_XOW_GetProperty, XPC_XOW_SetProperty,
+    XPC_XOW_Enumerate,   (JSResolveOp)XPC_XOW_NewResolve,
+    XPC_XOW_Convert,     XPC_XOW_Finalize,
+    nsnull,              XPC_XOW_CheckAccess,
+    XPC_XOW_Call,        XPC_XOW_Construct,
+    nsnull,              XPC_XOW_HasInstance,
+    nsnull,              nsnull
+  },
 
-    // ClassExtension
-    {
-      js::Valueify(XPC_XOW_Equality),
-      nsnull, // outerObject
-      nsnull, // innerObject
-      XPC_XOW_Iterator,
-      XPC_XOW_WrappedObject
-    }
+  // JSExtendedClass initialization
+  XPC_XOW_Equality,
+  nsnull,             // outerObject
+  nsnull,             // innerObject
+  XPC_XOW_Iterator,
+  XPC_XOW_WrappedObject,
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
 JSBool
 WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj,
                      XPCWrappedNativeScope *newScope)
 {
   typedef WrappedNative2WrapperMap::Link Link;
   XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
@@ -377,17 +371,17 @@ RewrapIfNeeded(JSContext *cx, JSObject *
 
   JSObject *obj = JSVAL_TO_OBJECT(*vp);
 
   if (JS_ObjectIsFunction(cx, obj)) {
     return WrapFunction(cx, outerObj, obj, vp);
   }
 
   XPCWrappedNative *wn = nsnull;
-  if (obj->getClass() == &XOWClass &&
+  if (obj->getJSClass() == &XOWClass.base &&
       outerObj->getParent() != obj->getParent()) {
     *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, obj));
   } else if (!(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, obj))) {
     return JS_TRUE;
   }
 
   return WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp, wn);
 }
@@ -398,17 +392,17 @@ WrapObject(JSContext *cx, JSObject *pare
   NS_ASSERTION(XPCPerThreadData::IsMainThread(cx),
                "Can't do this off the main thread!");
 
   // Our argument should be a wrapped native object, but the caller may have
   // passed it in as an optimization.
   JSObject *wrappedObj;
   if (JSVAL_IS_PRIMITIVE(*vp) ||
       !(wrappedObj = JSVAL_TO_OBJECT(*vp)) ||
-      wrappedObj->getClass() == &XOWClass) {
+      wrappedObj->getJSClass() == &XOWClass.base) {
     return JS_TRUE;
   }
 
   if (!wn &&
       !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) {
     return JS_TRUE;
   }
 
@@ -446,30 +440,30 @@ WrapObject(JSContext *cx, JSObject *pare
     parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent);
   }
 
   JSObject *outerObj = nsnull;
   WrappedNative2WrapperMap *map = parentScope->GetWrapperMap();
 
   outerObj = map->Find(wrappedObj);
   if (outerObj) {
-    NS_ASSERTION(outerObj->getClass() == &XOWClass,
+    NS_ASSERTION(outerObj->getJSClass() == &XOWClass.base,
                  "What crazy object are we getting here?");
     *vp = OBJECT_TO_JSVAL(outerObj);
 
     if (wnxow) {
       // NB: wnxow->GetXOW() must have returned false.
       SetFlags(cx, outerObj, AddFlags(GetFlags(cx, outerObj), FLAG_IS_CACHED));
       wnxow->SetXOW(outerObj);
     }
 
     return JS_TRUE;
   }
 
-  outerObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&XOWClass), nsnull,
+  outerObj = JS_NewObjectWithGivenProto(cx, &XOWClass.base, nsnull,
                                         parent);
   if (!outerObj) {
     return JS_FALSE;
   }
 
   jsval flags = INT_TO_JSVAL(wnxow ? FLAG_IS_CACHED : 0);
   if (!JS_SetReservedSlot(cx, outerObj, sWrappedObjSlot, *vp) ||
       !JS_SetReservedSlot(cx, outerObj, sFlagsSlot, flags) ||
@@ -591,24 +585,24 @@ WrapSameOriginProp(JSContext *cx, JSObje
 {
   // Don't call RewrapIfNeeded for same origin properties. We only
   // need to wrap window, document and location.
   if (JSVAL_IS_PRIMITIVE(*vp)) {
     return JS_TRUE;
   }
 
   JSObject *wrappedObj = JSVAL_TO_OBJECT(*vp);
-  js::Class *clasp = wrappedObj->getClass();
+  JSClass *clasp = wrappedObj->getJSClass();
   if (ClassNeedsXOW(clasp->name)) {
     return WrapObject(cx, JS_GetGlobalForObject(cx, outerObj), vp);
   }
 
   // Check if wrappedObj is an XOW. If so, verify that it's from the
   // right scope.
-  if (clasp == &XOWClass &&
+  if (clasp == &XOWClass.base &&
       wrappedObj->getParent() != outerObj->getParent()) {
     *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, wrappedObj));
     return WrapObject(cx, outerObj->getParent(), vp);
   }
 
   return JS_TRUE;
 }
 
@@ -621,17 +615,17 @@ XPC_XOW_AddProperty(JSContext *cx, JSObj
   obj = GetWrapper(obj);
   jsval resolving;
   if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &resolving)) {
     return JS_FALSE;
   }
 
   if (!JSVAL_IS_PRIMITIVE(*vp)) {
     JSObject *addedObj = JSVAL_TO_OBJECT(*vp);
-    if (addedObj->getClass() == &XOWClass &&
+    if (addedObj->getJSClass() == &XOWClass.base &&
         addedObj->getParent() != obj->getParent()) {
       *vp = OBJECT_TO_JSVAL(GetWrappedObject(cx, addedObj));
       if (!WrapObject(cx, obj->getParent(), vp, nsnull)) {
         return JS_FALSE;
       }
     }
   }
 
@@ -843,17 +837,17 @@ XPC_XOW_Enumerate(JSContext *cx, JSObjec
 // UXPCObject (which is just a XOW for the same object). This causes the JS
 // engine to do all of its work on another object, not polluting the main
 // object. However, if the get results in calling a setter, the engine still
 // uses the regular object as 'this', ensuring that the UXPCObject doesn't
 // leak to script.
 static JSObject *
 GetUXPCObject(JSContext *cx, JSObject *obj)
 {
-  NS_ASSERTION(obj->getClass() == &XOWClass, "wrong object");
+  NS_ASSERTION(obj->getJSClass() == &XOWClass.base, "wrong object");
 
   jsval v;
   if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &v)) {
     return nsnull;
   }
 
   if (HAS_FLAGS(v, FLAG_IS_UXPC_OBJECT)) {
     return obj;
@@ -863,18 +857,17 @@ GetUXPCObject(JSContext *cx, JSObject *o
     return nsnull;
   }
 
   if (JSVAL_IS_OBJECT(v)) {
     return JSVAL_TO_OBJECT(v);
   }
 
   JSObject *uxpco =
-    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&XOWClass), nsnull,
-                               obj->getParent());
+    JS_NewObjectWithGivenProto(cx, &XOWClass.base, nsnull, obj->getParent());
   if (!uxpco) {
     return nsnull;
   }
 
   js::AutoObjectRooter tvr(cx, uxpco);
 
   jsval wrappedObj, parentScope;
   if (!JS_GetReservedSlot(cx, obj, sWrappedObjSlot, &wrappedObj) ||
@@ -1183,17 +1176,17 @@ XPC_XOW_Equality(JSContext *cx, JSObject
 
   // Convert both sides to XPCWrappedNative and see if they match.
   if (JSVAL_IS_PRIMITIVE(v)) {
     *bp = JS_FALSE;
     return JS_TRUE;
   }
 
   JSObject *test = JSVAL_TO_OBJECT(v);
-  if (test->getClass() == &XOWClass) {
+  if (test->getJSClass() == &XOWClass.base) {
     if (!JS_GetReservedSlot(cx, test, sWrappedObjSlot, &v)) {
       return JS_FALSE;
     }
 
     if (JSVAL_IS_PRIMITIVE(v)) {
       *bp = JS_FALSE;
       return JS_TRUE;
     }
@@ -1211,17 +1204,18 @@ XPC_XOW_Equality(JSContext *cx, JSObject
     *bp = JS_FALSE;
     return JS_TRUE;
   }
 
   XPCWrappedNative *me = XPCWrappedNative::GetWrappedNativeOfJSObject(cx, obj);
   obj = me->GetFlatJSObject();
   test = other->GetFlatJSObject();
   jsval testVal = OBJECT_TO_JSVAL(test);
-  return js::Jsvalify(obj->getClass()->ext.equality)(cx, obj, &testVal, bp);
+  return ((JSExtendedClass *)obj->getJSClass())->
+    equality(cx, obj, &testVal, bp);
 }
 
 static JSObject *
 XPC_XOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
 {
   JSObject *wrappedObj = GetWrappedObject(cx, obj);
   if (!wrappedObj) {
     ThrowException(NS_ERROR_INVALID_ARG, cx);
@@ -1241,17 +1235,17 @@ XPC_XOW_Iterator(JSContext *cx, JSObject
       ThrowException(rv, cx);
       return nsnull;
     }
 
     ThrowException(NS_ERROR_FAILURE, cx);
     return nsnull;
   }
 
-  JSObject *wrapperIter = JS_NewObject(cx, js::Jsvalify(&XOWClass), nsnull,
+  JSObject *wrapperIter = JS_NewObject(cx, &XOWClass.base, nsnull,
                                        JS_GetGlobalForObject(cx, obj));
   if (!wrapperIter) {
     return nsnull;
   }
 
   js::AutoObjectRooter tvr(cx, wrapperIter);
 
   // Initialize our XOW.
--- a/js/src/xpconnect/src/XPCNativeWrapper.cpp
+++ b/js/src/xpconnect/src/XPCNativeWrapper.cpp
@@ -104,78 +104,66 @@ using namespace XPCWrapper;
 // not have expando properties set on implicit native wrappers.
 static const PRUint32 FLAG_EXPLICIT = XPCWrapper::LAST_FLAG << 1;
 
 namespace XPCNativeWrapper { namespace internal {
 
 // JS class for XPCNativeWrapper (and this doubles as the constructor
 // for XPCNativeWrapper for the moment too...)
 
-js::Class NW_NoCall_Class = {
-    "XPCNativeWrapper",
+JSExtendedClass NW_NoCall_Class = {
+  // JSClass (JSExtendedClass.base) initialization
+  { "XPCNativeWrapper",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds a jsint of flag bits
     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
-    JSCLASS_MARK_IS_TRACE | JSCLASS_CONSTRUCT_PROTOTYPE,
-    js::Valueify(XPC_NW_AddProperty),
-    js::Valueify(XPC_NW_DelProperty),
-    js::Valueify(XPC_NW_GetProperty),
-    js::Valueify(XPC_NW_SetProperty),
-    XPC_NW_Enumerate,
-    (JSResolveOp)XPC_NW_NewResolve,
-    js::Valueify(XPC_NW_Convert),
-    XPC_NW_Finalize,
-    nsnull,   // reserved0
-    js::Valueify(XPC_NW_CheckAccess),
-    nsnull,   // call
-    js::Valueify(XPC_NW_Construct),
-    nsnull,   // xdrObject
-    js::Valueify(XPC_NW_HasInstance),
-    JS_CLASS_TRACE(XPC_NW_Trace),
+    JSCLASS_MARK_IS_TRACE | JSCLASS_IS_EXTENDED | JSCLASS_CONSTRUCT_PROTOTYPE,
+    XPC_NW_AddProperty, XPC_NW_DelProperty,
+    XPC_NW_GetProperty, XPC_NW_SetProperty,
+    XPC_NW_Enumerate,   (JSResolveOp)XPC_NW_NewResolve,
+    XPC_NW_Convert,     XPC_NW_Finalize,
+    nsnull,             XPC_NW_CheckAccess,
+    nsnull,             XPC_NW_Construct,
+    nsnull,             XPC_NW_HasInstance,
+    JS_CLASS_TRACE(XPC_NW_Trace), nsnull
+  },
 
-    // ClassExtension
-    {
-      js::Valueify(XPC_NW_Equality),
-      nsnull, // outerObject
-      nsnull, // innerObject
-      XPC_NW_Iterator,
-      nsnull, // wrappedObject
-    }
+  // JSExtendedClass initialization
+  XPC_NW_Equality,
+  nsnull,             // outerObject
+  nsnull,             // innerObject
+  XPC_NW_Iterator,
+  nsnull,             // wrappedObject
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
-js::Class NW_Call_Class = {
-    "XPCNativeWrapper",
+JSExtendedClass NW_Call_Class = {
+  // JSClass (JSExtendedClass.base) initialization
+  { "XPCNativeWrapper",
     JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
     // Our one reserved slot holds a jsint of flag bits
     JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
-    JSCLASS_MARK_IS_TRACE | JSCLASS_CONSTRUCT_PROTOTYPE,
-    js::Valueify(XPC_NW_AddProperty),
-    js::Valueify(XPC_NW_DelProperty),
-    js::Valueify(XPC_NW_GetProperty),
-    js::Valueify(XPC_NW_SetProperty),
-    XPC_NW_Enumerate,
-    (JSResolveOp)XPC_NW_NewResolve,
-    js::Valueify(XPC_NW_Convert),
-    XPC_NW_Finalize,
-    nsnull,   // reserved0
-    js::Valueify(XPC_NW_CheckAccess),
-    js::Valueify(XPC_NW_Call),
-    js::Valueify(XPC_NW_Construct),
-    nsnull,   // xdrObject
-    js::Valueify(XPC_NW_HasInstance),
-    JS_CLASS_TRACE(XPC_NW_Trace),
+    JSCLASS_MARK_IS_TRACE | JSCLASS_IS_EXTENDED | JSCLASS_CONSTRUCT_PROTOTYPE,
+    XPC_NW_AddProperty, XPC_NW_DelProperty,
+    XPC_NW_GetProperty, XPC_NW_SetProperty,
+    XPC_NW_Enumerate,   (JSResolveOp)XPC_NW_NewResolve,
+    XPC_NW_Convert,     XPC_NW_Finalize,
+    nsnull,             XPC_NW_CheckAccess,
+    XPC_NW_Call,        XPC_NW_Construct,
+    nsnull,             XPC_NW_HasInstance,
+    JS_CLASS_TRACE(XPC_NW_Trace), nsnull
+  },
 
-    // ClassExtension
-    {
-      js::Valueify(XPC_NW_Equality),
-      nsnull, // outerObject
-      nsnull, // innerObject
-      XPC_NW_Iterator,
-      nsnull, // wrappedObject
-    }
+  // JSExtendedClass initialization
+  XPC_NW_Equality,
+  nsnull,             // outerObject
+  nsnull,             // innerObject
+  XPC_NW_Iterator,
+  nsnull,             // wrappedObject
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
 } // namespace internal
 
 JSBool
 GetWrappedNative(JSContext *cx, JSObject *obj,
                  XPCWrappedNative **aWrappedNative)
 {
@@ -1035,18 +1023,17 @@ static JSFunctionSpec static_functions[]
 };
 
 // static
 PRBool
 XPCNativeWrapper::AttachNewConstructorObject(XPCCallContext &ccx,
                                              JSObject *aGlobalObject)
 {
   JSObject *class_obj =
-    ::JS_InitClass(ccx, aGlobalObject, nsnull,
-                   js::Jsvalify(&internal::NW_Call_Class),
+    ::JS_InitClass(ccx, aGlobalObject, nsnull, &internal::NW_Call_Class.base,
                    XPCNativeWrapperCtor, 0, nsnull, nsnull,
                    nsnull, static_functions);
   if (!class_obj) {
     NS_WARNING("can't initialize the XPCNativeWrapper class");
     return PR_FALSE;
   }
   
   // Make sure our prototype chain is empty and that people can't mess
@@ -1054,17 +1041,17 @@ XPCNativeWrapper::AttachNewConstructorOb
   ::JS_SetPrototype(ccx, class_obj, nsnull);
   if (!::JS_SealObject(ccx, class_obj, JS_FALSE)) {
     NS_WARNING("Failed to seal XPCNativeWrapper.prototype");
     return PR_FALSE;
   }
 
   JSBool found;
   return ::JS_SetPropertyAttributes(ccx, aGlobalObject,
-                                    internal::NW_Call_Class.name,
+                                    internal::NW_Call_Class.base.name,
                                     JSPROP_READONLY | JSPROP_PERMANENT,
                                     &found);
 }
 
 // static
 JSObject *
 XPCNativeWrapper::GetNewOrUsed(JSContext *cx, XPCWrappedNative *wrapper,
                                JSObject *scope, nsIPrincipal *aObjectPrincipal)
--- a/js/src/xpconnect/src/XPCNativeWrapper.h
+++ b/js/src/xpconnect/src/XPCNativeWrapper.h
@@ -40,40 +40,40 @@
 #include "nscore.h"
 #include "jsapi.h"
 
 class nsIPrincipal;
 
 namespace XPCNativeWrapper {
 
 namespace internal {
-extern js::Class NW_NoCall_Class;
-extern js::Class NW_Call_Class;
+  extern JSExtendedClass NW_NoCall_Class;
+  extern JSExtendedClass NW_Call_Class;
 }
 
 PRBool
 AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject);
 
 JSObject *
 GetNewOrUsed(JSContext *cx, XPCWrappedNative *wrapper,
              JSObject *scope, nsIPrincipal *aObjectPrincipal);
 JSBool
 CreateExplicitWrapper(JSContext *cx, XPCWrappedNative *wrapper, jsval *rval);
 
 inline PRBool
-IsNativeWrapperClass(js::Class *clazz)
+IsNativeWrapperClass(JSClass *clazz)
 {
-  return clazz == &internal::NW_NoCall_Class ||
-         clazz == &internal::NW_Call_Class;
+  return clazz == &internal::NW_NoCall_Class.base ||
+         clazz == &internal::NW_Call_Class.base;
 }
 
 inline PRBool
 IsNativeWrapper(JSObject *obj)
 {
-  return IsNativeWrapperClass(obj->getClass());
+  return IsNativeWrapperClass(obj->getJSClass());
 }
 
 JSBool
 GetWrappedNative(JSContext *cx, JSObject *obj,
                  XPCWrappedNative **aWrappedNative);
 
 // NB: Use the following carefully.
 inline XPCWrappedNative *
@@ -81,16 +81,16 @@ SafeGetWrappedNative(JSObject *obj)
 {
   return static_cast<XPCWrappedNative *>(xpc_GetJSPrivate(obj));
 }
 
 inline JSClass *
 GetJSClass(bool call)
 {
   return call
-    ? js::Jsvalify(&internal::NW_Call_Class)
-    : js::Jsvalify(&internal::NW_NoCall_Class);
+    ? &internal::NW_Call_Class.base
+    : &internal::NW_NoCall_Class.base;
 }
 
 void
 ClearWrappedNativeScopes(JSContext* cx, XPCWrappedNative* wrapper);
 
 }
--- a/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
+++ b/js/src/xpconnect/src/XPCSafeJSObjectWrapper.cpp
@@ -228,17 +228,17 @@ FindObjectPrincipals(JSContext *cx, JSOb
 
   // The wrapper owns the principal now.
   return objPrincipal.forget().get();
 }
 
 static inline JSObject *
 FindSafeObject(JSObject *obj)
 {
-  while (obj->getClass() != &SJOWClass) {
+  while (obj->getJSClass() != &SJOWClass.base) {
     obj = obj->getProto();
 
     if (!obj) {
       break;
     }
   }
 
   return obj;
@@ -248,44 +248,37 @@ static JSBool
 XPC_SJOW_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                   jsval *rval);
 
 namespace XPCSafeJSObjectWrapper {
 
 // JS class for XPCSafeJSObjectWrapper (and this doubles as the
 // constructor for XPCSafeJSObjectWrapper for the moment too...)
 
-js::Class SJOWClass = {
-    "XPCSafeJSObjectWrapper",
-    JSCLASS_NEW_RESOLVE |
+JSExtendedClass SJOWClass = {
+  // JSClass (JSExtendedClass.base) initialization
+  { "XPCSafeJSObjectWrapper",
+    JSCLASS_NEW_RESOLVE | JSCLASS_IS_EXTENDED |
     JSCLASS_HAS_RESERVED_SLOTS(sSJOWSlots),
-    js::Valueify(XPC_SJOW_AddProperty),
-    js::Valueify(XPC_SJOW_DelProperty),
-    js::Valueify(XPC_SJOW_GetProperty),
-    js::Valueify(XPC_SJOW_SetProperty),
-    XPC_SJOW_Enumerate,
-    (JSResolveOp)XPC_SJOW_NewResolve,
-    js::Valueify(XPC_SJOW_Convert),
-    XPC_SJOW_Finalize,
-    nsnull,   // reserved0
-    js::Valueify(XPC_SJOW_CheckAccess),
-    js::Valueify(XPC_SJOW_Call),
-    js::Valueify(XPC_SJOW_Create),
-    nsnull,   // xdrObject
-    nsnull,   // hasInstance
-    nsnull,   // mark
-
-    // ClassExtension
-    {
-      js::Valueify(XPC_SJOW_Equality),
-      nsnull, // outerObject
-      nsnull, // innerObject
-      XPC_SJOW_Iterator,
-      XPC_SJOW_WrappedObject
-    }
+    XPC_SJOW_AddProperty, XPC_SJOW_DelProperty,
+    XPC_SJOW_GetProperty, XPC_SJOW_SetProperty,
+    XPC_SJOW_Enumerate,   (JSResolveOp)XPC_SJOW_NewResolve,
+    XPC_SJOW_Convert,     XPC_SJOW_Finalize,
+    nsnull,               XPC_SJOW_CheckAccess,
+    XPC_SJOW_Call,        XPC_SJOW_Create,
+    nsnull,               nsnull,
+    nsnull,               nsnull
+  },
+  // JSExtendedClass initialization
+  XPC_SJOW_Equality,
+  nsnull, // outerObject
+  nsnull, // innerObject
+  XPC_SJOW_Iterator,
+  XPC_SJOW_WrappedObject,
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
 JSBool
 WrapObject(JSContext *cx, JSObject *scope, jsval v, jsval *vp)
 {
   // This might be redundant if called from XPC_SJOW_Construct, but it should
   // be cheap in that case.
   JSObject *objToWrap = UnsafeUnwrapSecurityWrapper(cx, JSVAL_TO_OBJECT(v));
@@ -320,17 +313,17 @@ WrapObject(JSContext *cx, JSObject *scop
   }
 
   SLIM_LOG_WILL_MORPH(cx, objToWrap);
   if (IS_SLIM_WRAPPER(objToWrap) && !MorphSlimWrapper(cx, objToWrap)) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
   JSObject *wrapperObj =
-    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SJOWClass), nsnull, scope);
+    JS_NewObjectWithGivenProto(cx, &SJOWClass.base, nsnull, scope);
 
   if (!wrapperObj) {
     // JS_NewObjectWithGivenProto already threw.
     return JS_FALSE;
   }
 
   *vp = OBJECT_TO_JSVAL(wrapperObj);
   if (!JS_SetReservedSlot(cx, wrapperObj, XPCWrapper::sWrappedObjSlot,
@@ -348,17 +341,17 @@ AttachNewConstructorObject(XPCCallContex
   // Initialize sEvalNative the first time we attach a constructor.
   // NB: This always happens before any cross origin wrappers are
   // created, so it's OK to do this here.
   if (!XPCWrapper::FindEval(ccx, aGlobalObject)) {
     return PR_FALSE;
   }
 
   JSObject *class_obj =
-    ::JS_InitClass(ccx, aGlobalObject, nsnull, js::Jsvalify(&SJOWClass),
+    ::JS_InitClass(ccx, aGlobalObject, nsnull, &SJOWClass.base,
                    XPC_SJOW_Construct, 0, nsnull, nsnull, nsnull, nsnull);
   if (!class_obj) {
     NS_WARNING("can't initialize the XPCSafeJSObjectWrapper class");
     return PR_FALSE;
   }
 
   if (!::JS_DefineFunction(ccx, class_obj, "toString", XPC_SJOW_toString,
                            0, 0)) {
@@ -370,17 +363,17 @@ AttachNewConstructorObject(XPCCallContex
   ::JS_SetPrototype(ccx, class_obj, nsnull);
   if (!::JS_SealObject(ccx, class_obj, JS_FALSE)) {
     NS_WARNING("Failed to seal XPCSafeJSObjectWrapper.prototype");
     return PR_FALSE;
   }
 
   JSBool found;
   return ::JS_SetPropertyAttributes(ccx, aGlobalObject,
-                                    SJOWClass.name,
+                                    SJOWClass.base.name,
                                     JSPROP_READONLY | JSPROP_PERMANENT,
                                     &found);
 }
 
 JSObject *
 GetUnsafeObject(JSContext *cx, JSObject *obj)
 {
   obj = FindSafeObject(obj);
@@ -448,17 +441,17 @@ WrapJSValue(JSContext *cx, JSObject *obj
     if (!RewrapObject(cx, obj->getParent(), JSVAL_TO_OBJECT(val), SJOW,
                       rval)) {
       return JS_FALSE;
     }
     // Construct a new safe wrapper. Note that it doesn't matter what
     // parent we pass in here, the construct hook will ensure we get
     // the right parent for the wrapper.
     JSObject *safeObj = JSVAL_TO_OBJECT(*rval);
-    if (safeObj->getClass() == &SJOWClass &&
+    if (safeObj->getJSClass() == &SJOWClass.base &&
         JS_GetGlobalForObject(cx, obj) != JS_GetGlobalForObject(cx, safeObj)) {
       // Check to see if the new object we just wrapped is accessible
       // from the unsafe object we got the new object through. If not,
       // force the new wrapper to use the principal of the unsafe
       // object we got the new object from.
       nsCOMPtr<nsIPrincipal> srcObjPrincipal;
       nsCOMPtr<nsIPrincipal> subjPrincipal;
       nsCOMPtr<nsIPrincipal> valObjPrincipal;
@@ -1032,17 +1025,17 @@ XPC_SJOW_Iterator(JSContext *cx, JSObjec
   // Check that the caller can access the unsafe object.
   if (!CanCallerAccess(cx, obj, unsafeObj)) {
     // CanCallerAccess() already threw for us.
     return nsnull;
   }
 
   // Create our dummy SJOW.
   JSObject *wrapperIter =
-    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SJOWClass), nsnull,
+    JS_NewObjectWithGivenProto(cx, &SJOWClass.base, nsnull,
                                JS_GetGlobalForObject(cx, obj));
   if (!wrapperIter) {
     return nsnull;
   }
 
   if (!JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sWrappedObjSlot,
                           OBJECT_TO_JSVAL(unsafeObj)) ||
       !JS_SetReservedSlot(cx, wrapperIter, XPCWrapper::sFlagsSlot,
--- a/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp
+++ b/js/src/xpconnect/src/XPCSystemOnlyWrapper.cpp
@@ -93,58 +93,52 @@ ThrowException(nsresult rv, JSContext *c
 {
   return DoThrowException(rv, cx);
 }
 
 static const char prefix[] = "chrome://global/";
 
 namespace SystemOnlyWrapper {
 
-js::Class SOWClass = {
-    "SystemOnlyWrapper",
-    JSCLASS_NEW_RESOLVE |
+JSExtendedClass SOWClass = {
+  // JSClass (JSExtendedClass.base) initialization
+  { "SystemOnlyWrapper",
+    JSCLASS_NEW_RESOLVE | JSCLASS_IS_EXTENDED |
     JSCLASS_HAS_RESERVED_SLOTS(XPCWrapper::sNumSlots),
-    js::Valueify(XPC_SOW_AddProperty),
-    js::Valueify(XPC_SOW_DelProperty),
-    js::Valueify(XPC_SOW_GetProperty),
-    js::Valueify(XPC_SOW_SetProperty),
-    XPC_SOW_Enumerate,
-    (JSResolveOp)XPC_SOW_NewResolve,
-    js::Valueify(XPC_SOW_Convert),
-    nsnull,   // finalize
-    nsnull,   // reserved0
-    js::Valueify(XPC_SOW_CheckAccess),
-    nsnull,   // call
-    nsnull,   // construct
-    nsnull,   // xdrObject
-    js::Valueify(XPC_SOW_HasInstance),
-    nsnull,   // mark
+    XPC_SOW_AddProperty, XPC_SOW_DelProperty,
+    XPC_SOW_GetProperty, XPC_SOW_SetProperty,
+    XPC_SOW_Enumerate,   (JSResolveOp)XPC_SOW_NewResolve,
+    XPC_SOW_Convert,     nsnull,
+    nsnull,              XPC_SOW_CheckAccess,
+    nsnull,              nsnull,
+    nsnull,              XPC_SOW_HasInstance,
+    nsnull,              nsnull
+  },
 
-    // ClassExtension
-    {
-      js::Valueify(XPC_SOW_Equality),
-      nsnull, // outerObject
-      nsnull, // innerObject
-      XPC_SOW_Iterator,
-      XPC_SOW_WrappedObject
-    }
+  // JSExtendedClass initialization
+  XPC_SOW_Equality,
+  nsnull,             // outerObject
+  nsnull,             // innerObject
+  XPC_SOW_Iterator,
+  XPC_SOW_WrappedObject,
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
 JSBool
 WrapObject(JSContext *cx, JSObject *parent, jsval v, jsval *vp)
 {
   // Slim wrappers don't expect to be wrapped, so morph them to fat wrappers
   // if we're about to wrap one.
   JSObject *innerObj = JSVAL_TO_OBJECT(v);
   if (IS_SLIM_WRAPPER(innerObj) && !MorphSlimWrapper(cx, innerObj)) {
     return ThrowException(NS_ERROR_FAILURE, cx);
   }
 
   JSObject *wrapperObj =
-    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&SOWClass), NULL, parent);
+    JS_NewObjectWithGivenProto(cx, &SOWClass.base, NULL, parent);
   if (!wrapperObj) {
     return JS_FALSE;
   }
 
   *vp = OBJECT_TO_JSVAL(wrapperObj);
   js::AutoObjectRooter tvr(cx, wrapperObj);
 
   if (!JS_SetReservedSlot(cx, wrapperObj, sWrappedObjSlot, v) ||
@@ -155,19 +149,19 @@ WrapObject(JSContext *cx, JSObject *pare
   return JS_TRUE;
 }
 
 JSBool
 MakeSOW(JSContext *cx, JSObject *obj)
 {
 #ifdef DEBUG
   {
-    js::Class *clasp = obj->getClass();
-    NS_ASSERTION(clasp != &SystemOnlyWrapper::SOWClass &&
-                 clasp != &XPCCrossOriginWrapper::XOWClass,
+    JSClass *clasp = obj->getJSClass();
+    NS_ASSERTION(clasp != &SystemOnlyWrapper::SOWClass.base &&
+                 clasp != &XPCCrossOriginWrapper::XOWClass.base,
                  "bad call");
   }
 #endif
 
   jsval flags;
   return JS_GetReservedSlot(cx, obj, sFlagsSlot, &flags) &&
          JS_SetReservedSlot(cx, obj, sFlagsSlot,
                             INT_TO_JSVAL(JSVAL_TO_INT(flags) | FLAG_SOW));
@@ -269,27 +263,35 @@ CheckFilename(JSContext *cx, jsid id, JS
 
 using namespace SystemOnlyWrapper;
 
 // Like GetWrappedObject, but works on other types of wrappers, too.
 // TODO Move to XPCWrapper?
 static inline JSObject *
 GetWrappedJSObject(JSContext *cx, JSObject *obj)
 {
-  if (JSObjectOp op = obj->getClass()->ext.wrappedObject)
-    return op(cx, obj);
-  return obj;
+  JSClass *clasp = obj->getJSClass();
+  if (!(clasp->flags & JSCLASS_IS_EXTENDED)) {
+    return obj;
+  }
+
+  JSExtendedClass *xclasp = (JSExtendedClass *)clasp;
+  if (!xclasp->wrappedObject) {
+    return obj;
+  }
+
+  return xclasp->wrappedObject(cx, obj);
 }
 
 // Get the (possibly nonexistent) SOW off of an object
 static inline
 JSObject *
 GetWrapper(JSObject *obj)
 {
-  while (obj->getClass() != &SOWClass) {
+  while (obj->getJSClass() != &SOWClass.base) {
     obj = obj->getProto();
     if (!obj) {
       break;
     }
   }
 
   return obj;
 }
@@ -398,17 +400,17 @@ XPC_SOW_RewrapValue(JSContext *cx, JSObj
         return JS_FALSE;
       }
       obj = JSVAL_TO_OBJECT(v);
     }
 
     return XPC_SOW_WrapFunction(cx, wrapperObj, obj, vp);
   }
 
-  if (obj->getClass() == &SOWClass) {
+  if (obj->getJSClass() == &SOWClass.base) {
     // We are extra careful about content-polluted wrappers here. I don't know
     // if it's possible to reach them through objects that we wrap, but figuring
     // that out is more expensive (and harder) than simply checking and
     // rewrapping here.
     if (wrapperObj->getParent() == obj->getParent()) {
       // Already wrapped.
       return JS_TRUE;
     }
@@ -423,17 +425,17 @@ XPC_SOW_RewrapValue(JSContext *cx, JSObj
   }
 
   return WrapObject(cx, wrapperObj->getParent(), v, vp);
 }
 
 static JSBool
 XPC_SOW_AddProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
-  NS_ASSERTION(obj->getClass() == &SOWClass, "Wrong object");
+  NS_ASSERTION(obj->getJSClass() == &SOWClass.base, "Wrong object");
 
   jsval resolving;
   if (!JS_GetReservedSlot(cx, obj, sFlagsSlot, &resolving)) {
     return JS_FALSE;
   }
 
   if (HAS_FLAGS(resolving, FLAG_RESOLVING)) {
     // Allow us to define a property on ourselves.
@@ -646,42 +648,49 @@ XPC_SOW_Equality(JSContext *cx, JSObject
   JSObject *lhs = GetWrappedObject(cx, obj);
   JSObject *rhs = GetWrappedJSObject(cx, JSVAL_TO_OBJECT(v));
   if (lhs == rhs) {
     *bp = JS_TRUE;
     return JS_TRUE;
   }
 
   if (lhs) {
-    if (JSEqualityOp op = js::Jsvalify(lhs->getClass()->ext.equality)) {
+    // Delegate to our wrapped object if we can.
+    JSClass *clasp = lhs->getJSClass();
+    if (clasp->flags & JSCLASS_IS_EXTENDED) {
+      JSExtendedClass *xclasp = (JSExtendedClass *) clasp;
+      // NB: JSExtendedClass.equality is a required field.
       jsval rhsVal = OBJECT_TO_JSVAL(rhs);
-      return op(cx, lhs, &rhsVal, bp);
+      return xclasp->equality(cx, lhs, &rhsVal, bp);
     }
   }
 
   // We know rhs is non-null.
-  if (JSEqualityOp op = js::Jsvalify(rhs->getClass()->ext.equality)) {
+  JSClass *clasp = rhs->getJSClass();
+  if (clasp->flags & JSCLASS_IS_EXTENDED) {
+    JSExtendedClass *xclasp = (JSExtendedClass *) clasp;
+    // NB: JSExtendedClass.equality is a required field.
     jsval lhsVal = OBJECT_TO_JSVAL(lhs);
-    return op(cx, rhs, &lhsVal, bp);
+    return xclasp->equality(cx, rhs, &lhsVal, bp);
   }
 
   *bp = JS_FALSE;
   return JS_TRUE;
 }
 
 static JSObject *
 XPC_SOW_Iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
 {
   JSObject *wrappedObj = GetWrappedObject(cx, obj);
   if (!wrappedObj) {
     ThrowException(NS_ERROR_INVALID_ARG, cx);
     return nsnull;
   }
 
-  JSObject *wrapperIter = JS_NewObject(cx, js::Jsvalify(&SOWClass), nsnull,
+  JSObject *wrapperIter = JS_NewObject(cx, &SOWClass.base, nsnull,
                                        JS_GetGlobalForObject(cx, obj));
   if (!wrapperIter) {
     return nsnull;
   }
 
   js::AutoObjectRooter tvr(cx, wrapperIter);
 
   // Initialize our SOW.
--- a/js/src/xpconnect/src/XPCWrapper.cpp
+++ b/js/src/xpconnect/src/XPCWrapper.cpp
@@ -55,48 +55,48 @@ const PRUint32 FLAG_SOW = 0x2;
 const PRUint32 LAST_FLAG = FLAG_SOW;
 
 const PRUint32 sSecMgrSetProp = nsIXPCSecurityManager::ACCESS_SET_PROPERTY;
 const PRUint32 sSecMgrGetProp = nsIXPCSecurityManager::ACCESS_GET_PROPERTY;
 
 JSObject *
 Unwrap(JSContext *cx, JSObject *wrapper)
 {
-  js::Class *clasp = wrapper->getClass();
-  if (clasp == &XPCCrossOriginWrapper::XOWClass) {
+  JSClass *clasp = wrapper->getJSClass();
+  if (clasp == &XPCCrossOriginWrapper::XOWClass.base) {
     return UnwrapXOW(cx, wrapper);
   }
 
   if (XPCNativeWrapper::IsNativeWrapperClass(clasp)) {
     XPCWrappedNative *wrappedObj;
     if (!XPCNativeWrapper::GetWrappedNative(cx, wrapper, &wrappedObj) ||
         !wrappedObj) {
       return nsnull;
     }
 
     return wrappedObj->GetFlatJSObject();
   }
 
-  if (clasp == &XPCSafeJSObjectWrapper::SJOWClass) {
+  if (clasp == &XPCSafeJSObjectWrapper::SJOWClass.base) {
     JSObject *wrappedObj =
       XPCSafeJSObjectWrapper::GetUnsafeObject(cx, wrapper);
 
     if (NS_FAILED(XPCCrossOriginWrapper::CanAccessWrapper(cx, nsnull, wrappedObj, nsnull))) {
       JS_ClearPendingException(cx);
 
       return nsnull;
     }
 
     return wrappedObj;
   }
 
-  if (clasp == &SystemOnlyWrapper::SOWClass) {
+  if (clasp == &SystemOnlyWrapper::SOWClass.base) {
     return UnwrapSOW(cx, wrapper);
   }
-  if (clasp == &ChromeObjectWrapper::COWClass) {
+  if (clasp == &ChromeObjectWrapper::COWClass.base) {
     return UnwrapCOW(cx, wrapper);
   }
 
   return nsnull;
 }
 
 static void
 IteratorFinalize(JSContext *cx, JSObject *obj)
@@ -155,43 +155,32 @@ IteratorNext(JSContext *cx, uintN argc, 
 }
 
 static JSObject *
 IteratorIterator(JSContext *, JSObject *obj, JSBool)
 {
   return obj;
 }
 
-static js::Class IteratorClass = {
-    "Wrapper iterator",
-    JSCLASS_HAS_RESERVED_SLOTS(3),
-    js::PropertyStub,   // addProperty
-    js::PropertyStub,   // delProperty 
-    js::PropertyStub,   // getProperty
-    js::PropertyStub,   // setProperty
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    js::ConvertStub,
-    IteratorFinalize,
-    nsnull,             // reserved0
-    nsnull,             // checkAccess
-    nsnull,             // call
-    nsnull,             // construct
-    nsnull,             // xdrObject
-    nsnull,             // hasInstance
-    nsnull,             // mark
+static JSExtendedClass IteratorClass = {
+  { "Wrapper iterator",
+    JSCLASS_HAS_RESERVED_SLOTS(3) | JSCLASS_IS_EXTENDED,
+    JS_PropertyStub, JS_PropertyStub,
+    JS_PropertyStub, JS_PropertyStub,
+    JS_EnumerateStub, JS_ResolveStub,
+    JS_ConvertStub, IteratorFinalize,
 
-    // ClassExtension
-    {
-      nsnull, // equality
-      nsnull, // outerObject
-      nsnull, // innerObject
-      IteratorIterator,
-      nsnull, // wrappedObject
-    }
+    JSCLASS_NO_OPTIONAL_MEMBERS
+  },
+
+  nsnull,             // equality
+  nsnull, nsnull,     // innerObject/outerObject
+  IteratorIterator,
+  nsnull,             // wrappedObject
+  JSCLASS_NO_RESERVED_MEMBERS
 };
 
 JSBool
 RewrapObject(JSContext *cx, JSObject *scope, JSObject *obj, WrapperType hint,
              jsval *vp)
 {
   obj = UnsafeUnwrapSecurityWrapper(cx, obj);
   if (!obj) {
@@ -337,17 +326,17 @@ CreateIteratorObj(JSContext *cx, JSObjec
 {
   // This is rather ugly: we want to use the trick seen in Enumerate,
   // where we use our wrapper's resolve hook to determine if we should
   // enumerate a given property. However, we don't want to pollute the
   // identifiers with a next method, so we create an object that
   // delegates (via the __proto__ link) to the wrapper.
 
   JSObject *iterObj =
-    JS_NewObjectWithGivenProto(cx, js::Jsvalify(&IteratorClass), tempWrapper, wrapperObj);
+    JS_NewObjectWithGivenProto(cx, &IteratorClass.base, tempWrapper, wrapperObj);
   if (!iterObj) {
     return nsnull;
   }
 
   js::AutoObjectRooter tvr(cx, iterObj);
 
   // Do this sooner rather than later to avoid complications in
   // IteratorFinalize.
@@ -400,17 +389,17 @@ SimpleEnumerate(JSContext *cx, JSObject 
 
   return JS_TRUE;
 }
 
 JSObject *
 CreateSimpleIterator(JSContext *cx, JSObject *scope, JSBool keysonly,
                      JSObject *propertyContainer)
 {
-  JSObject *iterObj = JS_NewObjectWithGivenProto(cx, js::Jsvalify(&IteratorClass),
+  JSObject *iterObj = JS_NewObjectWithGivenProto(cx, &IteratorClass.base,
                                                  propertyContainer, scope);
   if (!iterObj) {
     return nsnull;
   }
 
   js::AutoObjectRooter tvr(cx, iterObj);
   if (!propertyContainer) {
     if (!JS_SetReservedSlot(cx, iterObj, 0, PRIVATE_TO_JSVAL(nsnull)) ||
--- a/js/src/xpconnect/src/XPCWrapper.h
+++ b/js/src/xpconnect/src/XPCWrapper.h
@@ -157,20 +157,20 @@ MakeSOW(JSContext *cx, JSObject *obj);
 JSBool
 AllowedToAct(JSContext *cx, jsid id);
 
 JSBool
 CheckFilename(JSContext *cx, jsid id, JSStackFrame *fp);
 
 }
 
-namespace ChromeObjectWrapper    { extern js::Class COWClass; }
-namespace XPCSafeJSObjectWrapper { extern js::Class SJOWClass; }
-namespace SystemOnlyWrapper      { extern js::Class SOWClass; }
-namespace XPCCrossOriginWrapper  { extern js::Class XOWClass; }
+namespace ChromeObjectWrapper    { extern JSExtendedClass COWClass; }
+namespace XPCSafeJSObjectWrapper { extern JSExtendedClass SJOWClass; }
+namespace SystemOnlyWrapper      { extern JSExtendedClass SOWClass; }
+namespace XPCCrossOriginWrapper  { extern JSExtendedClass XOWClass; }
 
 extern nsIScriptSecurityManager *gScriptSecurityManager;
 
 // This namespace wraps some common functionality between the three existing
 // wrappers. Its main purpose is to allow XPCCrossOriginWrapper to act both
 // as an XPCSafeJSObjectWrapper and as an XPCNativeWrapper when required to
 // do so (the decision is based on the principals of the wrapper and wrapped
 // objects).
@@ -316,17 +316,19 @@ MaybePreserveWrapper(JSContext *cx, XPCW
       ci->PreserveWrapper(wn->Native());
     }
   }
 }
 
 inline JSBool
 IsSecurityWrapper(JSObject *wrapper)
 {
-  return !!wrapper->getClass()->ext.wrappedObject;
+  JSClass *clasp = wrapper->getJSClass();
+  return (clasp->flags & JSCLASS_IS_EXTENDED) &&
+    ((JSExtendedClass*)clasp)->wrappedObject;
 }
 
 /**
  * Given an arbitrary object, Unwrap will return the wrapped object if the
  * passed-in object is a wrapper that Unwrap knows about *and* the
  * currently running code has permission to access both the wrapper and
  * wrapped object.
  *
@@ -336,19 +338,19 @@ IsSecurityWrapper(JSObject *wrapper)
  */
 JSObject *
 Unwrap(JSContext *cx, JSObject *wrapper);
 
 /**
  * Unwraps objects whose class is |xclasp|.
  */
 inline JSObject *
-UnwrapGeneric(JSContext *cx, const js::Class *xclasp, JSObject *wrapper)
+UnwrapGeneric(JSContext *cx, const JSExtendedClass *xclasp, JSObject *wrapper)
 {
-  if (wrapper->getClass() != xclasp) {
+  if (wrapper->getJSClass() != &xclasp->base) {
     return nsnull;
   }
 
   jsval v;
   if (!JS_GetReservedSlot(cx, wrapper, XPCWrapper::sWrappedObjSlot, &v)) {
     JS_ClearPendingException(cx);
     return nsnull;
   }
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -622,30 +622,30 @@ nsXPConnect::IsGray(void *thing)
 
 NS_IMETHODIMP
 nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
 {
     JSContext *cx = mCycleCollectionContext->GetJSContext();
 
     uint32 traceKind = js_GetGCThingTraceKind(p);
     JSObject *obj;
-    js::Class *clazz;
+    JSClass *clazz;
 
     // We do not want to add wrappers to the cycle collector if they're not
     // explicitly marked as main thread only, because the cycle collector isn't
     // able to deal with objects that might be used off of the main thread. We
     // do want to explicitly mark them for cycle collection if the wrapper has
     // an external reference, because the wrapper would mark the JS object if
     // we did add the wrapper to the cycle collector.
     JSBool dontTraverse = PR_FALSE;
     JSBool markJSObject = PR_FALSE;
     if(traceKind == JSTRACE_OBJECT)
     {
         obj = static_cast<JSObject*>(p);
-        clazz = obj->getClass();
+        clazz = obj->getJSClass();
 
         if(clazz == &XPC_WN_Tearoff_JSClass)
         {
             XPCWrappedNative *wrapper =
                 (XPCWrappedNative*)xpc_GetJSPrivate(obj->getParent());
             dontTraverse = WrapperIsNotMainThreadOnly(wrapper);
         }
         else if(IS_WRAPPER_CLASS(clazz) && IS_WN_WRAPPER_OBJECT(obj))
@@ -684,17 +684,17 @@ nsXPConnect::Traverse(void *p, nsCycleCo
         type = !markJSObject && IsGray(p) ? GCUnmarked : GCMarked;
     }
 
     if (cb.WantDebugInfo()) {
         char name[72];
         if(traceKind == JSTRACE_OBJECT)
         {
             JSObject *obj = static_cast<JSObject*>(p);
-            js::Class *clazz = obj->getClass();
+            JSClass *clazz = obj->getJSClass();
             if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
             {
                 XPCWrappedNative* wn;
                 if(XPCNativeWrapper::GetWrappedNative(cx, obj, &wn) && wn)
                 {
                     XPCNativeScriptableInfo* si = wn->GetScriptableInfo();
                     if(si)
                     {
@@ -744,31 +744,31 @@ nsXPConnect::Traverse(void *p, nsCycleCo
                         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
                     si = p->GetScriptableInfo();
                 }
                 if(si)
                 {
                     JS_snprintf(name, sizeof(name), "JS Object (%s - %s)",
                                 clazz->name, si->GetJSClass()->name);
                 }
-                else if(clazz == &js_ScriptClass)
+                else if(clazz == Jsvalify(&js_ScriptClass))
                 {
                     JSScript* script = (JSScript*) xpc_GetJSPrivate(obj);
                     if(script->filename)
                     {
                         JS_snprintf(name, sizeof(name),
                                     "JS Object (Script - %s)",
                                     script->filename);
                     }
                     else
                     {
                         JS_snprintf(name, sizeof(name), "JS Object (Script)");
                     }
                 }
-                else if(clazz == &js_FunctionClass)
+                else if(clazz == Jsvalify(&js_FunctionClass))
                 {
                     JSFunction* fun = (JSFunction*) xpc_GetJSPrivate(obj);
                     JSString* str = JS_GetFunctionId(fun);
                     if(str)
                     {
                         NS_ConvertUTF16toUTF8
                             fname(reinterpret_cast<const PRUnichar*>(JS_GetStringChars(str)));
                         JS_snprintf(name, sizeof(name),
@@ -1011,16 +1011,19 @@ nsXPConnect::InitClasses(JSContext * aJS
     // Nest frame chain save/restore in request created by XPCCallContext.
     XPCCallContext ccx(NATIVE_CALLER, aJSContext);
     if(!ccx.IsValid())
         return UnexpectedFailure(NS_ERROR_FAILURE);
     SaveFrame sf(aJSContext);
 
     xpc_InitJSxIDClassObjects();
 
+    if(!xpc_InitWrappedNativeJSOps())
+        return UnexpectedFailure(NS_ERROR_FAILURE);
+
     XPCWrappedNativeScope* scope =
         XPCWrappedNativeScope::GetNewOrUsed(ccx, aGlobalJSObj);
 
     if(!scope)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     scope->RemoveWrappedNativeProtos();
 
@@ -1909,17 +1912,17 @@ nsXPConnect::RestoreWrappedNativePrototy
     if(!aClassInfo || !aPrototype)
         return UnexpectedFailure(NS_ERROR_INVALID_ARG);
 
     JSObject *protoJSObject;
     nsresult rv = aPrototype->GetJSObject(&protoJSObject);
     if(NS_FAILED(rv))
         return UnexpectedFailure(rv);
 
-    if(!IS_PROTO_CLASS(protoJSObject->getClass()))
+    if(!IS_PROTO_CLASS(protoJSObject->getJSClass()))
         return UnexpectedFailure(NS_ERROR_INVALID_ARG);
 
     XPCWrappedNativeScope* scope =
         XPCWrappedNativeScope::FindInJSObjectScope(ccx, aScope);
     if(!scope)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     XPCWrappedNativeProto *proto =
--- a/js/src/xpconnect/src/xpcJSWeakReference.cpp
+++ b/js/src/xpconnect/src/xpcJSWeakReference.cpp
@@ -129,18 +129,18 @@ xpcJSWeakReference::Get()
             // Most users of XPCWrappedJS don't need to worry about
             // re-wrapping because things are implicitly rewrapped by
             // xpcconvert. However, because we're doing this directly
             // through the native call context, we need to call
             // nsXPConnect::GetWrapperForObject. But it takes a lot of
             // arguments! It turns out that the thisObject hook on XPConnect
             // objects does the right thing though, so...
 
-            if (obj->getOps()->thisObject &&
-                !(obj = obj->getOps()->thisObject(cx, obj)))
+            if (obj->map->ops->thisObject &&
+                !(obj = obj->map->ops->thisObject(cx, obj)))
             {
                 return NS_ERROR_FAILURE;
             }
 
             *retval = OBJECT_TO_JSVAL(obj);
         }
     }
 
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -3112,17 +3112,17 @@ static JSBool
 sandbox_setProto(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     if (!JSVAL_IS_OBJECT(*vp)) {
         return JS_TRUE;
     }
 
     JSObject *pobj = JSVAL_TO_OBJECT(*vp);
     if (pobj) {
-        if (pobj->getClass() == &XPCCrossOriginWrapper::XOWClass &&
+        if (pobj->getJSClass() == &XPCCrossOriginWrapper::XOWClass.base &&
             !XPCWrapper::RewrapObject(cx, obj, pobj,
                                       XPCWrapper::XPCNW_EXPLICIT, vp)) {
             return JS_FALSE;
         }
     }
 
     return JS_SetPrototype(cx, obj, JSVAL_TO_OBJECT(*vp));
 }
@@ -3820,19 +3820,23 @@ nsXPCComponents_Utils::GetGlobalForObjec
 
   // first argument must be an object
   if(JSVAL_IS_PRIMITIVE(argv[0]))
     return NS_ERROR_XPC_BAD_CONVERT_JS;
 
   JSObject *obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(argv[0]));
   *rval = OBJECT_TO_JSVAL(obj);
 
-  // Outerize if necessary.
-  if (JSObjectOp outerize = obj->getClass()->ext.outerObject)
+  // Outerize if necessary.  Embrace the ugliness!
+  JSClass *clasp = JS_GetClass(cx, obj);
+  if (clasp->flags & JSCLASS_IS_EXTENDED) {
+    JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp);
+    if (JSObjectOp outerize = xclasp->outerObject)
       *rval = OBJECT_TO_JSVAL(outerize(cx, obj));
+  }
 
   cc->SetReturnValueWasSet(PR_TRUE);
   return NS_OK;
 }
 
 #ifdef XPC_USE_SECURITY_CHECKED_COMPONENT
 /* string canCreateWrapper (in nsIIDPtr iid); */
 NS_IMETHODIMP
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -1262,102 +1262,60 @@ private:
 * Core classes for wrapped native objects for use from JavaScript...
 *
 ****************************************************************************
 ***************************************************************************/
 
 // These are the various JSClasses and callbacks whose use that required
 // visibility from more than one .cpp file.
 
-extern js::Class XPC_WN_NoHelper_JSClass;
-extern js::Class XPC_WN_NoMods_WithCall_Proto_JSClass;
-extern js::Class XPC_WN_NoMods_NoCall_Proto_JSClass;
-extern js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass;
-extern js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass;
-extern js::Class XPC_WN_Tearoff_JSClass;
-extern js::Class XPC_WN_NoHelper_Proto_JSClass;
+extern JSExtendedClass XPC_WN_NoHelper_JSClass;
+extern JSClass XPC_WN_NoMods_WithCall_Proto_JSClass;
+extern JSClass XPC_WN_NoMods_NoCall_Proto_JSClass;
+extern JSClass XPC_WN_ModsAllowed_WithCall_Proto_JSClass;
+extern JSClass XPC_WN_ModsAllowed_NoCall_Proto_JSClass;
+extern JSClass XPC_WN_Tearoff_JSClass;
+extern JSClass XPC_WN_NoHelper_Proto_JSClass;
 
 extern JSBool
 XPC_WN_Equality(JSContext *cx, JSObject *obj, const jsval *v, JSBool *bp);
 
+extern JSObjectOps *
+XPC_WN_Proto_GetObjectOps(JSContext *cx, JSClass *clazz);
+
 extern JSBool
 XPC_WN_CallMethod(JSContext *cx, JSObject *obj,
                   uintN argc, jsval *argv, jsval *vp);
 
 extern JSBool
 XPC_WN_GetterSetter(JSContext *cx, JSObject *obj,
                     uintN argc, jsval *argv, jsval *vp);
 
 extern JSBool
-XPC_WN_JSOp_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
-                      jsval *statep, jsid *idp);
-
-extern JSType
-XPC_WN_JSOp_TypeOf_Object(JSContext *cx, JSObject *obj);
-
-extern JSType
-XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSObject *obj);
-
-extern void
-XPC_WN_JSOp_Clear(JSContext *cx, JSObject *obj);
-
-extern JSObject*
-XPC_WN_JSOp_ThisObject(JSContext *cx, JSObject *obj);
-
-// Macros to initialize Object or Function like XPC_WN classes
-#define XPC_WN_WithCall_ObjectOps                                             \
-    {                                                                         \
-        nsnull, /* lookupProperty */                                          \
-        nsnull, /* defineProperty */                                          \
-        nsnull, /* getProperty    */                                          \
-        nsnull, /* setProperty    */                                          \
-        nsnull, /* getAttributes  */                                          \
-        nsnull, /* setAttributes  */                                          \
-        nsnull, /* deleteProperty */                                          \
-        js::Valueify(XPC_WN_JSOp_Enumerate),                                  \
-        XPC_WN_JSOp_TypeOf_Function,                                          \
-        nsnull, /* trace          */                                          \
-        XPC_WN_JSOp_ThisObject,                                               \
-        XPC_WN_JSOp_Clear                                                     \
-    }
-
-#define XPC_WN_NoCall_ObjectOps                                               \
-    {                                                                         \
-        nsnull, /* lookupProperty */                                          \
-        nsnull, /* defineProperty */                                          \
-        nsnull, /* getProperty    */                                          \
-        nsnull, /* setProperty    */                                          \
-        nsnull, /* getAttributes  */                                          \
-        nsnull, /* setAttributes  */                                          \
-        nsnull, /* deleteProperty */                                          \
-        js::Valueify(XPC_WN_JSOp_Enumerate),                                  \
-        XPC_WN_JSOp_TypeOf_Object,                                            \
-        nsnull, /* trace          */                                          \
-        XPC_WN_JSOp_ThisObject,                                               \
-        XPC_WN_JSOp_Clear                                                     \
-    }
+xpc_InitWrappedNativeJSOps();
 
 // Maybe this macro should check for class->enumerate ==
 // XPC_WN_Shared_Proto_Enumerate or something rather than checking for
 // 4 classes?
 #define IS_PROTO_CLASS(clazz)                                                 \
-    ((clazz) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||                      \
-     (clazz) == &XPC_WN_NoMods_NoCall_Proto_JSClass ||                        \
-     (clazz) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||                 \
-     (clazz) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass)
+          ((clazz) == &XPC_WN_NoMods_WithCall_Proto_JSClass ||                \
+           (clazz) == &XPC_WN_NoMods_NoCall_Proto_JSClass ||                  \
+           (clazz) == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||           \
+           (clazz) == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass)
 
 // NOTE!!!
 //
 // If this ever changes,
 // nsScriptSecurityManager::doGetObjectPrincipal() *must* be updated
 // also!
 //
 // NOTE!!!
 #define IS_WRAPPER_CLASS(clazz)                                               \
-    (clazz->ext.equality == js::Valueify(XPC_WN_Equality))
+    (((clazz)->flags & JSCLASS_IS_EXTENDED) &&                                \
+     reinterpret_cast<JSExtendedClass*>(clazz)->equality == XPC_WN_Equality)
 
 inline JSBool
 DebugCheckWrapperClass(JSObject* obj)
 {
     NS_ASSERTION(IS_WRAPPER_CLASS(obj->getClass()),
                  "Forgot to check if this is a wrapper?");
     return JS_TRUE;
 }
@@ -1952,30 +1910,28 @@ public:
 // XPCNativeScriptableShared is used to hold the JSClass and the
 // associated scriptable flags for XPCWrappedNatives. These are shared across
 // the runtime and are garbage collected by xpconnect. We *used* to just store
 // this inside the XPCNativeScriptableInfo (usually owned by instances of
 // XPCWrappedNativeProto. This had two problems... It was wasteful, and it
 // was a big problem when wrappers are reparented to different scopes (and
 // thus different protos (the DOM does this).
 
-struct XPCNativeScriptableSharedJSClass
+struct XPCNativeScriptableSharedJSClass : public JSExtendedClass
 {
-    js::Class base;
     PRUint32 interfacesBitmap;
 };
 
 class XPCNativeScriptableShared
 {
 public:
     const XPCNativeScriptableFlags& GetFlags() const {return mFlags;}
     PRUint32                        GetInterfacesBitmap() const
         {return mJSClass.interfacesBitmap;}
-    JSClass*                        GetJSClass()
-        {return js::Jsvalify(&mJSClass.base);}
+    JSClass*                        GetJSClass() {return &mJSClass.base;}
     JSClass*                        GetSlimJSClass()
         {if(mCanBeSlim) return GetJSClass(); return nsnull;}
 
     XPCNativeScriptableShared(JSUint32 aFlags, char* aName,
                               PRUint32 interfacesBitmap)
         : mFlags(aFlags),
           mCanBeSlim(JS_FALSE)
         {memset(&mJSClass, 0, sizeof(mJSClass));
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -291,17 +291,17 @@ LookupGetterOrSetter(JSContext *cx, JSBo
     // ensuring that we have an XPConnect prototype object ensures that
     // we are only going to expose quickstubbed properties to script.
     // Also be careful not to overwrite existing properties!
 
     const char *name = JSVAL_IS_STRING(idval)
                        ? JS_GetStringBytes(JSVAL_TO_STRING(idval))
                        : nsnull;
     if(!name ||
-       !IS_PROTO_CLASS(desc.obj->getClass()) ||
+       !IS_PROTO_CLASS(desc.obj->getJSClass()) ||
        (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) ||
        !(desc.getter || desc.setter) ||
        desc.setter == desc.obj->getJSClass()->setProperty)
     {
         JS_SET_RVAL(cx, vp, JSVAL_VOID);
         return JS_TRUE;
     }
 
@@ -358,17 +358,17 @@ DefineGetterOrSetter(JSContext *cx, uint
                                                 &found, &getter, &setter)))
         return JS_FALSE;
 
     // The property didn't exist, already has a getter or setter, or is not
     // our property, then just forward now.
     if(!obj2 ||
        (attrs & (JSPROP_GETTER | JSPROP_SETTER)) ||
        !(getter || setter) ||
-       !IS_PROTO_CLASS(obj2->getClass()))
+       !IS_PROTO_CLASS(obj2->getJSClass()))
         return forward(cx, argc, vp);
 
     // Reify the getter and setter...
     if(!ReifyPropertyOps(cx, obj, id, interned_id, name, getter, setter,
                          nsnull, nsnull))
         return JS_FALSE;
 
     return forward(cx, argc, vp);
@@ -499,18 +499,18 @@ GetMemberInfo(JSObject *obj,
 {
     // Get the interface name.  From DefinePropertyIfFound (in
     // xpcwrappednativejsops.cpp) and XPCThrower::Verbosify.
     //
     // We could instead make the quick stub could pass in its interface name,
     // but this code often produces a more specific error message, e.g.
     *ifaceName = "Unknown";
 
-    NS_ASSERTION(IS_WRAPPER_CLASS(obj->getClass()) ||
-                 obj->getClass() == &XPC_WN_Tearoff_JSClass,
+    NS_ASSERTION(IS_WRAPPER_CLASS(obj->getJSClass()) ||
+                 obj->getJSClass() == &XPC_WN_Tearoff_JSClass,
                  "obj must be a wrapper");
     XPCWrappedNativeProto *proto;
     if(IS_SLIM_WRAPPER(obj))
     {
         proto = GetSlimWrapperProto(obj);
     }
     else
     {
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -1119,19 +1119,17 @@ XPCWrappedNative::Init(XPCCallContext& c
             if(HasProto() && !HasSharedProto())
                 GetProto()->SetScriptableInfo(mScriptableInfo);
         }
     }
     XPCNativeScriptableInfo* si = mScriptableInfo;
 
     // create our flatJSObject
 
-    js::Class* jsclazz = si
-                         ? js::Valueify(si->GetJSClass())
-                         : &XPC_WN_NoHelper_JSClass;
+    JSClass* jsclazz = si ? si->GetJSClass() : &XPC_WN_NoHelper_JSClass.base;
 
     if(isGlobal)
     {
         // Resolving a global object's class can cause us to create a global's
         // JS class without the proper global flags. Notice that here and fix
         // the problem.
         if(!(jsclazz->flags & JSCLASS_IS_GLOBAL))
             jsclazz->flags |= JSCLASS_GLOBAL_FLAGS;
@@ -1155,18 +1153,18 @@ XPCWrappedNative::Init(XPCCallContext& c
     JSObject* protoJSObject = HasProto() ?
                                 GetProto()->GetJSProtoObject() :
                                 GetScope()->GetPrototypeNoHelper(ccx);
 
     if (!protoJSObject) {
         return JS_FALSE;
     }
 
-    mFlatJSObject = xpc_NewSystemInheritingJSObject(ccx, js::Jsvalify(jsclazz),
-                                                    protoJSObject, parent);
+    mFlatJSObject = xpc_NewSystemInheritingJSObject(ccx, jsclazz, protoJSObject,
+                                                    parent);
     if(!mFlatJSObject)
         return JS_FALSE;
 
     return FinishInit(ccx);
 }
 
 JSBool
 XPCWrappedNative::Init(XPCCallContext &ccx, JSObject *existingJSObject)
@@ -1680,17 +1678,17 @@ XPCWrappedNative::GetWrappedNativeOfJSOb
     // If we were passed a function object then we need to find the correct
     // wrapper out of those that might be in the callee obj's proto chain.
 
     if(funobj)
     {
         JSObject* funObjParent = funobj->getParent();
         NS_ASSERTION(funObjParent, "funobj has no parent");
 
-        js::Class* funObjParentClass = funObjParent->getClass();
+        JSClass* funObjParentClass = funObjParent->getJSClass();
 
         if(IS_PROTO_CLASS(funObjParentClass))
         {
             NS_ASSERTION(funObjParent->getParent(), "funobj's parent (proto) is global");
             proto = (XPCWrappedNativeProto*) xpc_GetJSPrivate(funObjParent);
             if(proto)
                 protoClassInfo = proto->GetClassInfo();
         }
@@ -1710,18 +1708,18 @@ XPCWrappedNative::GetWrappedNativeOfJSOb
             NS_ERROR("function object has parent of unknown class!");
             return nsnull;
         }
     }
 
     for(cur = obj; cur; cur = cur->getProto())
     {
         // this is on two lines to make the compiler happy given the goto.
-        js::Class* clazz;
-        clazz = cur->getClass();
+        JSClass* clazz;
+        clazz = cur->getJSClass();
 
         if(IS_WRAPPER_CLASS(clazz))
         {
 return_wrapper:
             JSBool isWN = IS_WN_WRAPPER_OBJECT(cur);
             XPCWrappedNative* wrapper =
                 isWN ? (XPCWrappedNative*) xpc_GetJSPrivate(cur) : nsnull;
             if(proto)
@@ -1768,19 +1766,22 @@ return_tearoff:
         if((unsafeObj = XPCWrapper::Unwrap(cx, cur)))
             return GetWrappedNativeOfJSObject(cx, unsafeObj, funobj, pobj2,
                                               pTearOff);
     }
 
     // If we didn't find a wrapper using the given funobj and obj, try
     // again with obj's outer object, if it's got one.
 
-    if(JSObjectOp op = obj->getClass()->ext.outerObject)
+    JSClass *clazz = obj->getJSClass();
+
+    if((clazz->flags & JSCLASS_IS_EXTENDED) &&
+        ((JSExtendedClass*)clazz)->outerObject)
     {
-        JSObject *outer = op(cx, obj);
+        JSObject *outer = ((JSExtendedClass*)clazz)->outerObject(cx, obj);
         if(outer && outer != obj)
             return GetWrappedNativeOfJSObject(cx, outer, funobj, pobj2,
                                               pTearOff);
     }
 
     if(pobj2)
         *pobj2 = nsnull;
     return nsnull;
@@ -2112,17 +2113,17 @@ XPCWrappedNative::InitTearOff(XPCCallCon
 
 JSBool
 XPCWrappedNative::InitTearOffJSObject(XPCCallContext& ccx,
                                       XPCWrappedNativeTearOff* to)
 {
     // This is only called while locked (during XPCWrappedNative::FindTearOff).
 
     JSObject* obj =
-        xpc_NewSystemInheritingJSObject(ccx, js::Jsvalify(&XPC_WN_Tearoff_JSClass),
+        xpc_NewSystemInheritingJSObject(ccx, &XPC_WN_Tearoff_JSClass,
                                         GetScope()->GetPrototypeJSObject(),
                                         mFlatJSObject);
 
     if(!obj || !JS_SetPrivate(ccx, obj, to))
         return JS_FALSE;
 
     to->SetJSObject(obj);
     return JS_TRUE;
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -808,17 +808,17 @@ XPC_WN_Equality(JSContext *cx, JSObject 
     XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
     if(si && si->GetFlags().WantEquality())
     {
         nsresult rv = si->GetCallback()->Equality(wrapper, cx, obj, v, bp);
         if(NS_FAILED(rv))
             return Throw(rv, cx);
 
         if(!*bp && !JSVAL_IS_PRIMITIVE(v) &&
-           JSVAL_TO_OBJECT(v)->getClass() == &XPCSafeJSObjectWrapper::SJOWClass)
+           JSVAL_TO_OBJECT(v)->getJSClass() == &XPCSafeJSObjectWrapper::SJOWClass.base)
         {
             v = OBJECT_TO_JSVAL(XPCSafeJSObjectWrapper::GetUnsafeObject(cx, JSVAL_TO_OBJECT(v)));
 
             rv = si->GetCallback()->Equality(wrapper, cx, obj, v, bp);
             if(NS_FAILED(rv))
                 return Throw(rv, cx);
         }
     }
@@ -907,66 +907,51 @@ XPC_WN_InnerObject(JSContext *cx, JSObje
         }
 
         obj = newThis;
     }
 
     return obj;
 }
 
-js::Class XPC_WN_NoHelper_JSClass = {
-    "XPCWrappedNative_NoHelper",    // name;
-    WRAPPER_SLOTS |
-    JSCLASS_PRIVATE_IS_NSISUPPORTS |
-    JSCLASS_MARK_IS_TRACE, // flags;
+JSObjectOps *XPC_WN_GetObjectOpsNoCall(JSContext *cx, JSClass *clazz);
+
+JSExtendedClass XPC_WN_NoHelper_JSClass = {
+    {
+        "XPCWrappedNative_NoHelper",    // name;
+        WRAPPER_SLOTS |
+        JSCLASS_PRIVATE_IS_NSISUPPORTS |
+        JSCLASS_MARK_IS_TRACE |
+        JSCLASS_IS_EXTENDED, // flags;
+
+        /* Mandatory non-null function pointer members. */
+        XPC_WN_OnlyIWrite_PropertyStub, // addProperty;
+        XPC_WN_CannotModifyPropertyStub,// delProperty;
+        JS_PropertyStub,                // getProperty;
+        XPC_WN_OnlyIWrite_PropertyStub, // setProperty;
 
-    /* Mandatory non-null function pointer members. */
-    js::Valueify(XPC_WN_OnlyIWrite_PropertyStub), // addProperty
-    js::Valueify(XPC_WN_CannotModifyPropertyStub),// delProperty
-    js::PropertyStub,                             // getProperty
-    js::Valueify(XPC_WN_OnlyIWrite_PropertyStub), // setProperty
-   
-    XPC_WN_Shared_Enumerate,                      // enumerate
-    XPC_WN_NoHelper_Resolve,                      // resolve
-    js::Valueify(XPC_WN_Shared_Convert),          // convert
-    XPC_WN_NoHelper_Finalize,                     // finalize
-   
-    /* Optionally non-null members start here. */
-    nsnull,                         // reserved0
-    nsnull,                         // checkAccess
-    nsnull,                         // call
-    nsnull,                         // construct
-    nsnull,                         // xdrObject;
-    nsnull,                         // hasInstance
-    JS_CLASS_TRACE(XPC_WN_Shared_Trace), // mark/trace
+        XPC_WN_Shared_Enumerate,        // enumerate;
+        XPC_WN_NoHelper_Resolve,        // resolve;
+        XPC_WN_Shared_Convert,          // convert;
+        XPC_WN_NoHelper_Finalize,       // finalize;
 
-    // ClassExtension
-    {
-        js::Valueify(XPC_WN_Equality),
-        XPC_WN_OuterObject,
-        XPC_WN_InnerObject,
-        nsnull, // iteratorObject
-        nsnull, // wrappedObject
+        /* Optionally non-null members start here. */
+        XPC_WN_GetObjectOpsNoCall,      // getObjectOps;
+        nsnull,                         // checkAccess;
+        nsnull,                         // call;
+        nsnull,                         // construct;
+        nsnull,                         // xdrObject;
+        nsnull,                         // hasInstance;
+        JS_CLASS_TRACE(XPC_WN_Shared_Trace), // mark/trace;
+        nsnull                          // spare;
     },
-   
-    // ObjectOps
-    {
-        nsnull, // lookupProperty
-        nsnull, // defineProperty
-        nsnull, // getProperty
-        nsnull, // setProperty
-        nsnull, // getAttributes
-        nsnull, // setAttributes
-        nsnull, // deleteProperty
-        js::Valueify(XPC_WN_JSOp_Enumerate),
-        XPC_WN_JSOp_TypeOf_Object,
-        nsnull, // trace
-        XPC_WN_JSOp_ThisObject,
-        XPC_WN_JSOp_Clear
-    }
+    XPC_WN_Equality,
+    XPC_WN_OuterObject,
+    XPC_WN_InnerObject,
+    nsnull,nsnull,nsnull,nsnull,nsnull
 };
 
 
 /***************************************************************************/
 
 static JSBool
 XPC_WN_MaybeResolvingPropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
@@ -1248,16 +1233,21 @@ XPC_WN_Helper_NewResolve(JSContext *cx, 
         }
     }
 
     return retval;
 }
 
 /***************************************************************************/
 
+extern JS_IMPORT_DATA(JSObjectOps) js_ObjectOps;
+
+static JSObjectOps XPC_WN_WithCall_JSOps;
+static JSObjectOps XPC_WN_NoCall_JSOps;
+
 /*
     Here are the enumerator cases:
 
     set jsclass enumerate to stub (unless noted otherwise)
 
     if( helper wants new enumerate )
         if( DONT_ENUM_STATICS )
             forward to scriptable enumerate
@@ -1283,28 +1273,28 @@ XPC_WN_Helper_NewResolve(JSContext *cx, 
 
     else //... if( helper wants NO enumerate )
         if( DONT_ENUM_STATICS )
             use enumerate stub - don't use this JSOp thing at all
         else
             do shared enumerate - don't use this JSOp thing at all
 */
 
-JSBool
+static JSBool
 XPC_WN_JSOp_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                       jsval *statep, jsid *idp)
 {
-    js::Class *clazz = obj->getClass();
-    if(!IS_WRAPPER_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass)
+    JSClass *clazz = obj->getJSClass();
+    if(!IS_WRAPPER_CLASS(clazz) || clazz == &XPC_WN_NoHelper_JSClass.base)
     {
         // obj must be a prototype object or a wrapper w/o a
-        // helper. Short circuit this call to the default
-        // implementation.
+        // helper. Short circuit this call to
+        // js_ObjectOps.enumerate().
 
-        return js_Enumerate(cx, obj, enum_op, js::Valueify(statep), idp);
+        return js_ObjectOps.enumerate(cx, obj, enum_op, js::Valueify(statep), idp);
     }
 
     MORPH_SLIM_WRAPPER(cx, obj);
 
     XPCCallContext ccx(JS_CALLER, cx, obj);
     XPCWrappedNative* wrapper = ccx.GetWrapper();
     THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
 
@@ -1361,38 +1351,38 @@ XPC_WN_JSOp_Enumerate(JSContext *cx, JSO
 
             if(NS_FAILED(rv) || !retval)
                 *statep = JSVAL_NULL;
 
             if(NS_FAILED(rv))
                 return Throw(rv, cx);
             if(!retval)
                 return JS_FALSE;
-            // Then fall through and call the default implementation...
+            // Then fall through and call js_ObjectOps.enumerate...
         }
     }
 
     // else call js_ObjectOps.enumerate...
 
-    return js_Enumerate(cx, obj, enum_op, js::Valueify(statep), idp);
+    return js_ObjectOps.enumerate(cx, obj, enum_op, js::Valueify(statep), idp);
 }
 
-JSType
+static JSType
 XPC_WN_JSOp_TypeOf_Object(JSContext *cx, JSObject *obj)
 {
     return JSTYPE_OBJECT;
 }
 
-JSType
+static JSType
 XPC_WN_JSOp_TypeOf_Function(JSContext *cx, JSObject *obj)
 {
     return JSTYPE_FUNCTION;
 }
 
-void
+static void
 XPC_WN_JSOp_Clear(JSContext *cx, JSObject *obj)
 {
     // We're likely to enter this JSOp with a wrapper prototype
     // object. In that case we won't find a wrapper, so we'll just
     // call into js_ObjectOps.clear(), which is exactly what we want.
 
     // If our scope is cleared, make sure we clear the scope of our
     // native wrapper as well.
@@ -1401,16 +1391,18 @@ XPC_WN_JSOp_Clear(JSContext *cx, JSObjec
 
     if(wrapper && wrapper->IsValid())
     {
         XPCNativeWrapper::ClearWrappedNativeScopes(cx, wrapper);
 
         nsXPConnect* xpc = nsXPConnect::GetXPConnect();
         xpc->UpdateXOWs(cx, wrapper, nsIXPConnect::XPC_XOW_CLEARSCOPE);
     }
+
+    js_ObjectOps.clear(cx, obj);
 }
 
 namespace {
 
 NS_STACK_CLASS class AutoPopJSContext
 {
 public:
   AutoPopJSContext(XPCJSContextStack *stack)
@@ -1439,17 +1431,17 @@ public:
 
 private:
   JSContext *mCx;
   XPCJSContextStack *mStack;
 };
 
 } // namespace
 
-JSObject*
+static JSObject*
 XPC_WN_JSOp_ThisObject(JSContext *cx, JSObject *obj)
 {
     // None of the wrappers we could potentially hand out are threadsafe so
     // just hand out the given object.
     if(!XPCPerThreadData::IsMainThread(cx))
         return obj;
 
     OBJ_TO_OUTER_OBJECT(cx, obj);
@@ -1536,16 +1528,47 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS
             XPCThrower::Throw(rv, cx);
             return nsnull;
         }
     }
 
     return JSVAL_TO_OBJECT(retval.jsval_value());
 }
 
+JSObjectOps *
+XPC_WN_GetObjectOpsNoCall(JSContext *cx, JSClass *clazz)
+{
+    return &XPC_WN_NoCall_JSOps;
+}
+
+JSObjectOps *
+XPC_WN_GetObjectOpsWithCall(JSContext *cx, JSClass *clazz)
+{
+    return &XPC_WN_WithCall_JSOps;
+}
+
+JSBool xpc_InitWrappedNativeJSOps()
+{
+    if(!XPC_WN_NoCall_JSOps.lookupProperty)
+    {
+        memcpy(&XPC_WN_NoCall_JSOps, &js_ObjectOps, sizeof(JSObjectOps));
+        XPC_WN_NoCall_JSOps.enumerate = js::Valueify(XPC_WN_JSOp_Enumerate);
+        XPC_WN_NoCall_JSOps.typeOf = XPC_WN_JSOp_TypeOf_Object;
+        XPC_WN_NoCall_JSOps.clear = XPC_WN_JSOp_Clear;
+        XPC_WN_NoCall_JSOps.thisObject = XPC_WN_JSOp_ThisObject;
+
+        memcpy(&XPC_WN_WithCall_JSOps, &js_ObjectOps, sizeof(JSObjectOps));
+        XPC_WN_WithCall_JSOps.enumerate = js::Valueify(XPC_WN_JSOp_Enumerate);
+        XPC_WN_WithCall_JSOps.typeOf = XPC_WN_JSOp_TypeOf_Function;
+        XPC_WN_WithCall_JSOps.clear = XPC_WN_JSOp_Clear;
+        XPC_WN_WithCall_JSOps.thisObject = XPC_WN_JSOp_ThisObject;
+    }
+    return JS_TRUE;
+}
+
 /***************************************************************************/
 
 // static
 XPCNativeScriptableInfo*
 XPCNativeScriptableInfo::Construct(XPCCallContext& ccx,
                                    JSBool isGlobal,
                                    const XPCNativeScriptableCreateInfo* sci)
 {
@@ -1586,122 +1609,117 @@ XPCNativeScriptableInfo::Construct(XPCCa
 void
 XPCNativeScriptableShared::PopulateJSClass(JSBool isGlobal)
 {
     NS_ASSERTION(mJSClass.base.name, "bad state!");
 
     mJSClass.base.flags = WRAPPER_SLOTS |
                           JSCLASS_PRIVATE_IS_NSISUPPORTS |
                           JSCLASS_NEW_RESOLVE |
-                          JSCLASS_MARK_IS_TRACE;
+                          JSCLASS_MARK_IS_TRACE |
+                          JSCLASS_IS_EXTENDED;
 
     if(isGlobal)
         mJSClass.base.flags |= JSCLASS_GLOBAL_FLAGS;
 
-    JSPropertyOp addProperty;
     if(mFlags.WantAddProperty())
-        addProperty = XPC_WN_Helper_AddProperty;
+        mJSClass.base.addProperty = XPC_WN_Helper_AddProperty;
     else if(mFlags.UseJSStubForAddProperty())
-        addProperty = JS_PropertyStub;
+        mJSClass.base.addProperty = JS_PropertyStub;
     else if(mFlags.AllowPropModsDuringResolve())
-        addProperty = XPC_WN_MaybeResolvingPropertyStub;
+        mJSClass.base.addProperty = XPC_WN_MaybeResolvingPropertyStub;
     else
-        addProperty = XPC_WN_CannotModifyPropertyStub;
-    mJSClass.base.addProperty = js::Valueify(addProperty);
+        mJSClass.base.addProperty = XPC_WN_CannotModifyPropertyStub;
 
-    JSPropertyOp delProperty;
     if(mFlags.WantDelProperty())
-        delProperty = XPC_WN_Helper_DelProperty;
+        mJSClass.base.delProperty = XPC_WN_Helper_DelProperty;
     else if(mFlags.UseJSStubForDelProperty())
-        delProperty = JS_PropertyStub;
+        mJSClass.base.delProperty = JS_PropertyStub;
     else if(mFlags.AllowPropModsDuringResolve())
-        delProperty = XPC_WN_MaybeResolvingPropertyStub;
+        mJSClass.base.delProperty = XPC_WN_MaybeResolvingPropertyStub;
     else
-        delProperty = XPC_WN_CannotModifyPropertyStub;
-    mJSClass.base.delProperty = js::Valueify(delProperty);
+        mJSClass.base.delProperty = XPC_WN_CannotModifyPropertyStub;
 
     if(mFlags.WantGetProperty())
-        mJSClass.base.getProperty = js::Valueify(XPC_WN_Helper_GetProperty);
+        mJSClass.base.getProperty = XPC_WN_Helper_GetProperty;
     else
-        mJSClass.base.getProperty = js::PropertyStub;
+        mJSClass.base.getProperty = JS_PropertyStub;
 
-    JSPropertyOp setProperty;
     if(mFlags.WantSetProperty())
-        setProperty = XPC_WN_Helper_SetProperty;
+        mJSClass.base.setProperty = XPC_WN_Helper_SetProperty;
     else if(mFlags.UseJSStubForSetProperty())
-        setProperty = JS_PropertyStub;
+        mJSClass.base.setProperty = JS_PropertyStub;
     else if(mFlags.AllowPropModsDuringResolve())
-        setProperty = XPC_WN_MaybeResolvingPropertyStub;
+        mJSClass.base.setProperty = XPC_WN_MaybeResolvingPropertyStub;
     else
-        setProperty = XPC_WN_CannotModifyPropertyStub;
-    mJSClass.base.setProperty = js::Valueify(setProperty);
+        mJSClass.base.setProperty = XPC_WN_CannotModifyPropertyStub;
 
     // We figure out most of the enumerate strategy at call time.
 
     if(mFlags.WantNewEnumerate() || mFlags.WantEnumerate() ||
        mFlags.DontEnumStaticProps())
-        mJSClass.base.enumerate = js::EnumerateStub;
+        mJSClass.base.enumerate = JS_EnumerateStub;
     else
         mJSClass.base.enumerate = XPC_WN_Shared_Enumerate;
 
     // We have to figure out resolve strategy at call time
     mJSClass.base.resolve = (JSResolveOp) XPC_WN_Helper_NewResolve;
 
     if(mFlags.WantConvert())
-        mJSClass.base.convert = js::Valueify(XPC_WN_Helper_Convert);
+        mJSClass.base.convert = XPC_WN_Helper_Convert;
     else
-        mJSClass.base.convert = js::Valueify(XPC_WN_Shared_Convert);
+        mJSClass.base.convert = XPC_WN_Shared_Convert;
 
     if(mFlags.WantFinalize())
         mJSClass.base.finalize = XPC_WN_Helper_Finalize;
     else
         mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
 
     // We let the rest default to nsnull unless the helper wants them...
     if(mFlags.WantCheckAccess())
-        mJSClass.base.checkAccess = js::Valueify(XPC_WN_Helper_CheckAccess);
+        mJSClass.base.checkAccess = XPC_WN_Helper_CheckAccess;
 
-    // Note that we *must* set the ObjectOps (even for the cases were it does
-    // not do much) because with these dynamically generated JSClasses, the
-    // code in XPCWrappedNative::GetWrappedNativeOfJSObject() needs to look
-    // for that these callback pointers in order to identify that a given
+    // Note that we *must* set
+    //   mJSClass.base.getObjectOps = XPC_WN_GetObjectOpsNoCall
+    // or
+    //   mJSClass.base.getObjectOps = XPC_WN_GetObjectOpsWithCall
+    // (even for the cases were it does not do much) because with these
+    // dynamically generated JSClasses, the code in
+    // XPCWrappedNative::GetWrappedNativeOfJSObject() needs to look for
+    // that this callback pointer in order to identify that a given
     // JSObject represents a wrapper.
-    js::ObjectOps *ops = &mJSClass.base.ops;
-    ops->enumerate = js::Valueify(XPC_WN_JSOp_Enumerate);
-    ops->clear = XPC_WN_JSOp_Clear;
-    ops->thisObject = XPC_WN_JSOp_ThisObject;
 
     if(mFlags.WantCall() || mFlags.WantConstruct())
     {
-        ops->typeOf = XPC_WN_JSOp_TypeOf_Function;
+        mJSClass.base.getObjectOps = XPC_WN_GetObjectOpsWithCall;
         if(mFlags.WantCall())
-            mJSClass.base.call = js::Valueify(XPC_WN_Helper_Call);
+            mJSClass.base.call = XPC_WN_Helper_Call;
         if(mFlags.WantConstruct())
-            mJSClass.base.construct = js::Valueify(XPC_WN_Helper_Construct);
+            mJSClass.base.construct = XPC_WN_Helper_Construct;
     }
     else
     {
-        ops->typeOf = XPC_WN_JSOp_TypeOf_Object;
+        mJSClass.base.getObjectOps = XPC_WN_GetObjectOpsNoCall;
     }
 
-    // Equality is a required hook.
-    mJSClass.base.ext.equality = js::Valueify(XPC_WN_Equality);
-
     if(mFlags.WantHasInstance())
-        mJSClass.base.hasInstance = js::Valueify(XPC_WN_Helper_HasInstance);
+        mJSClass.base.hasInstance = XPC_WN_Helper_HasInstance;
 
     if(mFlags.WantTrace())
         mJSClass.base.mark = JS_CLASS_TRACE(XPC_WN_Helper_Trace);
     else
         mJSClass.base.mark = JS_CLASS_TRACE(XPC_WN_Shared_Trace);
 
+    // Equality is a required hook.
+    mJSClass.equality = XPC_WN_Equality;
+
     if(mFlags.WantOuterObject())
-        mJSClass.base.ext.outerObject = XPC_WN_OuterObject;
+        mJSClass.outerObject = XPC_WN_OuterObject;
     if(mFlags.WantInnerObject())
-        mJSClass.base.ext.innerObject = XPC_WN_InnerObject;
+        mJSClass.innerObject = XPC_WN_InnerObject;
 
     if(!(mFlags & (nsIXPCScriptable::WANT_OUTER_OBJECT |
                    nsIXPCScriptable::WANT_INNER_OBJECT)))
         mCanBeSlim = JS_TRUE;
 }
 
 /***************************************************************************/
 /***************************************************************************/
@@ -1783,20 +1801,22 @@ XPC_WN_GetterSetter(JSContext *cx, JSObj
 }
 
 /***************************************************************************/
 
 static JSBool
 XPC_WN_Shared_Proto_Enumerate(JSContext *cx, JSObject *obj)
 {
     NS_ASSERTION(
-        obj->getClass() == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
-        obj->getClass() == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass ||
-        obj->getClass() == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
-        obj->getClass() == &XPC_WN_NoMods_NoCall_Proto_JSClass,
+        JS_InstanceOf(cx, obj, &XPC_WN_ModsAllowed_WithCall_Proto_JSClass,
+                      nsnull) ||
+        JS_InstanceOf(cx, obj, &XPC_WN_ModsAllowed_NoCall_Proto_JSClass,
+                      nsnull) ||
+        JS_InstanceOf(cx, obj, &XPC_WN_NoMods_WithCall_Proto_JSClass, nsnull) ||
+        JS_InstanceOf(cx, obj, &XPC_WN_NoMods_NoCall_Proto_JSClass, nsnull),
         "bad proto");
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if(!self)
         return JS_FALSE;
 
     if(self->GetScriptableInfo() &&
        self->GetScriptableInfo()->GetFlags().DontEnumStaticProps())
@@ -1854,19 +1874,21 @@ XPC_WN_Shared_Proto_Trace(JSTracer *trc,
 }
 
 /*****************************************************/
 
 static JSBool
 XPC_WN_ModsAllowed_Proto_Resolve(JSContext *cx, JSObject *obj, jsid id)
 {
     NS_ASSERTION(
-        obj->getClass() == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
-        obj->getClass() == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass,
-        "bad proto");
+        JS_InstanceOf(cx, obj, &XPC_WN_ModsAllowed_WithCall_Proto_JSClass,
+                      nsnull) ||
+        JS_InstanceOf(cx, obj, &XPC_WN_ModsAllowed_NoCall_Proto_JSClass,
+                      nsnull),
+                 "bad proto");
 
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if(!self)
         return JS_FALSE;
 
     XPCCallContext ccx(JS_CALLER, cx);
     if(!ccx.IsValid())
@@ -1878,77 +1900,103 @@ XPC_WN_ModsAllowed_Proto_Resolve(JSConte
 
     return DefinePropertyIfFound(ccx, obj, id,
                                  self->GetSet(), nsnull, nsnull,
                                  self->GetScope(),
                                  JS_TRUE, nsnull, nsnull, si,
                                  enumFlag, nsnull);
 }
 
-js::Class XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
+// Give our proto classes object ops that match the respective
+// wrappers so that the JS engine can share scope (maps) among
+// wrappers. This essentially duplicates the number of JSClasses we
+// use for prototype objects (from 2 to 4), but the scope sharing
+// benefit is well worth it.
+JSObjectOps *
+XPC_WN_Proto_GetObjectOps(JSContext *cx, JSClass *clazz)
+{
+    // Protos for wrappers that want calls to their call() hooks get
+    // jsops with a call hook, others get jsops w/o a call hook.
+
+    if(clazz == &XPC_WN_ModsAllowed_WithCall_Proto_JSClass ||
+       clazz == &XPC_WN_NoMods_WithCall_Proto_JSClass)
+        return &XPC_WN_WithCall_JSOps;
+
+    NS_ASSERTION(clazz == &XPC_WN_ModsAllowed_NoCall_Proto_JSClass ||
+                 clazz == &XPC_WN_NoMods_NoCall_Proto_JSClass ||
+                 clazz == &XPC_WN_NoHelper_Proto_JSClass,
+                 "bad proto");
+
+    return &XPC_WN_NoCall_JSOps;
+}
+
+JSClass XPC_WN_ModsAllowed_WithCall_Proto_JSClass = {
     "XPC_WN_ModsAllowed_WithCall_Proto_JSClass", // name;
     WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags;
 
     /* Mandatory non-null function pointer members. */
-    js::PropertyStub,               // addProperty;
-    js::PropertyStub,               // delProperty;
-    js::PropertyStub,               // getProperty;
-    js::PropertyStub,               // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,  // enumerate;
-    XPC_WN_ModsAllowed_Proto_Resolve, // resolve;
-    js::Valueify(XPC_WN_Shared_Proto_Convert), // convert;
-    XPC_WN_Shared_Proto_Finalize,   // finalize;
+    JS_PropertyStub,                // addProperty;
+    JS_PropertyStub,                // delProperty;
+    JS_PropertyStub,                // getProperty;
+    JS_PropertyStub,                // setProperty;
+    XPC_WN_Shared_Proto_Enumerate,         // enumerate;
+    XPC_WN_ModsAllowed_Proto_Resolve,      // resolve;
+    XPC_WN_Shared_Proto_Convert,           // convert;
+    XPC_WN_Shared_Proto_Finalize,          // finalize;
 
     /* Optionally non-null members start here. */
-    nsnull,                         // reserved0;
+    XPC_WN_Proto_GetObjectOps,      // getObjectOps;
     nsnull,                         // checkAccess;
     nsnull,                         // call;
     nsnull,                         // construct;
     nsnull,                         // xdrObject;
     nsnull,                         // hasInstance;
     JS_CLASS_TRACE(XPC_WN_Shared_Proto_Trace), // mark/trace;
-
-    JS_NULL_CLASS_EXT,
-    XPC_WN_WithCall_ObjectOps
+    nsnull                          // spare;
 };
 
-js::Class XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
+JSObjectOps *
+XPC_WN_ModsAllowedProto_NoCall_GetObjectOps(JSContext *cx, JSClass *clazz)
+{
+    return &XPC_WN_NoCall_JSOps;
+}
+
+JSClass XPC_WN_ModsAllowed_NoCall_Proto_JSClass = {
     "XPC_WN_ModsAllowed_NoCall_Proto_JSClass", // name;
     WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags;
 
     /* Mandatory non-null function pointer members. */
-    js::PropertyStub,               // addProperty;
-    js::PropertyStub,               // delProperty;
-    js::PropertyStub,               // getProperty;
-    js::PropertyStub,               // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,  // enumerate;
-    XPC_WN_ModsAllowed_Proto_Resolve,// resolve;
-    js::Valueify(XPC_WN_Shared_Proto_Convert), // convert;
-    XPC_WN_Shared_Proto_Finalize,    // finalize;
+    JS_PropertyStub,                // addProperty;
+    JS_PropertyStub,                // delProperty;
+    JS_PropertyStub,                // getProperty;
+    JS_PropertyStub,                // setProperty;
+    XPC_WN_Shared_Proto_Enumerate,         // enumerate;
+    XPC_WN_ModsAllowed_Proto_Resolve,      // resolve;
+    XPC_WN_Shared_Proto_Convert,           // convert;
+    XPC_WN_Shared_Proto_Finalize,          // finalize;
 
     /* Optionally non-null members start here. */
-    nsnull,                         // reserved0;
+    XPC_WN_Proto_GetObjectOps,      // getObjectOps;
     nsnull,                         // checkAccess;
     nsnull,                         // call;
     nsnull,                         // construct;
     nsnull,                         // xdrObject;
     nsnull,                         // hasInstance;
     JS_CLASS_TRACE(XPC_WN_Shared_Proto_Trace), // mark/trace;
-
-    JS_NULL_CLASS_EXT,
-    XPC_WN_NoCall_ObjectOps
+    nsnull                          // spare;
 };
 
 /***************************************************************************/
 
 static JSBool
 XPC_WN_OnlyIWrite_Proto_PropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
-    NS_ASSERTION(obj->getClass() == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
-                 obj->getClass() == &XPC_WN_NoMods_NoCall_Proto_JSClass,
+    NS_ASSERTION(
+        JS_InstanceOf(cx, obj, &XPC_WN_NoMods_WithCall_Proto_JSClass, nsnull) ||
+        JS_InstanceOf(cx, obj, &XPC_WN_NoMods_NoCall_Proto_JSClass, nsnull),
                  "bad proto");
 
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if(!self)
         return JS_FALSE;
 
     XPCCallContext ccx(JS_CALLER, cx);
@@ -1960,18 +2008,19 @@ XPC_WN_OnlyIWrite_Proto_PropertyStub(JSC
         return JS_TRUE;
 
     return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
 }
 
 static JSBool
 XPC_WN_NoMods_Proto_Resolve(JSContext *cx, JSObject *obj, jsid id)
 {
-    NS_ASSERTION(obj->getClass() == &XPC_WN_NoMods_WithCall_Proto_JSClass ||
-                 obj->getClass() == &XPC_WN_NoMods_NoCall_Proto_JSClass,
+    NS_ASSERTION(
+        JS_InstanceOf(cx, obj, &XPC_WN_NoMods_WithCall_Proto_JSClass, nsnull) ||
+        JS_InstanceOf(cx, obj, &XPC_WN_NoMods_NoCall_Proto_JSClass, nsnull),
                  "bad proto");
 
     XPCWrappedNativeProto* self =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if(!self)
         return JS_FALSE;
 
     XPCCallContext ccx(JS_CALLER, cx);
@@ -1986,68 +2035,64 @@ XPC_WN_NoMods_Proto_Resolve(JSContext *c
                                  self->GetSet(), nsnull, nsnull,
                                  self->GetScope(),
                                  JS_TRUE, nsnull, nsnull, si,
                                  JSPROP_READONLY |
                                  JSPROP_PERMANENT |
                                  enumFlag, nsnull);
 }
 
-js::Class XPC_WN_NoMods_WithCall_Proto_JSClass = {
+JSClass XPC_WN_NoMods_WithCall_Proto_JSClass = {
     "XPC_WN_NoMods_WithCall_Proto_JSClass",      // name;
     WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags;
 
     /* Mandatory non-null function pointer members. */
-    js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // addProperty;
-    js::Valueify(XPC_WN_CannotModifyPropertyStub),      // delProperty;
-    js::PropertyStub,                                   // getProperty;
-    js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,                      // enumerate;
-    XPC_WN_NoMods_Proto_Resolve,                        // resolve;
-    js::Valueify(XPC_WN_Shared_Proto_Convert),          // convert;
-    XPC_WN_Shared_Proto_Finalize,                       // finalize;
+    XPC_WN_OnlyIWrite_Proto_PropertyStub,  // addProperty;
+    XPC_WN_CannotModifyPropertyStub,       // delProperty;
+    JS_PropertyStub,                       // getProperty;
+    XPC_WN_OnlyIWrite_Proto_PropertyStub,  // setProperty;
+    XPC_WN_Shared_Proto_Enumerate,         // enumerate;
+    XPC_WN_NoMods_Proto_Resolve,           // resolve;
+    XPC_WN_Shared_Proto_Convert,           // convert;
+    XPC_WN_Shared_Proto_Finalize,          // finalize;
 
     /* Optionally non-null members start here. */
-    nsnull,                         // reserved0;
+    XPC_WN_Proto_GetObjectOps,      // getObjectOps;
     nsnull,                         // checkAccess;
     nsnull,                         // call;
     nsnull,                         // construct;
     nsnull,                         // xdrObject;
     nsnull,                         // hasInstance;
     JS_CLASS_TRACE(XPC_WN_Shared_Proto_Trace), // mark/trace;
-
-    JS_NULL_CLASS_EXT,
-    XPC_WN_WithCall_ObjectOps
+    nsnull                          // spare;
 };
 
-js::Class XPC_WN_NoMods_NoCall_Proto_JSClass = {
-    "XPC_WN_NoMods_NoCall_Proto_JSClass",               // name;
-    WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE,              // flags;
+JSClass XPC_WN_NoMods_NoCall_Proto_JSClass = {
+    "XPC_WN_NoMods_NoCall_Proto_JSClass",      // name;
+    WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags;
 
     /* Mandatory non-null function pointer members. */
-    js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // addProperty;
-    js::Valueify(XPC_WN_CannotModifyPropertyStub),      // delProperty;
-    js::PropertyStub,                                   // getProperty;
-    js::Valueify(XPC_WN_OnlyIWrite_Proto_PropertyStub), // setProperty;
-    XPC_WN_Shared_Proto_Enumerate,                      // enumerate;
-    XPC_WN_NoMods_Proto_Resolve,                        // resolve;
-    js::Valueify(XPC_WN_Shared_Proto_Convert),          // convert;
-    XPC_WN_Shared_Proto_Finalize,                       // finalize;
+    XPC_WN_OnlyIWrite_Proto_PropertyStub,  // addProperty;
+    XPC_WN_CannotModifyPropertyStub,       // delProperty;
+    JS_PropertyStub,                       // getProperty;
+    XPC_WN_OnlyIWrite_Proto_PropertyStub,  // setProperty;
+    XPC_WN_Shared_Proto_Enumerate,         // enumerate;
+    XPC_WN_NoMods_Proto_Resolve,           // resolve;
+    XPC_WN_Shared_Proto_Convert,           // convert;
+    XPC_WN_Shared_Proto_Finalize,          // finalize;
 
     /* Optionally non-null members start here. */
-    nsnull,                         // reserved0;
+    XPC_WN_Proto_GetObjectOps,      // getObjectOps;
     nsnull,                         // checkAccess;
     nsnull,                         // call;
     nsnull,                         // construct;
     nsnull,                         // xdrObject;
     nsnull,                         // hasInstance;
     JS_CLASS_TRACE(XPC_WN_Shared_Proto_Trace), // mark/trace;
-
-    JS_NULL_CLASS_EXT,
-    XPC_WN_NoCall_ObjectOps
+    nsnull                          // spare;
 };
 
 /***************************************************************************/
 
 static JSBool
 XPC_WN_TearOff_Enumerate(JSContext *cx, JSObject *obj)
 {
     MORPH_SLIM_WRAPPER(cx, obj);
@@ -2098,21 +2143,32 @@ XPC_WN_TearOff_Finalize(JSContext *cx, J
 {
     XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
         xpc_GetJSPrivate(obj);
     if(!p)
         return;
     p->JSObjectFinalized();
 }
 
-js::Class XPC_WN_Tearoff_JSClass = {
-    "WrappedNative_TearOff",                        // name;
-    WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE,          // flags;
+JSClass XPC_WN_Tearoff_JSClass = {
+    "WrappedNative_TearOff",            // name;
+    WRAPPER_SLOTS | JSCLASS_MARK_IS_TRACE, // flags;
 
-    js::Valueify(XPC_WN_OnlyIWrite_PropertyStub),   // addProperty;
-    js::Valueify(XPC_WN_CannotModifyPropertyStub),  // delProperty;
-    js::PropertyStub,                               // getProperty;
-    js::Valueify(XPC_WN_OnlyIWrite_PropertyStub),   // setProperty;
-    XPC_WN_TearOff_Enumerate,                       // enumerate;
-    XPC_WN_TearOff_Resolve,                         // resolve;
-    js::Valueify(XPC_WN_Shared_Convert),            // convert;
-    XPC_WN_TearOff_Finalize                        // finalize;
+    /* Mandatory non-null function pointer members. */
+    XPC_WN_OnlyIWrite_PropertyStub,     // addProperty;
+    XPC_WN_CannotModifyPropertyStub,    // delProperty;
+    JS_PropertyStub,                    // getProperty;
+    XPC_WN_OnlyIWrite_PropertyStub,     // setProperty;
+    XPC_WN_TearOff_Enumerate,           // enumerate;
+    XPC_WN_TearOff_Resolve,             // resolve;
+    XPC_WN_Shared_Convert,              // convert;
+    XPC_WN_TearOff_Finalize,            // finalize;
+
+    /* Optionally non-null members start here. */
+    nsnull,                         // getObjectOps;
+    nsnull,                         // checkAccess;
+    nsnull,                         // call;
+    nsnull,                         // construct;
+    nsnull,                         // xdrObject;
+    nsnull,                         // hasInstance;
+    nsnull,                         // mark/trace;
+    nsnull                          // spare;
 };
--- a/js/src/xpconnect/src/xpcwrappednativeproto.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativeproto.cpp
@@ -99,17 +99,17 @@ XPCWrappedNativeProto::Init(
     if(callback)
     {
         mScriptableInfo =
             XPCNativeScriptableInfo::Construct(ccx, isGlobal, scriptableCreateInfo);
         if(!mScriptableInfo)
             return JS_FALSE;
     }
 
-    js::Class* jsclazz;
+    JSClass* jsclazz;
 
 
     if(mScriptableInfo)
     {
         const XPCNativeScriptableFlags& flags(mScriptableInfo->GetFlags());
 
         if(flags.AllowPropModsToPrototype())
         {
@@ -127,17 +127,17 @@ XPCWrappedNativeProto::Init(
     else
     {
         jsclazz = &XPC_WN_NoMods_NoCall_Proto_JSClass;
     }
 
     JSObject *parent = mScope->GetGlobalJSObject();
 
     mJSProtoObject =
-        xpc_NewSystemInheritingJSObject(ccx, js::Jsvalify(jsclazz),
+        xpc_NewSystemInheritingJSObject(ccx, jsclazz,
                                         mScope->GetPrototypeJSObject(),
                                         parent);
 
     JSBool ok = mJSProtoObject && JS_SetPrivate(ccx, mJSProtoObject, this);
 
     if(ok && callback)
     {
         nsresult rv = callback->PostCreatePrototype(ccx, mJSProtoObject);
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -199,41 +199,39 @@ XPCWrappedNativeScope::SetComponents(nsX
 // scopes. By doing this we avoid allocating a new scope for every
 // wrapper on creation of the wrapper, and most wrappers won't need
 // their own scope at all for the lifetime of the wrapper.
 // WRAPPER_SLOTS is key here (even though there's never anything
 // in the private data slot in these prototypes), as the number of
 // reserved slots in this class needs to match that of the wrappers
 // for the JS engine to share scopes.
 
-js::Class XPC_WN_NoHelper_Proto_JSClass = {
+JSClass XPC_WN_NoHelper_Proto_JSClass = {
     "XPC_WN_NoHelper_Proto_JSClass",// name;
     WRAPPER_SLOTS,                  // flags;
 
     /* Mandatory non-null function pointer members. */
-    js::PropertyStub,               // addProperty;
-    js::PropertyStub,               // delProperty;
-    js::PropertyStub,               // getProperty;
-    js::PropertyStub,               // setProperty;
-    js::EnumerateStub,              // enumerate;
+    JS_PropertyStub,                // addProperty;
+    JS_PropertyStub,                // delProperty;
+    JS_PropertyStub,                // getProperty;
+    JS_PropertyStub,                // setProperty;
+    JS_EnumerateStub,               // enumerate;
     JS_ResolveStub,                 // resolve;
-    js::ConvertStub,                // convert;
+    JS_ConvertStub,                 // convert;
     nsnull,                         // finalize;
 
     /* Optionally non-null members start here. */
-    nsnull,                         // reserved0;
+    XPC_WN_Proto_GetObjectOps,      // getObjectOps;
     nsnull,                         // checkAccess;
     nsnull,                         // call;
     nsnull,                         // construct;
     nsnull,                         // xdrObject;
     nsnull,                         // hasInstance;
     nsnull,                         // mark/trace;
-
-    JS_NULL_CLASS_EXT,
-    XPC_WN_NoCall_ObjectOps
+    nsnull                          // spare;
 };
 
 
 void
 XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal)
 {
     // We allow for calling this more than once. This feature is used by
     // nsXPConnect::InitClassesWithNewWrappedGlobal.
@@ -347,18 +345,17 @@ JSObject *
 XPCWrappedNativeScope::GetPrototypeNoHelper(XPCCallContext& ccx)
 {
     // We could create this prototype in SetGlobal(), but all scopes
     // don't need one, so we save ourselves a bit of space if we
     // create these when they're needed.
     if(!mPrototypeNoHelper)
     {
         mPrototypeNoHelper =
-            xpc_NewSystemInheritingJSObject(ccx,
-                                            js::Jsvalify(&XPC_WN_NoHelper_Proto_JSClass),
+            xpc_NewSystemInheritingJSObject(ccx, &XPC_WN_NoHelper_Proto_JSClass,
                                             mPrototypeJSObject,
                                             mGlobalJSObject);
 
         NS_ASSERTION(mPrototypeNoHelper,
                      "Failed to create prototype for wrappers w/o a helper");
     }
 
     return mPrototypeNoHelper;
@@ -723,17 +720,17 @@ XPCWrappedNativeScope::SystemIsBeingShut
 
 /***************************************************************************/
 
 static
 XPCWrappedNativeScope*
 GetScopeOfObject(JSObject* obj)
 {
     nsISupports* supports;
-    js::Class* clazz = obj->getClass();
+    JSClass* clazz = obj->getJSClass();
     JSBool isWrapper = IS_WRAPPER_CLASS(clazz);
 
     if(isWrapper && IS_SLIM_WRAPPER_OBJECT(obj))
         return GetSlimWrapperProto(obj)->GetScope();
 
     if(!isWrapper || !(supports = (nsISupports*) xpc_GetJSPrivate(obj)))
     {
 #ifdef DEBUG