Merge tracemonkey to mozilla-central.
authorRobert Sayre <sayrer@gmail.com>
Tue, 04 May 2010 13:27:55 -0400
changeset 41885 13bcf4386e12bc714a4ecbafec588e95285e1d72
parent 41772 e26e8284484b477f25186f30fc4ad8884031fbd0 (current diff)
parent 41884 97f86119e3eebec033d3c700f358c3a611657c09 (diff)
child 41886 cbb8d8caed8a3d9e9581f37bf679f18bebf923c2
push idunknown
push userunknown
push dateunknown
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Merge tracemonkey to mozilla-central.
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -6854,19 +6854,19 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       //
       // We don't need to worry about property attributes here as we
       // know here we're dealing with an undefined property set, so
       // we're not declaring readonly or permanent properties.
       //
       // Since we always create the undeclared property here without given a
       // chance for the interpreter to report applicable strict mode warnings,
       // we must take care to check those warnings here.
-
       JSString *str = JSVAL_TO_STRING(id);
-      if (!::js_CheckUndeclaredVarAssignment(cx) ||
+      if ((!(flags & JSRESOLVE_QUALIFIED) &&
+           !js_CheckUndeclaredVarAssignment(cx, id)) ||
           !::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str),
                                  ::JS_GetStringLength(str), JSVAL_VOID,
                                  JS_PropertyStub, JS_PropertyStub,
                                  JSPROP_ENUMERATE)) {
         *_retval = JS_FALSE;
 
         return NS_OK;
       }
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.c
@@ -710,19 +710,19 @@ static JSBool
     }
     JSD_UNLOCK_SCRIPTS(jsdc);
     return JS_FALSE;
 }
 
 
 JSTrapStatus
 jsd_TrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
-                void *closure)
+                jsval closure)
 {
-    JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(((jsval)closure));
+    JSDExecHook* jsdhook = (JSDExecHook*) JSVAL_TO_PRIVATE(closure);
     JSD_ExecutionHookProc hook;
     void* hookData;
     JSDContext*  jsdc;
     JSDScript* jsdscript;
 
     JSD_LOCK();
 
     if( NULL == (jsdc = jsd_JSDContextForJSContext(cx)) ||
@@ -794,18 +794,18 @@ jsd_SetExecutionHook(JSDContext*        
         return JS_FALSE;
     }
     jsdhook->jsdscript  = jsdscript;
     jsdhook->pc         = pc;
     jsdhook->hook       = hook;
     jsdhook->callerdata = callerdata;
 
     if( ! JS_SetTrap(jsdc->dumbContext, jsdscript->script, 
-                     (jsbytecode*)pc, jsd_TrapHandler, 
-                     (void*) PRIVATE_TO_JSVAL(jsdhook)) )
+                     (jsbytecode*)pc, jsd_TrapHandler,
+                     PRIVATE_TO_JSVAL(jsdhook)) )
     {
         free(jsdhook);
         JSD_UNLOCK();
         return JS_FALSE;
     }
 
     JS_APPEND_LINK(&jsdhook->links, &jsdscript->hooks);
     JSD_UNLOCK();
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -91,17 +91,17 @@ namespace CType {
 
   static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsval idval,
     jsval* vp);
   static JSBool NameGetter(JSContext* cx, JSObject* obj, jsval idval,
     jsval* vp);
   static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsval idval,
     jsval* vp);
   static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp);
-  static JSBool Array(JSContext* cx, uintN argc, jsval* vp);
+  static JSBool CreateArray(JSContext* cx, uintN argc, jsval* vp);
   static JSBool ToString(JSContext* cx, uintN argc, jsval* vp);
   static JSBool ToSource(JSContext* cx, uintN argc, jsval* vp);
   static JSBool HasInstance(JSContext* cx, JSObject* obj, jsval v, JSBool* bp);
 }
 
 namespace PointerType {
   static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
   static JSBool ConstructData(JSContext* cx, JSObject* obj, uintN argc,
@@ -137,16 +137,17 @@ namespace StructType {
 
   static JSBool FieldsArrayGetter(JSContext* cx, JSObject* obj, jsval idval,
     jsval* vp);
   static JSBool FieldGetter(JSContext* cx, JSObject* obj, jsval idval,
     jsval* vp);
   static JSBool FieldSetter(JSContext* cx, JSObject* obj, jsval idval,
     jsval* vp);
   static JSBool AddressOfField(JSContext* cx, uintN argc, jsval* vp);
+  static JSBool Define(JSContext* cx, uintN argc, jsval* vp);
 }
 
 namespace FunctionType {
   static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
   static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
     JSObject* dataObj, JSObject* fnObj, JSObject* thisObj);
 
   static JSBool Call(JSContext* cx, JSObject* obj, uintN argc, jsval* argv,
@@ -295,17 +296,17 @@ static JSPropertySpec sCTypeProps[] = {
   { "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
   { "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
   { "ptr", 0, CTYPESPROP_FLAGS, CType::PtrGetter, NULL },
   { "prototype", 0, CTYPESPROP_FLAGS, CType::PrototypeGetter, NULL },
   { 0, 0, 0, NULL, NULL }
 };
 
 static JSFunctionSpec sCTypeFunctions[] = {
-  JS_FN("array", CType::Array, 0, CTYPESFN_FLAGS),
+  JS_FN("array", CType::CreateArray, 0, CTYPESFN_FLAGS),
   JS_FN("toString", CType::ToString, 0, CTYPESFN_FLAGS),
   JS_FN("toSource", CType::ToSource, 0, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 static JSPropertySpec sCDataProps[] = {
   { "value", 0, JSPROP_SHARED | JSPROP_PERMANENT,
     CData::ValueGetter, CData::ValueSetter },
@@ -362,16 +363,21 @@ static JSPropertySpec sArrayInstanceProp
 static JSFunctionSpec sStructFunction =
   JS_FN("StructType", StructType::Create, 2, CTYPESFN_FLAGS);
 
 static JSPropertySpec sStructProps[] = {
   { "fields", 0, CTYPESPROP_FLAGS, StructType::FieldsArrayGetter, NULL },
   { 0, 0, 0, NULL, NULL }
 };
 
+static JSFunctionSpec sStructFunctions[] = {
+  JS_FN("define", StructType::Define, 1, CDATAFN_FLAGS),
+  JS_FS_END
+};
+
 static JSFunctionSpec sStructInstanceFunctions[] = {
   JS_FN("addressOfField", StructType::AddressOfField, 1, CDATAFN_FLAGS),
   JS_FS_END
 };
 
 static JSFunctionSpec sFunctionFunction =
   JS_FN("FunctionType", FunctionType::Create, 2, CTYPESFN_FLAGS);
 
@@ -623,16 +629,17 @@ DefineABIConstant(JSContext* cx,
 // Set up a single type constructor for
 // ctypes.{Pointer,Array,Struct,Function}Type.
 static JSBool
 InitTypeConstructor(JSContext* cx,
                     JSObject* parent,
                     JSObject* CTypeProto,
                     JSObject* CDataProto,
                     JSFunctionSpec spec,
+                    JSFunctionSpec* fns,
                     JSPropertySpec* props,
                     JSFunctionSpec* instanceFns,
                     JSPropertySpec* instanceProps,
                     JSObject*& typeProto,
                     JSObject*& dataProto)
 {
   JSFunction* fun = JS_DefineFunction(cx, parent, spec.name, spec.call, 
                       spec.nargs, spec.flags);
@@ -648,16 +655,19 @@ InitTypeConstructor(JSContext* cx,
   if (!typeProto)
     return false;
 
   // Define property before proceeding, for GC safety.
   if (!JS_DefineProperty(cx, obj, "prototype", OBJECT_TO_JSVAL(typeProto),
          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
+  if (fns && !JS_DefineFunctions(cx, typeProto, fns))
+    return false;
+
   if (!JS_DefineProperties(cx, typeProto, props))
     return false;
 
   if (!JS_DefineProperty(cx, typeProto, "constructor", OBJECT_TO_JSVAL(obj),
          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   // Stash ctypes.{Pointer,Array,Struct}Type.prototype on a reserved slot of
@@ -797,36 +807,38 @@ InitTypeClasses(JSContext* cx, JSObject*
   //   * A constructor which creates and returns a CData object, containing
   //     binary data of the given type.
   //   * 'prototype' property:
   //     * [[Class]] "CDataProto"
   //     * __proto__ === 'p', the prototype object from above
   //     * 'constructor' property === 't'
   JSObject* protos[CTYPEPROTO_SLOTS];
   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
-         sPointerFunction, sPointerProps, sPointerInstanceFunctions,
-         sPointerInstanceProps, protos[SLOT_POINTERPROTO],
-         protos[SLOT_POINTERDATAPROTO]))
+         sPointerFunction, NULL, sPointerProps,
+         sPointerInstanceFunctions, sPointerInstanceProps,
+         protos[SLOT_POINTERPROTO], protos[SLOT_POINTERDATAPROTO]))
     return false;
   js::AutoValueRooter proot(cx, protos[SLOT_POINTERDATAPROTO]);
 
   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
-         sArrayFunction, sArrayProps, sArrayInstanceFunctions, sArrayInstanceProps,
+         sArrayFunction, NULL, sArrayProps,
+         sArrayInstanceFunctions, sArrayInstanceProps,
          protos[SLOT_ARRAYPROTO], protos[SLOT_ARRAYDATAPROTO]))
     return false;
   js::AutoValueRooter aroot(cx, protos[SLOT_ARRAYDATAPROTO]);
 
   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
-         sStructFunction, sStructProps, sStructInstanceFunctions, NULL,
+         sStructFunction, sStructFunctions, sStructProps,
+         sStructInstanceFunctions, NULL,
          protos[SLOT_STRUCTPROTO], protos[SLOT_STRUCTDATAPROTO]))
     return false;
   js::AutoValueRooter sroot(cx, protos[SLOT_STRUCTDATAPROTO]);
 
   if (!InitTypeConstructor(cx, parent, CTypeProto, CDataProto,
-         sFunctionFunction, sFunctionProps, NULL, NULL,
+         sFunctionFunction, NULL, sFunctionProps, NULL, NULL,
          protos[SLOT_FUNCTIONPROTO], protos[SLOT_FUNCTIONDATAPROTO]))
     return false;
   js::AutoValueRooter froot(cx, protos[SLOT_FUNCTIONDATAPROTO]);
 
   protos[SLOT_CDATAPROTO] = CDataProto;
 
   // Create and attach the ctypes.{Int64,UInt64} constructors.
   // Each of these has, respectively:
@@ -888,17 +900,17 @@ InitTypeClasses(JSContext* cx, JSObject*
 
   // Create objects representing the special types void_t and voidptr_t.
   JSObject* typeObj =
     CType::DefineBuiltin(cx, parent, "void_t", CTypeProto, CDataProto, "void",
       TYPE_void_t, JSVAL_VOID, JSVAL_VOID, &ffi_type_void);
   if (!typeObj)
     return false;
 
-  typeObj = PointerType::CreateInternal(cx, NULL, typeObj, NULL);
+  typeObj = PointerType::CreateInternal(cx, typeObj);
   if (!typeObj)
     return false;
   if (!JS_DefineProperty(cx, parent, "voidptr_t", OBJECT_TO_JSVAL(typeObj),
          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   return true;
 }
@@ -1686,47 +1698,45 @@ ImplicitConvert(JSContext* cx,
     } else if (!jsvalToInteger(cx, val, &result)) {                            \
       return TypeError(cx, #name, val);                                        \
     }                                                                          \
     *static_cast<type*>(buffer) = result;                                      \
     break;                                                                     \
   }
 #include "typedefs.h"
   case TYPE_pointer: {
-    JSObject* baseType = PointerType::GetBaseType(cx, targetType);
-
     if (JSVAL_IS_NULL(val)) {
       // Convert to a null pointer.
       *static_cast<void**>(buffer) = NULL;
       break;
     }
 
+    JSObject* baseType = PointerType::GetBaseType(cx, targetType);
     if (sourceData) {
       // First, determine if the targetType is ctypes.void_t.ptr.
       TypeCode sourceCode = CType::GetTypeCode(cx, sourceType);
       void* sourceBuffer = CData::GetData(cx, sourceData);
-      bool voidptrTarget = baseType &&
-                           CType::GetTypeCode(cx, baseType) == TYPE_void_t;
+      bool voidptrTarget = CType::GetTypeCode(cx, baseType) == TYPE_void_t;
 
       if (sourceCode == TYPE_pointer && voidptrTarget) {
         // Autoconvert if targetType is ctypes.voidptr_t.
         *static_cast<void**>(buffer) = *static_cast<void**>(sourceBuffer);
         break;
       }
       if (sourceCode == TYPE_array) {
         // Autoconvert an array to a ctypes.void_t.ptr or to
         // sourceType.elementType.ptr, just like C.
         JSObject* elementType = ArrayType::GetBaseType(cx, sourceType);
         if (voidptrTarget || CType::TypesEqual(cx, baseType, elementType)) {
           *static_cast<void**>(buffer) = sourceBuffer;
           break;
         }
       }
 
-    } else if (isArgument && baseType && JSVAL_IS_STRING(val)) {
+    } else if (isArgument && JSVAL_IS_STRING(val)) {
       // Convert the string for the ffi call. This requires allocating space
       // which the caller assumes ownership of.
       // TODO: Extend this so we can safely convert strings at other times also.
       JSString* sourceString = JSVAL_TO_STRING(val);
       const jschar* sourceChars = sourceString->chars();
       size_t sourceLength = sourceString->length();
 
       switch (CType::GetTypeCode(cx, baseType)) {
@@ -1894,17 +1904,17 @@ ImplicitConvert(JSContext* cx,
         js::AutoValueRooter fieldVal(cx);
         if (!JS_IdToValue(cx, id, fieldVal.addr()))
           return false;
         if (!JSVAL_IS_STRING(fieldVal.value())) {
           JS_ReportError(cx, "property name is not a string");
           return false;
         }
 
-        FieldInfo* field = StructType::LookupField(cx, targetType,
+        const FieldInfo* field = StructType::LookupField(cx, targetType,
           fieldVal.value());
         if (!field)
           return false;
 
         JSString* name = JSVAL_TO_STRING(fieldVal.value());
         js::AutoValueRooter prop(cx);
         if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), prop.addr()))
           return false;
@@ -1912,18 +1922,18 @@ ImplicitConvert(JSContext* cx,
         // Convert the field via ImplicitConvert().
         char* fieldData = intermediate.get() + field->mOffset;
         if (!ImplicitConvert(cx, prop.value(), field->mType, fieldData, false, NULL))
           return false;
 
         ++i;
       }
 
-      Array<FieldInfo>* fields = StructType::GetFieldInfo(cx, targetType);
-      if (i != fields->length()) {
+      const FieldInfoHash* fields = StructType::GetFieldInfo(cx, targetType);
+      if (i != fields->count()) {
         JS_ReportError(cx, "missing fields");
         return false;
       }
 
       memcpy(buffer, intermediate.get(), structSize);
       break;
     }
 
@@ -2020,57 +2030,49 @@ BuildTypeName(JSContext* cx, JSObject* t
   AutoString result;
 
   // Walk the hierarchy of types, outermost to innermost, building up the type
   // string. This consists of the base type, which goes on the left.
   // Derived type modifiers (* and []) build from the inside outward, with
   // pointers on the left and arrays on the right. An excellent description
   // of the rules for building C type declarations can be found at:
   // http://unixwiz.net/techtips/reading-cdecl.html
-  JSObject* currentType = typeObj;
-  JSObject* nextType;
-  TypeCode prevGrouping = CType::GetTypeCode(cx, currentType), currentGrouping;
+  TypeCode prevGrouping = CType::GetTypeCode(cx, typeObj), currentGrouping;
   while (1) {
-    currentGrouping = CType::GetTypeCode(cx, currentType);
+    currentGrouping = CType::GetTypeCode(cx, typeObj);
     switch (currentGrouping) {
     case TYPE_pointer: {
-      nextType = PointerType::GetBaseType(cx, currentType);
-      if (!nextType) {
-        // Opaque pointer type. Use the type's name as the base type.
-        break;
-      }
-
       // Pointer types go on the left.
       PrependString(result, "*");
 
-      currentType = nextType;
+      typeObj = PointerType::GetBaseType(cx, typeObj);
       prevGrouping = currentGrouping;
       continue;
     }
     case TYPE_array: {
       if (prevGrouping == TYPE_pointer) {
         // Outer type is pointer, inner type is array. Grouping is required.
         PrependString(result, "(");
         AppendString(result, ")");
       } 
 
       // Array types go on the right.
       AppendString(result, "[");
       size_t length;
-      if (ArrayType::GetSafeLength(cx, currentType, &length))
+      if (ArrayType::GetSafeLength(cx, typeObj, &length))
         IntegerToString(length, 10, result);
 
       AppendString(result, "]");
 
-      currentType = ArrayType::GetBaseType(cx, currentType);
+      typeObj = ArrayType::GetBaseType(cx, typeObj);
       prevGrouping = currentGrouping;
       continue;
     }
     case TYPE_function: {
-      FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, currentType);
+      FunctionInfo* fninfo = FunctionType::GetFunctionInfo(cx, typeObj);
 
       // Add in the calling convention, if it's not cdecl.
       if (GetABICode(cx, fninfo->mABI) == ABI_STDCALL)
         PrependString(result, "__stdcall ");
 
       // Wrap the entire expression so far with parens.
       PrependString(result, "(");
       AppendString(result, ")");
@@ -2083,31 +2085,31 @@ BuildTypeName(JSContext* cx, JSObject* t
         if (i != fninfo->mArgTypes.length() - 1 ||
             fninfo->mIsVariadic)
           AppendString(result, ", ");
       }
       if (fninfo->mIsVariadic)
         AppendString(result, "...");
       AppendString(result, ")");
 
-      // Set 'currentType' to the return type, and let the loop process it.
+      // Set 'typeObj' to the return type, and let the loop process it.
       // 'prevGrouping' doesn't matter here, because functions cannot return
       // arrays -- thus the parenthetical rules don't get tickled.
-      currentType = fninfo->mReturnType;
+      typeObj = fninfo->mReturnType;
       continue;
     }
     default:
       // Either a basic or struct type. Use the type's name as the base type.
       break;
     }
     break;
   }
 
   // Stick the base type and derived type parts together.
-  JSString* baseName = CType::GetName(cx, currentType);
+  JSString* baseName = CType::GetName(cx, typeObj);
   PrependString(result, baseName);
   return NewUCString(cx, result);
 }
 
 // Given a CType 'typeObj', generate a string 'result' such that 'eval(result)'
 // would construct the same CType. If 'makeShort' is true, assume that any
 // StructType 't' is bound to an in-scope variable of name 't.name', and use
 // that variable in place of generating a string to construct the type 't'.
@@ -2129,24 +2131,16 @@ BuildTypeSource(JSContext* cx,
   {
     AppendString(result, "ctypes.");
     JSString* nameStr = CType::GetName(cx, typeObj);
     AppendString(result, nameStr);
     break;
   }
   case TYPE_pointer: {
     JSObject* baseType = PointerType::GetBaseType(cx, typeObj);
-    if (!baseType) {
-      // Opaque pointer type. Use the type's name.
-      AppendString(result, "ctypes.PointerType(\"");
-      JSString* baseName = CType::GetName(cx, typeObj);
-      AppendString(result, baseName);
-      AppendString(result, "\")");
-      break;
-    }
 
     // Specialcase ctypes.voidptr_t.
     if (CType::GetTypeCode(cx, baseType) == TYPE_void_t) {
       AppendString(result, "ctypes.voidptr_t");
       break;
     }
 
     // Recursively build the source string, and append '.ptr'.
@@ -2214,27 +2208,43 @@ BuildTypeSource(JSContext* cx,
       // to an in-scope variable of name 't.name'.
       AppendString(result, name);
       break;
     }
 
     // Write the full struct declaration.
     AppendString(result, "ctypes.StructType(\"");
     AppendString(result, name);
-    AppendString(result, "\", [");
-
-    Array<FieldInfo>* fields = StructType::GetFieldInfo(cx, typeObj);
-    for (size_t i = 0; i < fields->length(); ++i) {
-      FieldInfo* field = fields->begin() + i;
+    AppendString(result, "\"");
+
+    // If it's an opaque struct, we're done.
+    if (!CType::IsSizeDefined(cx, typeObj)) {
+      AppendString(result, ")");
+      break;
+    }
+
+    AppendString(result, ", [");
+
+    const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj);
+    size_t length = fields->count();
+    Array<const FieldInfoHash::Entry*, 64> fieldsArray;
+    if (!fieldsArray.resize(length))
+      break;
+
+    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
+      fieldsArray[r.front().value.mIndex] = &r.front();
+
+    for (size_t i = 0; i < length; ++i) {
+      const FieldInfoHash::Entry* entry = fieldsArray[i];
       AppendString(result, "{ \"");
-      AppendString(result, field->mName);
+      AppendString(result, entry->key);
       AppendString(result, "\": ");
-      BuildTypeSource(cx, field->mType, true, result);
+      BuildTypeSource(cx, entry->value.mType, true, result);
       AppendString(result, " }");
-      if (i != fields->length() - 1)
+      if (i != length - 1)
         AppendString(result, ", ");
     }
 
     AppendString(result, "])");
     break;
   }
   }
 }
@@ -2359,31 +2369,39 @@ BuildDataSource(JSContext* cx,
       // The result must be able to ImplicitConvert successfully.
       // Serialize the data as an object with properties, rather than
       // a sequence of arguments to the StructType constructor.
       AppendString(result, "{");
     }
 
     // Serialize each field of the struct recursively. Each field must
     // be able to ImplicitConvert successfully.
-    Array<FieldInfo>* fields = StructType::GetFieldInfo(cx, typeObj);
-    for (size_t i = 0; i < fields->length(); ++i) {
-      FieldInfo* field = fields->begin() + i;
-      char* fieldData = static_cast<char*>(data) + field->mOffset;
+    const FieldInfoHash* fields = StructType::GetFieldInfo(cx, typeObj);
+    size_t length = fields->count();
+    Array<const FieldInfoHash::Entry*, 64> fieldsArray;
+    if (!fieldsArray.resize(length))
+      return false;
+
+    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront())
+      fieldsArray[r.front().value.mIndex] = &r.front();
+
+    for (size_t i = 0; i < length; ++i) {
+      const FieldInfoHash::Entry* entry = fieldsArray[i];
 
       if (isImplicit) {
         AppendString(result, "\"");
-        AppendString(result, field->mName);
+        AppendString(result, entry->key);
         AppendString(result, "\": ");
       }
 
-      if (!BuildDataSource(cx, field->mType, fieldData, true, result))
+      char* fieldData = static_cast<char*>(data) + entry->value.mOffset;
+      if (!BuildDataSource(cx, entry->value.mType, fieldData, true, result))
         return false;
 
-      if (i + 1 != fields->length())
+      if (i + 1 != length)
         AppendString(result, ", ");
     }
 
     if (isImplicit)
       AppendString(result, "}");
 
     break;
   }
@@ -2481,18 +2499,17 @@ CType::ConstructBasic(JSContext* cx,
 JSObject*
 CType::Create(JSContext* cx,
               JSObject* typeProto,
               JSObject* dataProto,
               TypeCode type,
               JSString* name,
               jsval size,
               jsval align,
-              ffi_type* ffiType,
-              PropertySpec* ps)
+              ffi_type* ffiType)
 {
   JSObject* parent = JS_GetParent(cx, typeProto);
   JS_ASSERT(parent);
 
   // Create a CType object with the properties and slots common to all CTypes.
   // Each type object 't' has:
   //   * [[Class]] "CType"
   //   * __proto__ === 'typeProto'; one of ctypes.{CType,PointerType,ArrayType,
@@ -2510,50 +2527,40 @@ CType::Create(JSContext* cx,
   //       specific type instance 't'.
   JSObject* typeObj = JS_NewObject(cx, &sCTypeClass, typeProto, parent);
   if (!typeObj)
     return NULL;
   js::AutoValueRooter root(cx, typeObj);
 
   // Set up the reserved slots.
   if (!JS_SetReservedSlot(cx, typeObj, SLOT_TYPECODE, INT_TO_JSVAL(type)) ||
-      !JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType)) ||
+      (ffiType && !JS_SetReservedSlot(cx, typeObj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(ffiType))) ||
       (name && !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(name))) ||
       !JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, size) ||
       !JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, align))
     return NULL;
 
-  // Set up the 'prototype' and 'prototype.constructor' properties.
-  JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, parent);
-  if (!prototype)
-    return NULL;
-  js::AutoValueRooter protoroot(cx, prototype);
-
-  if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
-         NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
-    return NULL;
-
-  // If required, define properties on the 'prototype' object. (These will
-  // become properties on CData objects created from this CType.)
-  if (ps) {
-    while (ps->name) {
-      if (!JS_DefineUCProperty(cx, prototype, ps->name, ps->namelen, JSVAL_VOID,
-             ps->getter, ps->setter, ps->flags))
-        return NULL;
-
-      ++ps;
-    }
-  }
-
-  // Set the 'prototype' object.
-  if (!JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
-    return NULL;
-
-  if (//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
-      !JS_SealObject(cx, typeObj, JS_FALSE))
+  if (dataProto) {
+    // Set up the 'prototype' and 'prototype.constructor' properties.
+    JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, parent);
+    if (!prototype)
+      return NULL;
+    js::AutoValueRooter protoroot(cx, prototype);
+
+    if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
+           NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
+      return NULL;
+
+    // Set the 'prototype' object.
+    if (//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
+        !JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
+      return NULL;
+  }
+
+  if (!JS_SealObject(cx, typeObj, JS_FALSE))
     return NULL;
 
   // Assert a sanity check on size and alignment: size % alignment should always
   // be zero.
   JS_ASSERT_IF(IsSizeDefined(cx, typeObj),
                GetSize(cx, typeObj) % GetAlignment(cx, typeObj) == 0);
 
   return typeObj;
@@ -2573,17 +2580,17 @@ CType::DefineBuiltin(JSContext* cx,
 {
   JSString* nameStr = JS_NewStringCopyZ(cx, name);
   if (!nameStr)
     return NULL;
   js::AutoValueRooter nameRoot(cx, nameStr);
 
   // Create a new CType object with the common properties and slots.
   JSObject* typeObj = Create(cx, typeProto, dataProto, type, nameStr, size,
-                        align, ffiType, NULL);
+                        align, ffiType);
   if (!typeObj)
     return NULL;
 
   // Define the CType as a 'propName' property on 'parent'.
   if (!JS_DefineProperty(cx, parent, propName, OBJECT_TO_JSVAL(typeObj),
          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return NULL;
 
@@ -2607,25 +2614,25 @@ CType::Finalize(JSContext* cx, JSObject*
       delete static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
     break;
   }
   case TYPE_struct:
     // Free the FieldInfo array.
     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
     if (!JSVAL_IS_VOID(slot)) {
       void* info = JSVAL_TO_PRIVATE(slot);
-      delete static_cast<js::ctypes::Array<FieldInfo>*>(info);
+      delete static_cast<FieldInfoHash*>(info);
     }
 
     // Fall through.
   case TYPE_array: {
     // Free the ffi_type info.
     jsval slot;
     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
-    if (!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot)) {
+    if (!JSVAL_IS_VOID(slot)) {
       ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
       delete[] ffiType->elements;
       delete ffiType;
     }
 
     break;
   }
   default:
@@ -2658,16 +2665,30 @@ CType::Trace(JSTracer* trc, JSObject* ob
 
   // Make sure our TypeCode slot is legit. If it's not, bail.
   jsval slot;
   if (!JS_GetReservedSlot(cx, obj, SLOT_TYPECODE, &slot) || JSVAL_IS_VOID(slot))
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(JSVAL_TO_INT(slot))) {
+  case TYPE_struct: {
+    ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
+    if (JSVAL_IS_VOID(slot))
+      return;
+
+    FieldInfoHash* fields =
+      static_cast<FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
+    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
+      JS_CALL_TRACER(trc, r.front().key, JSTRACE_STRING, "fieldName");
+      JS_CALL_TRACER(trc, r.front().value.mType, JSTRACE_OBJECT, "fieldType");
+    }
+
+    break;
+  }
   case TYPE_function: {
     // Check if we have a FunctionInfo.
     ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FNINFO, &slot));
     if (JSVAL_IS_VOID(slot))
       return;
 
     FunctionInfo* fninfo = static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot));
     JS_ASSERT(fninfo);
@@ -2715,28 +2736,19 @@ CType::TypesEqual(JSContext* cx, JSObjec
   TypeCode c1 = GetTypeCode(cx, t1);
   TypeCode c2 = GetTypeCode(cx, t2);
   if (c1 != c2)
     return false;
 
   // Determine whether the types require shallow or deep comparison.
   switch (c1) {
   case TYPE_pointer: {
+    // Compare base types.
     JSObject* b1 = PointerType::GetBaseType(cx, t1);
     JSObject* b2 = PointerType::GetBaseType(cx, t2);
-
-    if (!b1 || !b2) {
-      // One or both pointers are opaque.
-      // If both are opaque, compare names.
-      JSString* n1 = GetName(cx, t1);
-      JSString* n2 = GetName(cx, t2);
-      return b1 == b2 && JS_CompareStrings(n1, n2) == 0;
-    }
-
-    // Compare base types.
     return TypesEqual(cx, b1, b2);
   }
   case TYPE_function: {
     FunctionInfo* f1 = FunctionType::GetFunctionInfo(cx, t1);
     FunctionInfo* f2 = FunctionType::GetFunctionInfo(cx, t2);
 
     // Compare abi, return type, and argument types.
     if (f1->mABI != f2->mABI)
@@ -2848,28 +2860,57 @@ CType::GetAlignment(JSContext* cx, JSObj
 ffi_type*
 CType::GetFFIType(JSContext* cx, JSObject* obj)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
 
   jsval slot;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FFITYPE, &slot));
 
-  ffi_type* result = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
-  JS_ASSERT(result);
-  return result;
+  if (!JSVAL_IS_VOID(slot)) {
+    return static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
+  }
+
+  AutoPtr<ffi_type> result;
+  switch (CType::GetTypeCode(cx, obj)) {
+  case TYPE_array:
+    result = ArrayType::BuildFFIType(cx, obj);
+    break;
+
+  case TYPE_struct:
+    result = StructType::BuildFFIType(cx, obj);
+    break;
+
+  default:
+    JS_NOT_REACHED("simple types must have an ffi_type");
+  }
+
+  if (!result ||
+      !JS_SetReservedSlot(cx, obj, SLOT_FFITYPE, PRIVATE_TO_JSVAL(result.get())))
+    return NULL;
+
+  return result.forget();
 }
 
 JSString*
 CType::GetName(JSContext* cx, JSObject* obj)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
 
   jsval string;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_NAME, &string));
+  if (JSVAL_IS_VOID(string)) {
+    // Build the type name lazily.
+    JSString* name = BuildTypeName(cx, obj);
+    if (!name || !JS_SetReservedSlot(cx, obj, SLOT_NAME, STRING_TO_JSVAL(name)))
+      return NULL;
+
+    return name;
+  }
+
   return JSVAL_TO_STRING(string);
 }
 
 JSObject*
 CType::GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot)
 {
   // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
   // on the type constructor.
@@ -2905,30 +2946,33 @@ JSBool
 CType::PrototypeGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj)) {
     JS_ReportError(cx, "not a CType");
     return JS_FALSE;
   }
 
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_PROTO, vp));
-  JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
+  JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) || JSVAL_IS_VOID(*vp));
   return JS_TRUE;
 }
 
 JSBool
 CType::NameGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj)) {
     JS_ReportError(cx, "not a CType");
     return JS_FALSE;
   }
 
-  ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_NAME, vp));
-  JS_ASSERT(JSVAL_IS_STRING(*vp));
+  JSString* name = CType::GetName(cx, obj);
+  if (!name)
+    return JS_FALSE;
+
+  *vp = STRING_TO_JSVAL(name);
   return JS_TRUE;
 }
 
 JSBool
 CType::SizeGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj)) {
     JS_ReportError(cx, "not a CType");
@@ -2943,26 +2987,26 @@ CType::SizeGetter(JSContext* cx, JSObjec
 JSBool
 CType::PtrGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj)) {
     JS_ReportError(cx, "not a CType");
     return JS_FALSE;
   }
 
-  JSObject* pointerType = PointerType::CreateInternal(cx, NULL, obj, NULL);
+  JSObject* pointerType = PointerType::CreateInternal(cx, obj);
   if (!pointerType)
     return JS_FALSE;
 
   *vp = OBJECT_TO_JSVAL(pointerType);
   return JS_TRUE;
 }
 
 JSBool
-CType::Array(JSContext* cx, uintN argc, jsval *vp)
+CType::CreateArray(JSContext* cx, uintN argc, jsval *vp)
 {
   JSObject* baseType = JS_THIS_OBJECT(cx, vp);
   JS_ASSERT(baseType);
 
   if (!CType::IsCType(cx, baseType)) {
     JS_ReportError(cx, "not a CType");
     return JS_FALSE;
   }
@@ -3067,93 +3111,61 @@ PointerType::Create(JSContext* cx, uintN
 {
   // Construct and return a new PointerType object.
   if (argc != 1) {
     JS_ReportError(cx, "PointerType takes one argument");
     return JS_FALSE;
   }
 
   jsval arg = JS_ARGV(cx, vp)[0];
-  JSObject* baseType = NULL;
-  JSString* name = NULL;
-  if (!JSVAL_IS_PRIMITIVE(arg) &&
-      CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) {
-    baseType = JSVAL_TO_OBJECT(arg);
-
-  } else if (JSVAL_IS_STRING(arg)) {
-    // Construct an opaque pointer type from a string.
-    name = JSVAL_TO_STRING(arg);
-
-  } else {
-    JS_ReportError(cx, "first argument must be a CType or a string");
+  if (JSVAL_IS_PRIMITIVE(arg) || !CType::IsCType(cx, JSVAL_TO_OBJECT(arg))) {
+    JS_ReportError(cx, "first argument must be a CType");
     return JS_FALSE;
   }
 
-  JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
-  JSObject* result = CreateInternal(cx, callee, baseType, name);
+  JSObject* result = CreateInternal(cx, JSVAL_TO_OBJECT(arg));
   if (!result)
     return JS_FALSE;
 
   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 JSObject*
-PointerType::CreateInternal(JSContext* cx,
-                            JSObject* ctor,
-                            JSObject* baseType,
-                            JSString* name)
+PointerType::CreateInternal(JSContext* cx, JSObject* baseType)
 {
-  JS_ASSERT(ctor || baseType);
-  JS_ASSERT((baseType && !name) || (!baseType && name));
-
-  if (baseType) {
-    // check if we have a cached PointerType on our base CType.
-    jsval slot;
-    ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot));
-    if (!JSVAL_IS_VOID(slot))
-      return JSVAL_TO_OBJECT(slot);
-  }
+  // check if we have a cached PointerType on our base CType.
+  jsval slot;
+  ASSERT_OK(JS_GetReservedSlot(cx, baseType, SLOT_PTR, &slot));
+  if (!JSVAL_IS_VOID(slot))
+    return JSVAL_TO_OBJECT(slot);
 
   // Get ctypes.PointerType.prototype and the common prototype for CData objects
-  // of this type, either from ctor or the baseType, whichever was provided.
+  // of this type.
   JSObject* typeProto;
   JSObject* dataProto;
-  if (ctor) {
-    typeProto = CType::GetProtoFromCtor(cx, ctor, SLOT_POINTERPROTO);
-    dataProto = CType::GetProtoFromCtor(cx, ctor, SLOT_POINTERDATAPROTO);
-  } else {
-    typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
-    dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO);
-  }
+  typeProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERPROTO);
+  dataProto = CType::GetProtoFromType(cx, baseType, SLOT_POINTERDATAPROTO);
 
   // Create a new CType object with the common properties and slots.
   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_pointer,
-                        name, INT_TO_JSVAL(sizeof(void*)),
+                        NULL, INT_TO_JSVAL(sizeof(void*)),
                         INT_TO_JSVAL(ffi_type_pointer.alignment),
-                        &ffi_type_pointer, NULL);
+                        &ffi_type_pointer);
   if (!typeObj)
     return NULL;
   js::AutoValueRooter root(cx, typeObj);
 
   // Set the target type. (This will be 'null' for an opaque pointer type.)
   if (!JS_SetReservedSlot(cx, typeObj, SLOT_TARGET_T, OBJECT_TO_JSVAL(baseType)))
     return NULL;
 
-  if (baseType) {
-    // Determine the name of the PointerType, since it wasn't supplied.
-    JSString* nameStr = BuildTypeName(cx, typeObj);
-    if (!nameStr ||
-        !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(nameStr)))
-      return NULL;
-
-    // Finally, cache our newly-created PointerType on our pointed-to CType.
-    if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)))
-      return NULL;
-  }
+  // Finally, cache our newly-created PointerType on our pointed-to CType.
+  if (!JS_SetReservedSlot(cx, baseType, SLOT_PTR, OBJECT_TO_JSVAL(typeObj)))
+    return NULL;
 
   return typeObj;
 }
 
 JSBool
 PointerType::ConstructData(JSContext* cx,
                            JSObject* obj,
                            uintN argc,
@@ -3178,17 +3190,17 @@ PointerType::ConstructData(JSContext* cx
 
   if (argc == 0) {
     // Construct a null pointer.
     return JS_TRUE;
   }
 
   if (argc >= 1) {
     JSObject* baseObj = PointerType::GetBaseType(cx, obj);
-    if (baseObj && CType::GetTypeCode(cx, baseObj) == TYPE_function &&
+    if (CType::GetTypeCode(cx, baseObj) == TYPE_function &&
         JSVAL_IS_OBJECT(argv[0]) &&
         JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(argv[0]))) {
       // Construct a FunctionType.ptr from a JS function, and allow an
       // optional 'this' argument.
       JSObject* thisObj = NULL;
       if (argc == 2) {
         if (JSVAL_IS_OBJECT(argv[1])) {
           thisObj = JSVAL_TO_OBJECT(argv[1]);
@@ -3213,16 +3225,17 @@ PointerType::ConstructData(JSContext* cx
 
 JSObject*
 PointerType::GetBaseType(JSContext* cx, JSObject* obj)
 {
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_pointer);
 
   jsval type;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_TARGET_T, &type));
+  JS_ASSERT(!JSVAL_IS_NULL(type));
   return JSVAL_TO_OBJECT(type);
 }
 
 JSBool
 PointerType::TargetTypeGetter(JSContext* cx,
                               JSObject* obj,
                               jsval idval,
                               jsval* vp)
@@ -3275,21 +3288,16 @@ PointerType::ContentsGetter(JSContext* c
   // Get pointer type and base type.
   JSObject* typeObj = CData::GetCType(cx, obj);
   if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
     JS_ReportError(cx, "not a PointerType");
     return JS_FALSE;
   }
 
   JSObject* baseType = GetBaseType(cx, typeObj);
-  if (!baseType) {
-    JS_ReportError(cx, "cannot get contents of an opaque pointer type");
-    return JS_FALSE;
-  }
-
   if (!CType::IsSizeDefined(cx, baseType)) {
     JS_ReportError(cx, "cannot get contents of undefined size");
     return JS_FALSE;
   }
 
   void* data = *static_cast<void**>(CData::GetData(cx, obj));
   if (data == NULL) {
     JS_ReportError(cx, "cannot read contents of null pointer");
@@ -3318,23 +3326,18 @@ PointerType::ContentsSetter(JSContext* c
   // Get pointer type and base type.
   JSObject* typeObj = CData::GetCType(cx, obj);
   if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
     JS_ReportError(cx, "not a PointerType");
     return JS_FALSE;
   }
 
   JSObject* baseType = GetBaseType(cx, typeObj);
-  if (!baseType) {
-    JS_ReportError(cx, "cannot set contents of an opaque pointer type");
-    return JS_FALSE;
-  }
-
   if (!CType::IsSizeDefined(cx, baseType)) {
-    JS_ReportError(cx, "cannot get contents of undefined size");
+    JS_ReportError(cx, "cannot set contents of undefined size");
     return JS_FALSE;
   }
 
   void* data = *static_cast<void**>(CData::GetData(cx, obj));
   if (data == NULL) {
     JS_ReportError(cx, "cannot write contents to null pointer");
     return JS_FALSE;
   }
@@ -3393,82 +3396,47 @@ ArrayType::CreateInternal(JSContext* cx,
   // The size of the base type must be defined.
   // If our length is undefined, both our size and length will be undefined.
   size_t baseSize;
   if (!CType::GetSafeSize(cx, baseType, &baseSize)) {
     JS_ReportError(cx, "base size must be defined");
     return NULL;
   }
 
-  ffi_type* ffiType = NULL;
-  size_t align = CType::GetAlignment(cx, baseType);
-
   jsval sizeVal = JSVAL_VOID;
   jsval lengthVal = JSVAL_VOID;
   if (lengthDefined) {
     // Check for overflow, and convert to a jsint or jsdouble as required.
     size_t size = length * baseSize;
     if (length > 0 && size / length != baseSize) {
       JS_ReportError(cx, "size overflow");
       return NULL;
     }
     if (!SizeTojsval(cx, size, &sizeVal) ||
         !SizeTojsval(cx, length, &lengthVal))
       return NULL;
-
-    // Create an ffi_type to represent the array. This is necessary for the case
-    // where the array is part of a struct. Since libffi has no intrinsic
-    // support for array types, we approximate it by creating a struct type
-    // with elements of type 'baseType' and with appropriate size and alignment
-    // values. It would be nice to not do all the work of setting up 'elements',
-    // but some libffi platforms currently require that it be meaningful. I'm
-    // looking at you, x86_64.
-    ffiType = new ffi_type;
-    if (!ffiType) {
-      JS_ReportOutOfMemory(cx);
-      return NULL;
-    }
-
-    ffiType->type = FFI_TYPE_STRUCT;
-    ffiType->size = size;
-    ffiType->alignment = align;
-    ffiType->elements = new ffi_type*[length + 1];
-    if (!ffiType->elements) {
-      delete ffiType;
-      JS_ReportAllocationOverflow(cx);
-      return NULL;
-    }
-
-    ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
-    for (size_t i = 0; i < length; ++i)
-      ffiType->elements[i] = ffiBaseType;
-    ffiType->elements[length] = NULL;
-  }
+  }
+
+  size_t align = CType::GetAlignment(cx, baseType);
 
   // Create a new CType object with the common properties and slots.
   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_array, NULL,
-                        sizeVal, INT_TO_JSVAL(align), ffiType, NULL);
+                        sizeVal, INT_TO_JSVAL(align), NULL);
   if (!typeObj)
     return NULL;
   js::AutoValueRooter root(cx, typeObj);
 
   // Set the element type.
   if (!JS_SetReservedSlot(cx, typeObj, SLOT_ELEMENT_T, OBJECT_TO_JSVAL(baseType)))
     return NULL;
 
   // Set the length.
   if (!JS_SetReservedSlot(cx, typeObj, SLOT_LENGTH, lengthVal))
     return NULL;
 
-  // Determine the name of the ArrayType.
-  JSString* name = BuildTypeName(cx, typeObj);
-  if (!name ||
-      !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(name)))
-    return NULL;
-
   return typeObj;
 }
 
 JSBool
 ArrayType::ConstructData(JSContext* cx,
                          JSObject* obj,
                          uintN argc,
                          jsval* argv,
@@ -3572,16 +3540,17 @@ ArrayType::ConstructData(JSContext* cx,
 JSObject*
 ArrayType::GetBaseType(JSContext* cx, JSObject* obj)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
 
   jsval type;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ELEMENT_T, &type));
+  JS_ASSERT(!JSVAL_IS_NULL(type));
   return JSVAL_TO_OBJECT(type);
 }
 
 bool
 ArrayType::GetSafeLength(JSContext* cx, JSObject* obj, size_t* result)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
@@ -3618,16 +3587,59 @@ ArrayType::GetLength(JSContext* cx, JSOb
   // The "length" property can be a jsint, a jsdouble, or JSVAL_VOID
   // (for arrays of undefined length), and must always fit in a size_t.
   // For callers who know it can never be JSVAL_VOID, return a size_t directly.
   if (JSVAL_IS_INT(length))
     return JSVAL_TO_INT(length);
   return Convert<size_t>(*JSVAL_TO_DOUBLE(length));
 }
 
+ffi_type*
+ArrayType::BuildFFIType(JSContext* cx, JSObject* obj)
+{
+  JS_ASSERT(CType::IsCType(cx, obj));
+  JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_array);
+  JS_ASSERT(CType::IsSizeDefined(cx, obj));
+
+  JSObject* baseType = ArrayType::GetBaseType(cx, obj);
+  ffi_type* ffiBaseType = CType::GetFFIType(cx, baseType);
+  if (!ffiBaseType)
+    return NULL;
+
+  size_t length = ArrayType::GetLength(cx, obj);
+
+  // Create an ffi_type to represent the array. This is necessary for the case
+  // where the array is part of a struct. Since libffi has no intrinsic
+  // support for array types, we approximate it by creating a struct type
+  // with elements of type 'baseType' and with appropriate size and alignment
+  // values. It would be nice to not do all the work of setting up 'elements',
+  // but some libffi platforms currently require that it be meaningful. I'm
+  // looking at you, x86_64.
+  AutoPtr<ffi_type> ffiType(new ffi_type);
+  if (!ffiType) {
+    JS_ReportOutOfMemory(cx);
+    return NULL;
+  }
+
+  ffiType->type = FFI_TYPE_STRUCT;
+  ffiType->size = CType::GetSize(cx, obj);
+  ffiType->alignment = CType::GetAlignment(cx, obj);
+  ffiType->elements = new ffi_type*[length + 1];
+  if (!ffiType->elements) {
+    JS_ReportAllocationOverflow(cx);
+    return NULL;
+  }
+
+  for (size_t i = 0; i < length; ++i)
+    ffiType->elements[i] = ffiBaseType;
+  ffiType->elements[length] = NULL;
+
+  return ffiType.forget();
+}
+
 JSBool
 ArrayType::ElementTypeGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_array) {
     JS_ReportError(cx, "not an ArrayType");
     return JS_FALSE;
   }
 
@@ -3742,17 +3754,17 @@ ArrayType::AddressOfElement(JSContext* c
   }
 
   if (argc != 1) {
     JS_ReportError(cx, "addressOfElement takes one argument");
     return JS_FALSE;
   }
 
   JSObject* baseType = GetBaseType(cx, typeObj);
-  JSObject* pointerType = PointerType::CreateInternal(cx, NULL, baseType, NULL);
+  JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
   if (!pointerType)
     return JS_FALSE;
   js::AutoValueRooter root(cx, pointerType);
 
   // Create a PointerType CData object containing null.
   JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
   if (!result)
     return JS_FALSE;
@@ -3775,212 +3787,226 @@ ArrayType::AddressOfElement(JSContext* c
   return JS_TRUE;
 }
 
 /*******************************************************************************
 ** StructType implementation
 *******************************************************************************/
 
 // For a struct field descriptor 'val' of the form { name : type }, extract
-// 'name' and 'type', and populate 'field' with the information.
-static JSBool
-ExtractStructField(JSContext* cx, jsval val, FieldInfo* field)
+// 'name' and 'type'.
+static JSString*
+ExtractStructField(JSContext* cx, jsval val, JSObject** typeObj)
 {
   if (JSVAL_IS_PRIMITIVE(val)) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
-    return false;
+    return NULL;
   }
 
   JSObject* obj = JSVAL_TO_OBJECT(val);
   JSObject* iter = JS_NewPropertyIterator(cx, obj);
   if (!iter)
-    return false;
+    return NULL;
   js::AutoValueRooter iterroot(cx, iter);
 
   jsid id;
   if (!JS_NextProperty(cx, iter, &id))
-    return false;
+    return NULL;
 
   js::AutoValueRooter nameVal(cx);
   if (!JS_IdToValue(cx, id, nameVal.addr()))
-    return false;
+    return NULL;
   if (!JSVAL_IS_STRING(nameVal.value())) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
-    return false;
-  }
+    return NULL;
+  }
+  JSString* name = JSVAL_TO_STRING(nameVal.value());
 
   // make sure we have one, and only one, property
   if (!JS_NextProperty(cx, iter, &id))
-    return false;
+    return NULL;
   if (!JSVAL_IS_VOID(id)) {
     JS_ReportError(cx, "struct field descriptors must contain one property");
-    return false;
-  }
-
-  JSString* name = JSVAL_TO_STRING(nameVal.value());
-  field->mName.clear();
-  AppendString(field->mName, name);
+    return NULL;
+  }
 
   js::AutoValueRooter propVal(cx);
   if (!JS_GetUCProperty(cx, obj, name->chars(), name->length(), propVal.addr()))
-    return false;
+    return NULL;
 
   if (JSVAL_IS_PRIMITIVE(propVal.value()) ||
       !CType::IsCType(cx, JSVAL_TO_OBJECT(propVal.value()))) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
-    return false;
+    return NULL;
   }
 
   // Undefined size or zero size struct members are illegal.
   // (Zero-size arrays are legal as struct members in C++, but libffi will
   // choke on a zero-size struct, so we disallow them.)
-  field->mType = JSVAL_TO_OBJECT(propVal.value());
+  *typeObj = JSVAL_TO_OBJECT(propVal.value());
   size_t size;
-  if (!CType::GetSafeSize(cx, field->mType, &size) || size == 0) {
+  if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) {
     JS_ReportError(cx, "struct field types must have defined and nonzero size");
-    return false;
-  }
-
-  return true;
+    return NULL;
+  }
+
+  return name;
 }
 
 // For a struct field with 'name' and 'type', add an element to field
 // descriptor array 'arrayObj' of the form { name : type }.
 static JSBool
 AddFieldToArray(JSContext* cx,
                 JSObject* arrayObj,
                 jsval* element,
-                const String& name,
+                JSString* name,
                 JSObject* typeObj)
 {
   JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, arrayObj);
   if (!fieldObj)
     return false;
 
   *element = OBJECT_TO_JSVAL(fieldObj);
 
   if (!JS_DefineUCProperty(cx, fieldObj,
-         name.begin(), name.length(),
+         name->chars(), name->length(),
          OBJECT_TO_JSVAL(typeObj), NULL, NULL,
          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   return JS_SealObject(cx, fieldObj, JS_FALSE);
 }
 
 JSBool
 StructType::Create(JSContext* cx, uintN argc, jsval* vp)
 {
   // Construct and return a new StructType object.
-  if (argc != 2) {
-    JS_ReportError(cx, "StructType takes two arguments");
+  if (argc < 1 || argc > 2) {
+    JS_ReportError(cx, "StructType takes one or two arguments");
     return JS_FALSE;
   }
 
   jsval* argv = JS_ARGV(cx, vp);
   jsval name = argv[0];
   if (!JSVAL_IS_STRING(name)) {
     JS_ReportError(cx, "first argument must be a string");
     return JS_FALSE;
   }
 
-  if (JSVAL_IS_PRIMITIVE(argv[1]) ||
-      !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[1]))) {
-    JS_ReportError(cx, "second argument must be an array");
+  // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
+  JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+  JSObject* typeProto = CType::GetProtoFromCtor(cx, callee, SLOT_STRUCTPROTO);
+
+  // Create a simple StructType with no defined fields. The result will be
+  // non-instantiable as CData, will have no 'prototype' property, and will
+  // have undefined size and alignment and no ffi_type.
+  JSObject* result = CType::Create(cx, typeProto, NULL, TYPE_struct,
+                       JSVAL_TO_STRING(name), JSVAL_VOID, JSVAL_VOID, NULL);
+  if (!result)
     return JS_FALSE;
-  }
-
-  JSObject* fieldsObj = JSVAL_TO_OBJECT(argv[1]);
+  js::AutoValueRooter root(cx, result);
+
+  if (argc == 2) {
+    if (JSVAL_IS_PRIMITIVE(argv[1]) ||
+        !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[1]))) {
+      JS_ReportError(cx, "second argument must be an array");
+      return JS_FALSE;
+    }
+
+    // Define the struct fields.
+    if (!DefineInternal(cx, result, JSVAL_TO_OBJECT(argv[1])))
+      return JS_FALSE;
+  }
+
+  JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
+  return JS_TRUE;
+}
+
+JSBool
+StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj)
+{
   jsuint len;
   ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
 
-  // Prepare a new array for the .fields property of the StructType.
-  jsval* fieldsVec;
-  JSObject* fieldsProp =
-    js_NewArrayObjectWithCapacity(cx, len, &fieldsVec);
-  if (!fieldsProp)
+  // Get the common prototype for CData objects of this type from
+  // ctypes.CType.prototype.
+  JSObject* dataProto =
+    CType::GetProtoFromType(cx, typeObj, SLOT_STRUCTDATAPROTO);
+
+  // Set up the 'prototype' and 'prototype.constructor' properties.
+  // The prototype will reflect the struct fields as properties on CData objects
+  // created from this type.
+  JSObject* prototype = JS_NewObject(cx, &sCDataProtoClass, dataProto, NULL);
+  if (!prototype)
     return JS_FALSE;
-  js::AutoValueRooter root(cx, fieldsProp);
-  JS_ASSERT(len == 0 || fieldsVec);
-
-  AutoPtr<ffi_type> ffiType(new ffi_type);
-  if (!ffiType) {
+  js::AutoValueRooter protoroot(cx, prototype);
+
+  if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(typeObj),
+         NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT))
+    return JS_FALSE;
+
+  // Create a hash of FieldInfo objects to stash on the type object.
+  FieldInfoHash* fields(new FieldInfoHash);
+  if (!fields || !fields->init(len)) {
+    delete fields;
     JS_ReportOutOfMemory(cx);
     return JS_FALSE;
   }
-  ffiType->type = FFI_TYPE_STRUCT;
-
-  // Create an array of FieldInfo objects to stash on the type object, and an
-  // array of PropertySpecs to reflect the struct fields as properties
-  // on CData objects created from this type.
-  AutoPtr< Array<FieldInfo> > fields(new Array<FieldInfo>);
-  Array<PropertySpec> instanceProps;
-  if (!fields ||
-      !fields->resize(len) ||
-      !instanceProps.resize(len + 1)) {
-    JS_ReportOutOfMemory(cx);
+
+  // Stash the FieldInfo hash in a reserved slot now, for GC safety of its
+  // constituents.
+  if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
+         PRIVATE_TO_JSVAL(fields)))
     return JS_FALSE;
-  }
-
-  AutoPtr<ffi_type*>::Array elements;
-
-  // Process the field types and fill in the ffi_type fields.
-  size_t structSize = 0, structAlign = 0;
+
+  // Process the field types.
+  size_t structSize, structAlign;
   if (len != 0) {
-    elements = new ffi_type*[len + 1];
-    if (!elements) {
-      JS_ReportOutOfMemory(cx);
-      return JS_FALSE;
-    }
-    elements[len] = NULL;
+    structSize = 0;
+    structAlign = 0;
 
     for (jsuint i = 0; i < len; ++i) {
       js::AutoValueRooter item(cx);
       if (!JS_GetElement(cx, fieldsObj, i, item.addr()))
         return JS_FALSE;
 
-      FieldInfo* info = fields->begin() + i;
-      if (!ExtractStructField(cx, item.value(), info))
+      JSObject* fieldType;
+      JSString* name = ExtractStructField(cx, item.value(), &fieldType);
+      if (!name)
         return JS_FALSE;
 
-      // Make sure each field name is unique.
-      for (size_t j = 0; j < i; ++j) {
-        FieldInfo* field = fields->begin() + j;
-        if (StringsEqual(field->mName, info->mName)) {
-          JS_ReportError(cx, "struct fields must have unique names");
-          return JS_FALSE;
-        }
+      // Make sure each field name is unique, and add it to the hash.
+      FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
+      if (entryPtr) {
+        JS_ReportError(cx, "struct fields must have unique names");
+        return JS_FALSE;
       }
-
-      // Duplicate the object for the fields property.
-      if (!AddFieldToArray(cx, fieldsProp, &fieldsVec[i],
-             info->mName, info->mType))
+      ASSERT_OK(fields->add(entryPtr, name, FieldInfo()));
+      FieldInfo& info = entryPtr->value;
+      info.mType = fieldType;
+      info.mIndex = i;
+
+      // Add the field to the StructType's 'prototype' property.
+      if (!JS_DefineUCProperty(cx, prototype,
+             name->chars(), name->length(), JSVAL_VOID,
+             StructType::FieldGetter, StructType::FieldSetter,
+             JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
         return JS_FALSE;
 
-      // Fill in the PropertySpec for the field.
-      PropertySpec* instanceProp = instanceProps.begin() + i;
-      instanceProp->name = info->mName.begin();
-      instanceProp->namelen = info->mName.length();
-      instanceProp->flags = JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT;
-      instanceProp->getter = StructType::FieldGetter;
-      instanceProp->setter = StructType::FieldSetter;
-
-      elements[i] = CType::GetFFIType(cx, info->mType);
-
-      size_t fieldSize = CType::GetSize(cx, info->mType);
-      size_t fieldAlign = CType::GetAlignment(cx, info->mType);
+      size_t fieldSize = CType::GetSize(cx, fieldType);
+      size_t fieldAlign = CType::GetAlignment(cx, fieldType);
       size_t fieldOffset = Align(structSize, fieldAlign);
       // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
       // be zero, we can safely check fieldOffset + fieldSize without first
       // checking fieldOffset for overflow.
       if (fieldOffset + fieldSize < structSize) {
         JS_ReportError(cx, "size overflow");
         return JS_FALSE;
       }
-      info->mOffset = fieldOffset;
+      info.mOffset = fieldOffset;
       structSize = fieldOffset + fieldSize;
 
       if (fieldAlign > structAlign)
         structAlign = fieldAlign;
     }
 
     // Pad the struct tail according to struct alignment.
     size_t structTail = Align(structSize, structAlign);
@@ -3992,20 +4018,76 @@ StructType::Create(JSContext* cx, uintN 
 
   } else {
     // Empty structs are illegal in C, but are legal and have a size of
     // 1 byte in C++. We're going to allow them, and trick libffi into
     // believing this by adding a char member. The resulting struct will have
     // no getters or setters, and will be initialized to zero.
     structSize = 1;
     structAlign = 1;
+  }
+
+  jsval sizeVal;
+  if (!SizeTojsval(cx, structSize, &sizeVal))
+    return JS_FALSE;
+
+  if (!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, sizeVal) ||
+      !JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)) ||
+      //!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
+      !JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
+    return JS_FALSE;
+
+  return JS_TRUE;
+}
+
+ffi_type*
+StructType::BuildFFIType(JSContext* cx, JSObject* obj)
+{
+  JS_ASSERT(CType::IsCType(cx, obj));
+  JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
+  JS_ASSERT(CType::IsSizeDefined(cx, obj));
+
+  const FieldInfoHash* fields = GetFieldInfo(cx, obj);
+  size_t len = fields->count();
+
+  size_t structSize = CType::GetSize(cx, obj);
+  size_t structAlign = CType::GetAlignment(cx, obj);
+
+  AutoPtr<ffi_type> ffiType(new ffi_type);
+  if (!ffiType) {
+    JS_ReportOutOfMemory(cx);
+    return NULL;
+  }
+  ffiType->type = FFI_TYPE_STRUCT;
+
+  AutoPtr<ffi_type*>::Array elements;
+  if (len != 0) {
+    elements = new ffi_type*[len + 1];
+    if (!elements) {
+      JS_ReportOutOfMemory(cx);
+      return NULL;
+    }
+    elements[len] = NULL;
+
+    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
+      const FieldInfoHash::Entry& entry = r.front();
+      ffi_type* fieldType = CType::GetFFIType(cx, entry.value.mType);
+      if (!fieldType)
+        return NULL;
+      elements[entry.value.mIndex] = fieldType;
+    }
+
+  } else {
+    // Represent an empty struct as having a size of 1 byte, just like C++.
+    JS_ASSERT(structSize == 1);
+    JS_ASSERT(structAlign == 1);
     elements = new ffi_type*[2];
     if (!elements) {
       JS_ReportOutOfMemory(cx);
-      return JS_FALSE;
+      return NULL;
     }
     elements[0] = &ffi_type_uint8;
     elements[1] = NULL;
   }
 
   ffiType->elements = elements.get();
 
 #ifdef DEBUG
@@ -4023,171 +4105,218 @@ StructType::Create(JSContext* cx, uintN 
   // Fill in the ffi_type's size and align fields. This makes libffi treat the
   // type as initialized; it will not recompute the values. (We assume
   // everything agrees; if it doesn't, we really want to know about it, which
   // is the purpose of the above debug-only check.)
   ffiType->size = structSize;
   ffiType->alignment = structAlign;
 #endif
 
-  // Terminate the PropertySpec array.
-  instanceProps[len].name = NULL;
-
-  jsval sizeVal;
-  if (!SizeTojsval(cx, structSize, &sizeVal))
+  elements.forget();
+  return ffiType.forget();
+}
+
+JSBool
+StructType::Define(JSContext* cx, uintN argc, jsval* vp)
+{
+  JSObject* obj = JS_THIS_OBJECT(cx, vp);
+  JS_ASSERT(obj);
+
+  if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
+    JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
-
-  // Get ctypes.StructType.prototype and the common prototype for CData objects
-  // of this type, from the ctypes.StructType constructor.
-  JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
-  JSObject* typeProto = CType::GetProtoFromCtor(cx, callee, SLOT_STRUCTPROTO);
-  JSObject* dataProto = CType::GetProtoFromCtor(cx, callee, SLOT_STRUCTDATAPROTO);
-
-  // Create a new CType object with the common properties and slots.
-  JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_struct,
-                        JSVAL_TO_STRING(name), sizeVal,
-                        INT_TO_JSVAL(structAlign), ffiType.get(),
-                        instanceProps.begin());
-  if (!typeObj)
+  }
+
+  if (CType::IsSizeDefined(cx, obj)) {
+    JS_ReportError(cx, "StructType has already been defined");
+    return JS_FALSE;
+  }
+
+  if (argc != 1) {
+    JS_ReportError(cx, "define takes one argument");
     return JS_FALSE;
-  ffiType.forget();
-  elements.forget();
-
-  JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(typeObj));
-
-  // Seal and attach the fields array. (The fields array also prevents the
-  // type objects we depend on from being GC'ed).
-  if (!JS_SealObject(cx, fieldsProp, JS_FALSE) ||
-      !JS_SetReservedSlot(cx, typeObj, SLOT_FIELDS, OBJECT_TO_JSVAL(fieldsProp)))
+  }
+
+  jsval arg = JS_ARGV(cx, vp)[0];
+  if (JSVAL_IS_PRIMITIVE(arg) ||
+      !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(arg))) {
+    JS_ReportError(cx, "argument must be an array");
     return JS_FALSE;
-
-  // Stash the FieldInfo array in a reserved slot.
-  if (!JS_SetReservedSlot(cx, typeObj, SLOT_FIELDINFO,
-         PRIVATE_TO_JSVAL(fields.get())))
-    return JS_FALSE;
-  fields.forget();
-
-  return JS_TRUE;
+  }
+
+  return DefineInternal(cx, obj, JSVAL_TO_OBJECT(arg));
 }
 
 JSBool
 StructType::ConstructData(JSContext* cx,
                           JSObject* obj,
                           uintN argc,
                           jsval* argv,
                           jsval* rval)
 {
   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
   }
 
+  if (!CType::IsSizeDefined(cx, obj)) {
+    JS_ReportError(cx, "cannot construct an opaque StructType");
+    return JS_FALSE;
+  }
+
   JSObject* result = CData::Create(cx, obj, NULL, NULL, true);
   if (!result)
     return JS_FALSE;
 
   *rval = OBJECT_TO_JSVAL(result);
 
   if (argc == 0)
     return JS_TRUE;
 
   char* buffer = static_cast<char*>(CData::GetData(cx, result));
-  Array<FieldInfo>* fields = GetFieldInfo(cx, obj);
+  const FieldInfoHash* fields = GetFieldInfo(cx, obj);
 
   if (argc == 1) {
     // There are two possible interpretations of the argument:
     // 1) It may be an object '{ ... }' with properties representing the
     //    struct fields intended to ExplicitConvert wholesale to our StructType.
     // 2) If the struct contains one field, the arg may be intended to
     //    ImplicitConvert directly to that arg's CType.
     // Thankfully, the conditions for these two possibilities to succeed
     // are mutually exclusive, so we can pick the right one.
 
     // Try option 1) first.
     if (ExplicitConvert(cx, argv[0], obj, buffer))
       return JS_TRUE;
 
-    if (fields->length() != 1)
+    if (fields->count() != 1)
       return JS_FALSE;
 
     // If ExplicitConvert failed, and there is no pending exception, then assume
     // hard failure (out of memory, or some other similarly serious condition).
     if (!JS_IsExceptionPending(cx))
       return JS_FALSE;
 
     // Otherwise, assume soft failure, and clear the pending exception so that we
     // can throw a different one as required.
     JS_ClearPendingException(cx);
 
     // Fall through to try option 2).
   }
 
   // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
   // ImplicitConvert each field.
-  if (argc == fields->length()) {
-    for (size_t i = 0; i < fields->length(); ++i) {
-      FieldInfo* field = fields->begin() + i;
-      if (!ImplicitConvert(cx, argv[i], field->mType, buffer + field->mOffset,
+  if (argc == fields->count()) {
+    for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
+      const FieldInfo& field = r.front().value;
+      if (!ImplicitConvert(cx, argv[field.mIndex], field.mType,
+             buffer + field.mOffset,
              false, NULL))
         return JS_FALSE;
     }
 
     return JS_TRUE;
   }
 
   JS_ReportError(cx, "constructor takes 0, 1, or %u arguments",
-    fields->length());
+    fields->count());
   return JS_FALSE;
 }
 
-Array<FieldInfo>*
+const FieldInfoHash*
 StructType::GetFieldInfo(JSContext* cx, JSObject* obj)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
 
   jsval slot;
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDINFO, &slot));
   JS_ASSERT(!JSVAL_IS_VOID(slot) && JSVAL_TO_PRIVATE(slot));
 
-  return static_cast<Array<FieldInfo>*>(JSVAL_TO_PRIVATE(slot));
+  return static_cast<const FieldInfoHash*>(JSVAL_TO_PRIVATE(slot));
 }
 
-FieldInfo*
+const FieldInfo*
 StructType::LookupField(JSContext* cx, JSObject* obj, jsval idval)
 {
   JS_ASSERT(CType::IsCType(cx, obj));
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
 
-  Array<FieldInfo>* fields = GetFieldInfo(cx, obj);
-
   JSString* name = JSVAL_TO_STRING(idval);
-  for (size_t i = 0; i < fields->length(); ++i) {
-    FieldInfo* field = fields->begin() + i;
-    if (StringsEqual(field->mName, name))
-      return field;
-  }
+  FieldInfoHash::Ptr ptr = GetFieldInfo(cx, obj)->lookup(name);
+  if (ptr)
+    return &ptr->value;
 
   const char* bytes = JS_GetStringBytesZ(cx, name);
   if (!bytes)
     return NULL;
 
   JS_ReportError(cx, "%s does not name a field", bytes);
   return NULL;
 }
 
+JSObject*
+StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
+{
+  JS_ASSERT(CType::IsCType(cx, obj));
+  JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
+  JS_ASSERT(CType::IsSizeDefined(cx, obj));
+
+  const FieldInfoHash* fields = GetFieldInfo(cx, obj);
+  size_t len = fields->count();
+
+  // Prepare a new array for the 'fields' property of the StructType.
+  jsval* fieldsVec;
+  JSObject* fieldsProp =
+    js_NewArrayObjectWithCapacity(cx, len, &fieldsVec);
+  if (!fieldsProp)
+    return NULL;
+  js::AutoValueRooter root(cx, fieldsProp);
+  JS_ASSERT(len == 0 || fieldsVec);
+
+  for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
+    const FieldInfoHash::Entry& entry = r.front();
+    // Add the field descriptor to the array.
+    if (!AddFieldToArray(cx, fieldsProp, &fieldsVec[entry.value.mIndex],
+           entry.key, entry.value.mType))
+      return NULL;
+  }
+
+  // Seal the fields array.
+  if (!JS_SealObject(cx, fieldsProp, JS_FALSE))
+    return NULL;
+
+  return fieldsProp;
+}
+
 JSBool
 StructType::FieldsArrayGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
   if (!CType::IsCType(cx, obj) || CType::GetTypeCode(cx, obj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
   }
 
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_FIELDS, vp));
+
+  if (!CType::IsSizeDefined(cx, obj)) {
+    JS_ASSERT(JSVAL_IS_VOID(*vp));
+    return JS_TRUE;
+  }
+
+  if (JSVAL_IS_VOID(*vp)) {
+    // Build the 'fields' array lazily.
+    JSObject* fields = BuildFieldsArray(cx, obj);
+    if (!fields ||
+        !JS_SetReservedSlot(cx, obj, SLOT_FIELDS, OBJECT_TO_JSVAL(fields)))
+      return JS_FALSE;
+
+    *vp = OBJECT_TO_JSVAL(fields);
+  }
+
   JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp) &&
             JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)));
   return JS_TRUE;
 }
 
 JSBool
 StructType::FieldGetter(JSContext* cx, JSObject* obj, jsval idval, jsval* vp)
 {
@@ -4197,17 +4326,17 @@ StructType::FieldGetter(JSContext* cx, J
   }
 
   JSObject* typeObj = CData::GetCType(cx, obj);
   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
   }
 
-  FieldInfo* field = LookupField(cx, typeObj, idval);
+  const FieldInfo* field = LookupField(cx, typeObj, idval);
   if (!field)
     return JS_FALSE;
 
   char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
   return ConvertToJS(cx, field->mType, obj, data, false, false, vp);
 }
 
 JSBool
@@ -4219,17 +4348,17 @@ StructType::FieldSetter(JSContext* cx, J
   }
 
   JSObject* typeObj = CData::GetCType(cx, obj);
   if (CType::GetTypeCode(cx, typeObj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
     return JS_FALSE;
   }
 
-  FieldInfo* field = LookupField(cx, typeObj, idval);
+  const FieldInfo* field = LookupField(cx, typeObj, idval);
   if (!field)
     return JS_FALSE;
 
   char* data = static_cast<char*>(CData::GetData(cx, obj)) + field->mOffset;
   return ImplicitConvert(cx, *vp, field->mType, data, false, NULL);
 }
 
 JSBool
@@ -4249,22 +4378,22 @@ StructType::AddressOfField(JSContext* cx
     return JS_FALSE;
   }
 
   if (argc != 1) {
     JS_ReportError(cx, "addressOfField takes one argument");
     return JS_FALSE;
   }
 
-  FieldInfo* field = LookupField(cx, typeObj, JS_ARGV(cx, vp)[0]);
+  const FieldInfo* field = LookupField(cx, typeObj, JS_ARGV(cx, vp)[0]);
   if (!field)
     return JS_FALSE;
 
   JSObject* baseType = field->mType;
-  JSObject* pointerType = PointerType::CreateInternal(cx, NULL, baseType, NULL);
+  JSObject* pointerType = PointerType::CreateInternal(cx, baseType);
   if (!pointerType)
     return JS_FALSE;
   js::AutoValueRooter root(cx, pointerType);
 
   // Create a PointerType CData object containing null.
   JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
   if (!result)
     return JS_FALSE;
@@ -4340,26 +4469,31 @@ PrepareType(JSContext* cx, jsval type)
 
   JSObject* result = JSVAL_TO_OBJECT(type);
   TypeCode typeCode = CType::GetTypeCode(cx, result);
 
   if (typeCode == TYPE_array) {
     // convert array argument types to pointers, just like C.
     // ImplicitConvert will do the same, when passing an array as data.
     JSObject* baseType = ArrayType::GetBaseType(cx, result);
-    result = PointerType::CreateInternal(cx, NULL, baseType, NULL);
+    result = PointerType::CreateInternal(cx, baseType);
     if (!result)
       return NULL;
 
   } else if (typeCode == TYPE_void_t || typeCode == TYPE_function) {
     // disallow void or function argument types
     JS_ReportError(cx, "Cannot have void or function argument type");
     return NULL;
   }
 
+  if (!CType::IsSizeDefined(cx, result)) {
+    JS_ReportError(cx, "Argument type must have defined size");
+    return NULL;
+  }
+
   // libffi cannot pass types of zero size by value.
   JS_ASSERT(CType::GetSize(cx, result) != 0);
 
   return result;
 }
 
 static JSObject*
 PrepareReturnType(JSContext* cx, jsval type)
@@ -4374,16 +4508,21 @@ PrepareReturnType(JSContext* cx, jsval t
   TypeCode typeCode = CType::GetTypeCode(cx, result);
 
   // Arrays and functions can never be return types.
   if (typeCode == TYPE_array || typeCode == TYPE_function) {
     JS_ReportError(cx, "Return type cannot be an array or function");
     return NULL;
   }
 
+  if (typeCode != TYPE_void_t && !CType::IsSizeDefined(cx, result)) {
+    JS_ReportError(cx, "Return type must have defined size");
+    return NULL;
+  }
+
   // libffi cannot pass types of zero size by value.
   JS_ASSERT(typeCode == TYPE_void_t || CType::GetSize(cx, result) != 0);
 
   return result;
 }
 
 static JS_ALWAYS_INLINE bool
 IsEllipsis(jsval v)
@@ -4404,21 +4543,25 @@ PrepareCIF(JSContext* cx,
            FunctionInfo* fninfo)
 {
   ffi_abi abi;
   if (!GetABI(cx, OBJECT_TO_JSVAL(fninfo->mABI), &abi)) {
     JS_ReportError(cx, "Invalid ABI specification");
     return false;
   }
 
+  ffi_type* rtype = CType::GetFFIType(cx, fninfo->mReturnType);
+  if (!rtype)
+    return false;
+
   ffi_status status =
     ffi_prep_cif(&fninfo->mCIF,
                  abi,
                  fninfo->mFFITypes.length(),
-                 CType::GetFFIType(cx, fninfo->mReturnType),
+                 rtype,
                  fninfo->mFFITypes.begin());
 
   switch (status) {
   case FFI_OK:
     return true;
   case FFI_BAD_ABI:
     JS_ReportError(cx, "Invalid ABI specification");
     return false;
@@ -4485,18 +4628,22 @@ NewFunctionInfo(JSContext* cx,
       }
       break;
     }
 
     JSObject* argType = PrepareType(cx, argTypes[i]);
     if (!argType)
       return NULL;
 
+    ffi_type* ffiType = CType::GetFFIType(cx, argType);
+    if (!ffiType)
+      return NULL;
+
     fninfo->mArgTypes.append(argType);
-    fninfo->mFFITypes.append(CType::GetFFIType(cx, argType));
+    fninfo->mFFITypes.append(ffiType);
   }
 
   if (fninfo->mIsVariadic)
     // wait to PrepareCIF until function is called
     return fninfo.forget();
 
   if (!PrepareCIF(cx, fninfo.get()))
     return NULL;
@@ -4570,35 +4717,28 @@ FunctionType::CreateInternal(JSContext* 
   // Get ctypes.FunctionType.prototype and the common prototype for CData objects
   // of this type, from ctypes.CType.prototype.
   JSObject* typeProto = CType::GetProtoFromType(cx, fninfo->mReturnType,
                                                 SLOT_FUNCTIONPROTO);
   JSObject* dataProto = CType::GetProtoFromType(cx, fninfo->mReturnType,
                                                 SLOT_FUNCTIONDATAPROTO);
 
   // Create a new CType object with the common properties and slots.
-  // We use ffi_type_void here in its capacity as "a type of undefined size".
   JSObject* typeObj = CType::Create(cx, typeProto, dataProto, TYPE_function,
-                        NULL, JSVAL_VOID, JSVAL_VOID, &ffi_type_void, NULL);
+                        NULL, JSVAL_VOID, JSVAL_VOID, NULL);
   if (!typeObj)
     return NULL;
   js::AutoValueRooter root(cx, typeObj);
 
   // Stash the FunctionInfo in a reserved slot.
   if (!JS_SetReservedSlot(cx, typeObj, SLOT_FNINFO,
          PRIVATE_TO_JSVAL(fninfo.get())))
     return NULL;
   fninfo.forget();
 
-  // Determine the name of the FunctionType.
-  JSString* name = BuildTypeName(cx, typeObj);
-  if (!name ||
-      !JS_SetReservedSlot(cx, typeObj, SLOT_NAME, STRING_TO_JSVAL(name)))
-    return NULL;
-
   return typeObj;
 }
 
 JSBool
 FunctionType::ConstructData(JSContext* cx,
                             JSObject* typeObj,
                             JSObject* dataObj,
                             JSObject* fnObj,
@@ -4674,19 +4814,23 @@ FunctionType::Call(JSContext* cx,
   // get the callee object...
   obj = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv));
   if (!CData::IsCData(cx, obj)) {
     JS_ReportError(cx, "not a CData");
     return false;
   }
 
   JSObject* typeObj = CData::GetCType(cx, obj);
-  if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer ||
-      !(typeObj = PointerType::GetBaseType(cx, typeObj)) ||
-      CType::GetTypeCode(cx, typeObj) != TYPE_function) {
+  if (CType::GetTypeCode(cx, typeObj) != TYPE_pointer) {
+    JS_ReportError(cx, "not a FunctionType.ptr");
+    return false;
+  }
+
+  typeObj = PointerType::GetBaseType(cx, typeObj);
+  if (CType::GetTypeCode(cx, typeObj) != TYPE_function) {
     JS_ReportError(cx, "not a FunctionType.ptr");
     return false;
   }
 
   FunctionInfo* fninfo = GetFunctionInfo(cx, typeObj);
   JSUint32 argcFixed = fninfo->mArgTypes.length();
 
   if ((!fninfo->mIsVariadic && argc != argcFixed) ||
@@ -4735,21 +4879,21 @@ FunctionType::Call(JSContext* cx,
         JS_ReportError(cx, "argument %d of type %s is not a CData object",
                        i, JS_GetTypeName(cx, JS_TypeOfValue(cx, argv[i])));
         return false;
       }
       if (!(type = CData::GetCType(cx, obj)) ||
           !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
           // Relying on ImplicitConvert only for the limited purpose of
           // converting one CType to another (e.g., T[] to T*).
-          !ConvertArgument(cx, argv[i], type, &values[i], &strings)) {
+          !ConvertArgument(cx, argv[i], type, &values[i], &strings) ||
+          !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
         // These functions report their own errors.
         return false;
       }
-      fninfo->mFFITypes[i] = CType::GetFFIType(cx, type);
     }
     if (!PrepareCIF(cx, fninfo))
       return false;
   }
 
   // initialize a pointer to an appropriate location, for storing the result
   AutoValue returnValue;
   if (CType::GetTypeCode(cx, fninfo->mReturnType) != TYPE_void_t &&
@@ -5157,17 +5301,17 @@ CData::Create(JSContext* cx,
       memset(data, 0, size);
     else
       memcpy(data, source, size);
   }
 
   *buffer = data;
   if (!JS_SetReservedSlot(cx, dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer))) {
     if (ownResult)
-      delete data;
+      delete[] data;
     delete buffer;
     return NULL;
   }
 
   return dataObj;
 }
 
 void
@@ -5259,17 +5403,17 @@ CData::Address(JSContext* cx, uintN argc
   JS_ASSERT(obj);
 
   if (!IsCData(cx, obj)) {
     JS_ReportError(cx, "not a CData");
     return JS_FALSE;
   }
 
   JSObject* typeObj = CData::GetCType(cx, obj);
-  JSObject* pointerType = PointerType::CreateInternal(cx, NULL, typeObj, NULL);
+  JSObject* pointerType = PointerType::CreateInternal(cx, typeObj);
   if (!pointerType)
     return JS_FALSE;
   js::AutoValueRooter root(cx, pointerType);
 
   // Create a PointerType CData object containing null.
   JSObject* result = CData::Create(cx, pointerType, NULL, NULL, true);
   if (!result)
     return JS_FALSE;
@@ -5346,21 +5490,16 @@ CData::ReadString(JSContext* cx, uintN a
   JSObject* baseType;
   JSObject* typeObj = GetCType(cx, obj);
   TypeCode typeCode = CType::GetTypeCode(cx, typeObj);
   void* data;
   size_t maxLength = -1;
   switch (typeCode) {
   case TYPE_pointer:
     baseType = PointerType::GetBaseType(cx, typeObj);
-    if (!baseType) {
-      JS_ReportError(cx, "cannot read contents of pointer to opaque type");
-      return JS_FALSE;
-    }
-
     data = *static_cast<void**>(GetData(cx, obj));
     if (data == NULL) {
       JS_ReportError(cx, "cannot read contents of null pointer");
       return JS_FALSE;
     }
     break;
   case TYPE_array:
     baseType = ArrayType::GetBaseType(cx, typeObj);
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -36,16 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef CTYPES_H
 #define CTYPES_H
 
 #include "jscntxt.h"
 #include "jsapi.h"
+#include "jshashtable.h"
 #include "prlink.h"
 #include "ffi.h"
 
 namespace js {
 namespace ctypes {
 
 /*******************************************************************************
 ** Utility classes
@@ -232,39 +233,52 @@ enum TypeCode {
 #define DEFINE_TYPE(name, type, ffiType) TYPE_##name,
 #include "typedefs.h"
   TYPE_pointer,
   TYPE_function,
   TYPE_array,
   TYPE_struct
 };
 
+// Descriptor of one field in a StructType. The name of the field is stored
+// as the key to the hash entry.
 struct FieldInfo
 {
-  // We need to provide a copy constructor because of Vector.
-  FieldInfo() {}
-  FieldInfo(const FieldInfo& other)
-  {
-    JS_NOT_REACHED("shouldn't be copy constructing FieldInfo");
+  JSObject* mType;    // CType of the field
+  size_t    mIndex;   // index of the field in the struct (first is 0)
+  size_t    mOffset;  // offset of the field in the struct, in bytes
+};
+
+// Hash policy for FieldInfos.
+struct FieldHashPolicy
+{
+  typedef JSString* Key;
+  typedef Key Lookup;
+
+  static uint32 hash(const Lookup &l) {
+    const jschar* s = l->chars();
+    size_t n = l->length();
+    uint32 hash = 0;
+    for (; n > 0; s++, n--)
+      hash = hash * 33 + *s;
+    return hash;
   }
 
-  String    mName;
-  JSObject* mType;
-  size_t    mOffset;
+  static JSBool match(const Key &k, const Lookup &l) {
+    if (k == l)
+      return true;
+
+    if (k->length() != l->length())
+      return false;
+
+    return memcmp(k->chars(), l->chars(), k->length() * sizeof(jschar)) == 0;
+  }
 };
 
-// Just like JSPropertySpec, but with a Unicode name.
-struct PropertySpec
-{
-  const jschar* name;
-  size_t namelen;
-  uint8 flags;
-  JSPropertyOp getter;
-  JSPropertyOp setter;
-};
+typedef HashMap<JSString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
 
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo
 {
   // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
   // FunctionType::Call, when mIsVariadic. Not always consistent with
   // mFFITypes, due to lazy initialization when mIsVariadic.
@@ -390,18 +404,17 @@ enum Int64FunctionSlot {
 };
 
 /*******************************************************************************
 ** Object API definitions
 *******************************************************************************/
 
 namespace CType {
   JSObject* Create(JSContext* cx, JSObject* typeProto, JSObject* dataProto,
-    TypeCode type, JSString* name, jsval size, jsval align, ffi_type* ffiType, 
-    PropertySpec* ps);
+    TypeCode type, JSString* name, jsval size, jsval align, ffi_type* ffiType);
 
   JSObject* DefineBuiltin(JSContext* cx, JSObject* parent, const char* propName,
     JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type,
     jsval size, jsval align, ffi_type* ffiType);
 
   bool IsCType(JSContext* cx, JSObject* obj);
   TypeCode GetTypeCode(JSContext* cx, JSObject* typeObj);
   bool TypesEqual(JSContext* cx, JSObject* t1, JSObject* t2);
@@ -411,34 +424,38 @@ namespace CType {
   size_t GetAlignment(JSContext* cx, JSObject* obj);
   ffi_type* GetFFIType(JSContext* cx, JSObject* obj);
   JSString* GetName(JSContext* cx, JSObject* obj);
   JSObject* GetProtoFromCtor(JSContext* cx, JSObject* obj, CTypeProtoSlot slot);
   JSObject* GetProtoFromType(JSContext* cx, JSObject* obj, CTypeProtoSlot slot);
 }
 
 namespace PointerType {
-  JSObject* CreateInternal(JSContext* cx, JSObject* ctor, JSObject* baseType,
-    JSString* name);
+  JSObject* CreateInternal(JSContext* cx, JSObject* baseType);
 
   JSObject* GetBaseType(JSContext* cx, JSObject* obj);
 }
 
 namespace ArrayType {
   JSObject* CreateInternal(JSContext* cx, JSObject* baseType, size_t length,
     bool lengthDefined);
 
   JSObject* GetBaseType(JSContext* cx, JSObject* obj);
   size_t GetLength(JSContext* cx, JSObject* obj);
   bool GetSafeLength(JSContext* cx, JSObject* obj, size_t* result);
+  ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
 }
 
 namespace StructType {
-  Array<FieldInfo>* GetFieldInfo(JSContext* cx, JSObject* obj);
-  FieldInfo* LookupField(JSContext* cx, JSObject* obj, jsval idval);
+  JSBool DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj);
+
+  const FieldInfoHash* GetFieldInfo(JSContext* cx, JSObject* obj);
+  const FieldInfo* LookupField(JSContext* cx, JSObject* obj, jsval idval);
+  JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
+  ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
 }
 
 namespace FunctionType {
   JSObject* CreateInternal(JSContext* cx, jsval abi, jsval rtype,
     jsval* argtypes, jsuint arglen);
 
   JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj,
     JSObject* refObj, PRFuncPtr fnptr, JSObject* result);
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -248,17 +248,17 @@ Library::Declare(JSContext* cx, uintN ar
     // Create a FunctionType representing the function.
     typeObj = FunctionType::CreateInternal(cx,
                 argv[1], argv[2], &argv[3], argc - 3);
     if (!typeObj)
       return JS_FALSE;
     root.setObject(typeObj);
 
     // Make a function pointer type.
-    typeObj = PointerType::CreateInternal(cx, NULL, typeObj, NULL);
+    typeObj = PointerType::CreateInternal(cx, typeObj);
     if (!typeObj)
       return JS_FALSE;
     root.setObject(typeObj);
 
   } else {
     // Case 2).
     if (JSVAL_IS_PRIMITIVE(argv[1]) ||
         !CType::IsCType(cx, JSVAL_TO_OBJECT(argv[1])) ||
--- a/js/src/imacros.c.out
+++ b/js/src/imacros.c.out
@@ -252,16 +252,72 @@ static struct {
 /*35*/  JSOP_PRIMTOP, (JSTYPE_NUMBER),
 /*37*/  JSOP_SWAP,
 /*38*/  JSOP_POP,
 /*39*/  JSOP_IMACOP,
 /*40*/  JSOP_STOP,
     },
 };
 static struct {
+    jsbytecode incelem[7];
+    jsbytecode eleminc[15];
+} incelem_imacros = {
+    {
+/* 0*/  JSOP_DUP2,
+/* 1*/  JSOP_GETELEM,
+/* 2*/  JSOP_POS,
+/* 3*/  JSOP_ONE,
+/* 4*/  JSOP_ADD,
+/* 5*/  JSOP_SETELEM,
+/* 6*/  JSOP_STOP,
+    },
+    {
+/* 0*/  JSOP_DUP2,
+/* 1*/  JSOP_GETELEM,
+/* 2*/  JSOP_POS,
+/* 3*/  JSOP_DUP,
+/* 4*/  JSOP_ONE,
+/* 5*/  JSOP_ADD,
+/* 6*/  JSOP_PICK, 3,
+/* 8*/  JSOP_PICK, 3,
+/*10*/  JSOP_PICK, 2,
+/*12*/  JSOP_SETELEM,
+/*13*/  JSOP_POP,
+/*14*/  JSOP_STOP,
+    },
+};
+static struct {
+    jsbytecode decelem[7];
+    jsbytecode elemdec[15];
+} decelem_imacros = {
+    {
+/* 0*/  JSOP_DUP2,
+/* 1*/  JSOP_GETELEM,
+/* 2*/  JSOP_POS,
+/* 3*/  JSOP_ONE,
+/* 4*/  JSOP_SUB,
+/* 5*/  JSOP_SETELEM,
+/* 6*/  JSOP_STOP,
+    },
+    {
+/* 0*/  JSOP_DUP2,
+/* 1*/  JSOP_GETELEM,
+/* 2*/  JSOP_POS,
+/* 3*/  JSOP_DUP,
+/* 4*/  JSOP_ONE,
+/* 5*/  JSOP_SUB,
+/* 6*/  JSOP_PICK, 3,
+/* 8*/  JSOP_PICK, 3,
+/*10*/  JSOP_PICK, 2,
+/*12*/  JSOP_SETELEM,
+/*13*/  JSOP_POP,
+/*14*/  JSOP_STOP,
+    },
+};
+static struct {
     jsbytecode String[38];
 } call_imacros = {
     {
 /* 0*/  JSOP_DUP,
 /* 1*/  JSOP_DUP,
 /* 2*/  JSOP_GETPROP, 0, COMMON_ATOM_INDEX(toString),
 /* 5*/  JSOP_IFPRIMTOP, 0, 15,
 /* 8*/  JSOP_SWAP,
@@ -739,26 +795,26 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     3,  /* JSOP_POS */
     0,  /* JSOP_DELNAME */
     0,  /* JSOP_DELPROP */
     0,  /* JSOP_DELELEM */
     0,  /* JSOP_TYPEOF */
     0,  /* JSOP_VOID */
     0,  /* JSOP_INCNAME */
     0,  /* JSOP_INCPROP */
-    0,  /* JSOP_INCELEM */
+    3,  /* JSOP_INCELEM */
     0,  /* JSOP_DECNAME */
     0,  /* JSOP_DECPROP */
-    0,  /* JSOP_DECELEM */
+    3,  /* JSOP_DECELEM */
     0,  /* JSOP_NAMEINC */
     0,  /* JSOP_PROPINC */
-    0,  /* JSOP_ELEMINC */
+    3,  /* JSOP_ELEMINC */
     0,  /* JSOP_NAMEDEC */
     0,  /* JSOP_PROPDEC */
-    0,  /* JSOP_ELEMDEC */
+    3,  /* JSOP_ELEMDEC */
     1,  /* JSOP_GETPROP */
     0,  /* JSOP_SETPROP */
     0,  /* JSOP_GETELEM */
     0,  /* JSOP_SETELEM */
     0,  /* JSOP_CALLNAME */
     3,  /* JSOP_CALL */
     0,  /* JSOP_NAME */
     0,  /* JSOP_DOUBLE */
@@ -958,16 +1014,20 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
  || x == JSOP_URSH \
  || x == JSOP_ADD \
  || x == JSOP_SUB \
  || x == JSOP_MUL \
  || x == JSOP_DIV \
  || x == JSOP_MOD \
  || x == JSOP_NEG \
  || x == JSOP_POS \
+ || x == JSOP_INCELEM \
+ || x == JSOP_DECELEM \
+ || x == JSOP_ELEMINC \
+ || x == JSOP_ELEMDEC \
  || x == JSOP_GETPROP \
  || x == JSOP_CALL \
  || x == JSOP_ITER \
  || x == JSOP_NEXTITER \
  || x == JSOP_APPLY \
  || x == JSOP_NEW \
  || x == JSOP_CALLPROP \
  || x == JSOP_GETTHISPROP \
@@ -981,16 +1041,20 @@ js_GetImacroStart(jsbytecode* pc) {
     if (size_t(pc - equality_imacros.obj_any) < 38) return equality_imacros.obj_any;
     if (size_t(pc - binary_imacros.any_obj) < 36) return binary_imacros.any_obj;
     if (size_t(pc - binary_imacros.obj_any) < 38) return binary_imacros.obj_any;
     if (size_t(pc - binary_imacros.obj_obj) < 72) return binary_imacros.obj_obj;
     if (size_t(pc - add_imacros.any_obj) < 36) return add_imacros.any_obj;
     if (size_t(pc - add_imacros.obj_any) < 38) return add_imacros.obj_any;
     if (size_t(pc - add_imacros.obj_obj) < 72) return add_imacros.obj_obj;
     if (size_t(pc - unary_imacros.sign) < 41) return unary_imacros.sign;
+    if (size_t(pc - incelem_imacros.incelem) < 7) return incelem_imacros.incelem;
+    if (size_t(pc - incelem_imacros.eleminc) < 15) return incelem_imacros.eleminc;
+    if (size_t(pc - decelem_imacros.decelem) < 7) return decelem_imacros.decelem;
+    if (size_t(pc - decelem_imacros.elemdec) < 15) return decelem_imacros.elemdec;
     if (size_t(pc - call_imacros.String) < 38) return call_imacros.String;
     if (size_t(pc - new_imacros.String) < 38) return new_imacros.String;
     if (size_t(pc - apply_imacros.apply0) < 8) return apply_imacros.apply0;
     if (size_t(pc - apply_imacros.apply1) < 12) return apply_imacros.apply1;
     if (size_t(pc - apply_imacros.apply2) < 16) return apply_imacros.apply2;
     if (size_t(pc - apply_imacros.apply3) < 21) return apply_imacros.apply3;
     if (size_t(pc - apply_imacros.apply4) < 26) return apply_imacros.apply4;
     if (size_t(pc - apply_imacros.apply5) < 31) return apply_imacros.apply5;
--- a/js/src/imacros.jsasm
+++ b/js/src/imacros.jsasm
@@ -293,16 +293,70 @@ 3:      callprop toString               
         swap                            # lval obj
         pop                             # lval
 4:      imacop                          # aval
         stop
     .end
 
 .end unary
 
+.igroup incelem JSOP_INCELEM,JSOP_ELEMINC
+    .imacro incelem                     # obj id
+        dup2                            # obj id obj id
+        getelem                         # obj id val
+        pos                             # obj id n
+        one                             # obj id n 1
+        add                             # obj id m
+        setelem                         # m
+        stop
+    .end
+
+    .imacro eleminc                     # obj id
+        dup2                            # obj id obj id
+        getelem                         # obj id val
+        pos                             # obj id n
+        dup                             # obj id n n
+        one                             # obj id n n 1
+        add                             # obj id n m
+        pick 3                          # id n m obj
+        pick 3                          # n m obj id
+        pick 2                          # n obj id m
+        setelem                         # n m
+        pop                             # n
+        stop
+    .end
+.end incelem
+
+.igroup decelem JSOP_DECELEM,JSOP_ELEMDEC
+    .imacro decelem                     # obj id
+        dup2                            # obj id obj id
+        getelem                         # obj id val
+        pos                             # obj id n
+        one                             # obj id n 1
+        sub                             # obj id m
+        setelem                         # m
+        stop
+    .end
+
+    .imacro elemdec                     # obj id
+        dup2                            # obj id obj id
+        getelem                         # obj id val
+        pos                             # obj id n
+        dup                             # obj id n n
+        one                             # obj id n n 1
+        sub                             # obj id n m
+        pick 3                          # id n m obj
+        pick 3                          # n m obj id
+        pick 2                          # n obj id m
+        setelem                         # n m
+        pop                             # n
+        stop
+    .end
+.end decelem
+
 .igroup call JSOP_CALL
 
     .imacro String                          # String this obj
         dup                                 # String this obj obj
         dup                                 # String this obj obj obj
         getprop toString                    # String this obj obj toString
         ifprimtop 1                         # String this obj obj toString
         swap                                # String this obj toString obj
--- a/js/src/jsapi-tests/testContexts.cpp
+++ b/js/src/jsapi-tests/testContexts.cpp
@@ -19,8 +19,57 @@ BEGIN_TEST(testContexts_IsRunning)
         // acx should not be running
         bool ok = !JS_IsRunning(acx);
         if (!ok)
             JS_ReportError(cx, "Assertion failed: brand new context claims to be running");
         JS_DestroyContext(acx);
         return ok;
     }
 END_TEST(testContexts_IsRunning)
+
+#ifdef JS_THREADSAFE
+
+#include "prthread.h"
+
+struct ThreadData {
+    JSRuntime *rt;
+    JSObject *obj;
+    const char *code;
+    bool ok;
+};
+
+BEGIN_TEST(testContexts_bug561444)
+    {
+	const char *code = "<a><b/></a>.b.@c = '';";
+	EXEC(code);
+
+	jsrefcount rc = JS_SuspendRequest(cx);
+	{
+	    ThreadData data = {rt, global, code, false};
+	    PRThread *thread = 
+		PR_CreateThread(PR_USER_THREAD, threadMain, &data,
+				PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
+	    CHECK(thread);
+	    PR_JoinThread(thread);
+	    CHECK(data.ok);
+	}
+	JS_ResumeRequest(cx, rc);
+        return true;
+    }
+
+    static void threadMain(void *arg) {
+	ThreadData *d = (ThreadData *) arg;
+
+	JSContext *cx = JS_NewContext(d->rt, 8192);
+	if (!cx)
+	    return;
+	JS_BeginRequest(cx);
+	{
+	    jsvalRoot v(cx);
+	    if (!JS_EvaluateScript(cx, d->obj, d->code, strlen(d->code), __FILE__, __LINE__, v.addr()))
+		return;
+	}
+	JS_DestroyContext(cx);
+	d->ok = true;
+    }
+END_TEST(testContexts_bug561444)
+
+#endif
--- a/js/src/jsapi-tests/testTrap.cpp
+++ b/js/src/jsapi-tests/testTrap.cpp
@@ -1,19 +1,20 @@
 #include "tests.h"
 #include "jsdbgapi.h"
 
 static int emptyTrapCallCount = 0;
 
 static JSTrapStatus
 EmptyTrapHandler(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
-                 void *closure)
+                 jsval closure)
 {
     JS_GC(cx);
-    ++emptyTrapCallCount;
+    if (JSVAL_IS_STRING(closure))
+        ++emptyTrapCallCount;
     return JSTRAP_CONTINUE;
 }
 
 BEGIN_TEST(testTrap_gc)
 {
     static const char source[] =
 "var i = 0;\n"
 "var sum = 0;\n"
@@ -44,18 +45,18 @@ BEGIN_TEST(testTrap_gc)
     CHECK(line2);
 
     jsbytecode *line6 = JS_LineNumberToPC(cx, script, 5);
     CHECK(line2);
 
     static const char trapClosureText[] = "some trap closure";
     JSString *trapClosure = JS_NewStringCopyZ(cx, trapClosureText);
     CHECK(trapClosure);
-    JS_SetTrap(cx, script, line2, EmptyTrapHandler, trapClosure);
-    JS_SetTrap(cx, script, line6, EmptyTrapHandler, trapClosure);
+    JS_SetTrap(cx, script, line2, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
+    JS_SetTrap(cx, script, line6, EmptyTrapHandler, STRING_TO_JSVAL(trapClosure));
 
     JS_GC(cx);
 
     CHECK(0 == strcmp(trapClosureText, JS_GetStringBytes(trapClosure)));
 
     // execute
     CHECK(JS_ExecuteScript(cx, global, script, v2.addr()));
     CHECK(emptyTrapCallCount == 11);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -587,19 +587,16 @@ JSRuntime::init(uint32 maxbytes)
         return false;
     titleSharingDone = JS_NEW_CONDVAR(gcLock);
     if (!titleSharingDone)
         return false;
     titleSharingTodo = NO_TITLE_SHARING_TODO;
     debuggerLock = JS_NEW_LOCK();
     if (!debuggerLock)
         return false;
-    deallocatorThread = new JSBackgroundThread();
-    if (!deallocatorThread || !deallocatorThread->init())
-        return false;
 #endif
     return propertyTree.init() && js_InitThreads(this);
 }
 
 JSRuntime::~JSRuntime()
 {
 #ifdef DEBUG
     /* Don't hurt everyone in leaky ol' Mozilla with a fatal JS_ASSERT! */
@@ -638,20 +635,16 @@ JSRuntime::~JSRuntime()
     if (rtLock)
         JS_DESTROY_LOCK(rtLock);
     if (stateChange)
         JS_DESTROY_CONDVAR(stateChange);
     if (titleSharingDone)
         JS_DESTROY_CONDVAR(titleSharingDone);
     if (debuggerLock)
         JS_DESTROY_LOCK(debuggerLock);
-    if (deallocatorThread) {
-        deallocatorThread->cancel();
-        delete deallocatorThread;
-    }
 #endif
     propertyTree.finish();
 }
 
 
 JS_PUBLIC_API(JSRuntime *)
 JS_NewRuntime(uint32 maxbytes)
 {
@@ -5509,49 +5502,33 @@ JS_NewUCRegExpObject(JSContext *cx, jsch
 JS_PUBLIC_API(void)
 JS_SetRegExpInput(JSContext *cx, JSString *input, JSBool multiline)
 {
     JSRegExpStatics *res;
 
     CHECK_REQUEST(cx);
     /* No locking required, cx is thread-private and input must be live. */
     res = &cx->regExpStatics;
+    res->clearRoots();
     res->input = input;
     res->multiline = multiline;
-    cx->runtime->gcPoke = JS_TRUE;
 }
 
 JS_PUBLIC_API(void)
 JS_ClearRegExpStatics(JSContext *cx)
 {
-    JSRegExpStatics *res;
-
     /* No locking required, cx is thread-private and input must be live. */
-    res = &cx->regExpStatics;
-    res->input = NULL;
-    res->multiline = JS_FALSE;
-    res->parenCount = 0;
-    res->lastMatch = res->lastParen = js_EmptySubString;
-    res->leftContext = res->rightContext = js_EmptySubString;
-    if (res->moreParens) {
-      cx->free(res->moreParens);
-      res->moreParens = NULL;
-    }
-    cx->runtime->gcPoke = JS_TRUE;
+    cx->regExpStatics.clear();
 }
 
 JS_PUBLIC_API(void)
 JS_ClearRegExpRoots(JSContext *cx)
 {
-    JSRegExpStatics *res;
-
     /* No locking required, cx is thread-private and input must be live. */
-    res = &cx->regExpStatics;
-    res->input = NULL;
-    cx->runtime->gcPoke = JS_TRUE;
+    cx->regExpStatics.clearRoots();
 }
 
 /* TODO: compile, execute, get/set other statics... */
 
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -107,17 +107,17 @@ static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_OBJECT(jsval v)
 {
     return JSVAL_TAG(v) == JSVAL_OBJECT;
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_INT(jsval v)
 {
-    return v & JSVAL_INT;
+    return (JSBool)(v & JSVAL_INT);
 }
 
 static JS_ALWAYS_INLINE JSBool
 JSVAL_IS_DOUBLE(jsval v)
 {
     return JSVAL_TAG(v) == JSVAL_DOUBLE;
 }
 
@@ -265,17 +265,17 @@ static JS_ALWAYS_INLINE jsval
 BOOLEAN_TO_JSVAL(JSBool b)
 {
     JS_ASSERT(b == JS_TRUE || b == JS_FALSE);
     return SPECIAL_TO_JSVAL(b);
 }
 
 /* A private data pointer (2-byte-aligned) can be stored as an int jsval. */
 #define JSVAL_TO_PRIVATE(v)     ((void *)((v) & ~JSVAL_INT))
-#define PRIVATE_TO_JSVAL(p)     ((jsval)(p) | JSVAL_INT)
+#define PRIVATE_TO_JSVAL(p)     ((jsval)(ptrdiff_t)(p) | JSVAL_INT)
 
 /* Property attributes, set in JSPropertySpec and passed to API functions. */
 #define JSPROP_ENUMERATE        0x01    /* property is visible to for/in loop */
 #define JSPROP_READONLY         0x02    /* not settable: assignment is no-op */
 #define JSPROP_PERMANENT        0x04    /* property cannot be deleted */
 #define JSPROP_GETTER           0x10    /* property holds getter function */
 #define JSPROP_SETTER           0x20    /* property holds setter function */
 #define JSPROP_SHARED           0x40    /* don't allocate a value slot for this
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -37,25 +37,32 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
  * JS array class.
  *
  * Array objects begin as "dense" arrays, optimized for index-only property
- * access over a vector of slots (obj->dslots) with high load factor.  Array
- * methods optimize for denseness by testing that the object's class is
+ * access over a vector of slots with high load factor.  Array methods
+ * optimize for denseness by testing that the object's class is
  * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
  *
  * We track these pieces of metadata for arrays in dense mode:
- *  - the array's length property as a uint32, in JSSLOT_ARRAY_LENGTH,
- *  - the number of indices that are filled (non-holes), in JSSLOT_ARRAY_COUNT,
- *  - the net number of slots starting at dslots (capacity), in dslots[-1] if
- *    dslots is non-NULL.
+ *  - The array's length property as a uint32, accessible with
+ *    getArrayLength(), setDenseArrayLength().
+ *  - The number of indices that are filled (non-holes), accessible with
+ *    {get,set}DenseArrayCount().
+ *  - The number of element slots (capacity), gettable with
+ *    getDenseArrayCapacity().
+ *  - The minimum of length and capacity (minLenCap).  There are no explicit
+ *    setters, it's updated automatically by setDenseArrayLength() and
+ *    setDenseArrayCapacity().  There are also no explicit getters, the only
+ *    user is TraceRecorder which can access it directly because it's a
+ *    friend.
  *
  * In dense mode, holes in the array are represented by JSVAL_HOLE.  The final
  * slot in fslots is unused.
  *
  * NB: the capacity and length of a dense array are entirely unrelated!  The
  * length may be greater than, less than, or equal to the capacity.  See
  * array_length_setter for an explanation of how the first, most surprising
  * case may occur.
@@ -115,20 +122,20 @@ using namespace js;
 #define MIN_SPARSE_INDEX 256
 
 static inline bool
 INDEX_TOO_BIG(jsuint index)
 {
     return index > JS_BIT(29) - 1;
 }
 
-#define INDEX_TOO_SPARSE(array, index)                                         \
-    (INDEX_TOO_BIG(index) ||                                                   \
-     ((index) > js_DenseArrayCapacity(array) && (index) >= MIN_SPARSE_INDEX && \
-      (index) > ((array)->getArrayCount() + 1) * 4))
+#define INDEX_TOO_SPARSE(array, index)                                           \
+    (INDEX_TOO_BIG(index) ||                                                     \
+     ((index) > array->getDenseArrayCapacity() && (index) >= MIN_SPARSE_INDEX && \
+      (index) > ((array)->getDenseArrayCount() + 1) * 4))
 
 JS_STATIC_ASSERT(sizeof(JSScopeProperty) > 4 * sizeof(jsval));
 
 #define ENSURE_SLOW_ARRAY(cx, obj)                                             \
     (obj->getClass() == &js_SlowArrayClass || js_MakeArraySlow(cx, obj))
 
 /*
  * Determine if the id represents an array index or an XML property index.
@@ -310,106 +317,102 @@ BigIndexToId(JSContext *cx, JSObject *ob
         if (!atom)
             return JS_FALSE;
     }
 
     *idp = ATOM_TO_JSID(atom);
     return JS_TRUE;
 }
 
-static JSBool
-ResizeSlots(JSContext *cx, JSObject *obj, uint32 oldlen, uint32 newlen,
-            bool initializeAllSlots = true)
+bool
+JSObject::resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
+                                   bool initializeAllSlots)
 {
-    jsval *slots, *newslots;
-
-    if (newlen == 0) {
-        if (obj->dslots) {
-            cx->free(obj->dslots - 1);
-            obj->dslots = NULL;
-        }
+    JS_ASSERT(isDenseArray());
+
+    if (newcap == 0) {
+        freeDenseArrayElements(cx);
         return JS_TRUE;
     }
 
-    if (newlen > MAX_DSLOTS_LENGTH) {
+    if (newcap > MAX_DSLOTS_LENGTH) {
         js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 
-    slots = obj->dslots ? obj->dslots - 1 : NULL;
-    newslots = (jsval *) cx->realloc(slots, (size_t(newlen) + 1) * sizeof(jsval));
+    jsval *slots = dslots ? dslots - 1 : NULL;
+    jsval *newslots = (jsval *) cx->realloc(slots, (size_t(newcap) + 1) * sizeof(jsval));
     if (!newslots)
-        return JS_FALSE;
-
-    obj->dslots = newslots + 1;
-    js_SetDenseArrayCapacity(obj, newlen);
+        return false;
+
+    dslots = newslots + 1;
+    setDenseArrayCapacity(newcap);
 
     if (initializeAllSlots) {
-        for (slots = obj->dslots + oldlen; slots < obj->dslots + newlen; slots++)
-            *slots = JSVAL_HOLE;
+        for (uint32 i = oldcap; i < newcap; i++)
+            setDenseArrayElement(i, JSVAL_HOLE);
     }
 
-    return JS_TRUE;
+    return true;
 }
 
-/*
- * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to grow,
- * double its capacity, to push() N elements in amortized O(N) time.
- *
- * Above this limit, grow by 12.5% each time. Speed is still amortized O(N),
- * with a higher constant factor, and we waste less space.
- */
-#define CAPACITY_DOUBLING_MAX    (1024 * 1024)
-
-/*
- * Round up all large allocations to a multiple of this (1MB), so as not to
- * waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
- */
-#define CAPACITY_CHUNK  (1024 * 1024 / sizeof(jsval))
-
-static JSBool
-EnsureCapacity(JSContext *cx, JSObject *obj, uint32 newcap,
-               bool initializeAllSlots = true)
+bool
+JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap, bool initializeAllSlots)
 {
-    uint32 oldcap = js_DenseArrayCapacity(obj);
+    /*
+     * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to
+     * grow, double its capacity, to push() N elements in amortized O(N) time.
+     *
+     * Above this limit, grow by 12.5% each time. Speed is still amortized
+     * O(N), with a higher constant factor, and we waste less space.
+     */
+    static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
+
+    /*
+     * Round up all large allocations to a multiple of this (1MB), so as not
+     * to waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
+     */
+    static const size_t CAPACITY_CHUNK = 1024 * 1024 / sizeof(jsval);
+
+    uint32 oldcap = getDenseArrayCapacity();
 
     if (newcap > oldcap) {
         /*
          * If this overflows uint32, newcap is very large. nextsize will end
          * up being less than newcap, the code below will thus disregard it,
-         * and ResizeSlots will fail.
+         * and resizeDenseArrayElements() will fail.
          *
          * The way we use dslots[-1] forces a few +1s and -1s here. For
          * example, (oldcap * 2 + 1) produces the sequence 7, 15, 31, 63, ...
          * which makes the total allocation size (with dslots[-1]) a power
          * of two.
          */
         uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
                           ? oldcap * 2 + 1
                           : oldcap + (oldcap >> 3);
 
         uint32 actualCapacity = JS_MAX(newcap, nextsize);
         if (actualCapacity >= CAPACITY_CHUNK)
             actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */
         else if (actualCapacity < ARRAY_CAPACITY_MIN)
             actualCapacity = ARRAY_CAPACITY_MIN;
-        if (!ResizeSlots(cx, obj, oldcap, actualCapacity, initializeAllSlots))
-            return JS_FALSE;
+
+        if (!resizeDenseArrayElements(cx, oldcap, actualCapacity, initializeAllSlots))
+            return false;
+
         if (!initializeAllSlots) {
             /*
              * Initialize the slots caller didn't actually ask for.
              */
-            for (jsval *slots = obj->dslots + newcap;
-                 slots < obj->dslots + actualCapacity;
-                 slots++) {
-                *slots = JSVAL_HOLE;
+            for (uint32 i = newcap; i < actualCapacity; i++) {
+                setDenseArrayElement(i, JSVAL_HOLE);
             }
         }
     }
-    return JS_TRUE;
+    return true;
 }
 
 static bool
 ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
 {
     AutoValueRooter dval(cx);
     if (!js_NewDoubleInRootedValue(cx, index, dval.addr()) ||
         !js_ValueToStringId(cx, dval.value(), idp)) {
@@ -444,18 +447,19 @@ IndexToId(JSContext* cx, JSObject* obj, 
  * to JSVAL_VOID. This function assumes that the location pointed by vp is
  * properly rooted and can be used as GC-protected storage for temporaries.
  */
 static JSBool
 GetArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool *hole,
                 jsval *vp)
 {
     JS_ASSERT(index >= 0);
-    if (obj->isDenseArray() && index < js_DenseArrayCapacity(obj) &&
-        (*vp = obj->dslots[jsuint(index)]) != JSVAL_HOLE) {
+    if (obj->isDenseArray() && index < obj->getDenseArrayCapacity() &&
+        (*vp = obj->getDenseArrayElement(jsuint(index))) != JSVAL_HOLE) {
+
         *hole = JS_FALSE;
         return JS_TRUE;
     }
 
     AutoIdRooter idr(cx);
 
     *hole = JS_FALSE;
     if (!IndexToId(cx, obj, index, hole, idr.addr()))
@@ -490,23 +494,23 @@ SetArrayElement(JSContext *cx, JSObject 
     JS_ASSERT(index >= 0);
 
     if (obj->isDenseArray()) {
         /* Predicted/prefetched code should favor the remains-dense case. */
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (!INDEX_TOO_SPARSE(obj, idx)) {
                 JS_ASSERT(idx + 1 > idx);
-                if (!EnsureCapacity(cx, obj, idx + 1))
+                if (!obj->ensureDenseArrayElements(cx, idx + 1))
                     return JS_FALSE;
                 if (idx >= obj->getArrayLength())
-                    obj->setArrayLength(idx + 1);
-                if (obj->dslots[idx] == JSVAL_HOLE)
-                    obj->incArrayCountBy(1);
-                obj->dslots[idx] = v;
+                    obj->setDenseArrayLength(idx + 1);
+                if (obj->getDenseArrayElement(idx) == JSVAL_HOLE)
+                    obj->incDenseArrayCountBy(1);
+                obj->setDenseArrayElement(idx, v);
                 return JS_TRUE;
             }
         }
 
         if (!js_MakeArraySlow(cx, obj))
             return JS_FALSE;
     }
 
@@ -521,20 +525,20 @@ SetArrayElement(JSContext *cx, JSObject 
 
 static JSBool
 DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
 {
     JS_ASSERT(index >= 0);
     if (obj->isDenseArray()) {
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
-            if (!INDEX_TOO_SPARSE(obj, idx) && idx < js_DenseArrayCapacity(obj)) {
-                if (obj->dslots[idx] != JSVAL_HOLE)
-                    obj->decArrayCountBy(1);
-                obj->dslots[idx] = JSVAL_HOLE;
+            if (!INDEX_TOO_SPARSE(obj, idx) && idx < obj->getDenseArrayCapacity()) {
+                if (obj->getDenseArrayElement(idx) != JSVAL_HOLE)
+                    obj->decDenseArrayCountBy(1);
+                obj->setDenseArrayElement(idx, JSVAL_HOLE);
                 return JS_TRUE;
             }
         }
         return JS_TRUE;
     }
 
     AutoIdRooter idr(cx);
 
@@ -643,31 +647,36 @@ array_length_setter(JSContext *cx, JSObj
 
     if (oldlen == newlen)
         return true;
 
     if (!IndexToValue(cx, newlen, vp))
         return false;
 
     if (oldlen < newlen) {
-        obj->setArrayLength(newlen);
+        if (obj->isDenseArray())
+            obj->setDenseArrayLength(newlen);
+        else
+            obj->setSlowArrayLength(newlen);
         return true;
     }
 
     if (obj->isDenseArray()) {
         /* Don't reallocate if we're not actually shrinking our slots. */
-        jsuint capacity = js_DenseArrayCapacity(obj);
-        if (capacity > newlen && !ResizeSlots(cx, obj, capacity, newlen))
+        jsuint capacity = obj->getDenseArrayCapacity();
+        if (capacity > newlen && !obj->resizeDenseArrayElements(cx, capacity, newlen))
             return false;
+        obj->setDenseArrayLength(newlen);
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, oldlen))
                 return false;
         } while (oldlen != newlen);
+        obj->setSlowArrayLength(newlen);
     } else {
         /*
          * We are going to remove a lot of indexes in a presumably sparse
          * array. So instead of looping through indexes between newlen and
          * oldlen, we iterate through all properties and remove those that
          * correspond to indexes in the half-open range [newlen, oldlen).  See
          * bug 322135.
          */
@@ -684,37 +693,37 @@ array_length_setter(JSContext *cx, JSObj
                 return false;
             if (JSVAL_IS_VOID(id))
                 break;
             if (js_IdIsIndex(id, &index) && index - newlen < gap &&
                 !obj->deleteProperty(cx, id, &junk)) {
                 return false;
             }
         }
+        obj->setSlowArrayLength(newlen);
     }
 
-    obj->setArrayLength(newlen);
     return true;
 }
 
 /*
  * We have only indexed properties up to capacity (excepting holes), plus the
  * length property. For all else, we delegate to the prototype.
  */
 static inline bool
 IsDenseArrayId(JSContext *cx, JSObject *obj, jsid id)
 {
     JS_ASSERT(obj->isDenseArray());
 
     uint32 i;
     return id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom) ||
            (js_IdIsIndex(id, &i) &&
             obj->getArrayLength() != 0 &&
-            i < js_DenseArrayCapacity(obj) &&
-            obj->dslots[i] != JSVAL_HOLE);
+            i < obj->getDenseArrayCapacity() &&
+            obj->getDenseArrayElement(i) != JSVAL_HOLE);
 }
 
 static JSBool
 array_lookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                      JSProperty **propp)
 {
     if (!obj->isDenseArray())
         return js_LookupProperty(cx, obj, id, objp, propp);
@@ -747,17 +756,17 @@ js_GetDenseArrayElementValue(JSContext *
     jsid id = (jsid) prop;
     JS_ASSERT(IsDenseArrayId(cx, obj, id));
 
     uint32 i;
     if (!js_IdIsIndex(id, &i)) {
         JS_ASSERT(id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom));
         return IndexToValue(cx, obj->getArrayLength(), vp);
     }
-    *vp = obj->dslots[i];
+    *vp = obj->getDenseArrayElement(i);
     return JS_TRUE;
 }
 
 static JSBool
 array_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     uint32 i;
 
@@ -767,18 +776,18 @@ array_getProperty(JSContext *cx, JSObjec
     if (id == ATOM_TO_JSID(cx->runtime->atomState.protoAtom)) {
         *vp = OBJECT_TO_JSVAL(obj->getProto());
         return JS_TRUE;
     }
 
     if (!obj->isDenseArray())
         return js_GetProperty(cx, obj, id, vp);
 
-    if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= js_DenseArrayCapacity(obj) ||
-        obj->dslots[i] == JSVAL_HOLE) {
+    if (!js_IdIsIndex(ID_TO_VALUE(id), &i) || i >= obj->getDenseArrayCapacity() ||
+        obj->getDenseArrayElement(i) == JSVAL_HOLE) {
         JSObject *obj2;
         JSProperty *prop;
         JSScopeProperty *sprop;
 
         JSObject *proto = obj->getProto();
         if (!proto) {
             *vp = JSVAL_VOID;
             return JS_TRUE;
@@ -795,30 +804,30 @@ array_getProperty(JSContext *cx, JSObjec
                 if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_METHOD_BARRIER, vp))
                     return JS_FALSE;
             }
             obj2->dropProperty(cx, prop);
         }
         return JS_TRUE;
     }
 
-    *vp = obj->dslots[i];
+    *vp = obj->getDenseArrayElement(i);
     return JS_TRUE;
 }
 
 static JSBool
 slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
     jsuint index, length;
 
     if (!js_IdIsIndex(id, &index))
         return JS_TRUE;
     length = obj->getArrayLength();
     if (index >= length)
-        obj->setArrayLength(index + 1);
+        obj->setSlowArrayLength(index + 1);
     return JS_TRUE;
 }
 
 static JSBool
 slowarray_enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
                     jsval *statep, jsid *idp);
 
 static JSType
@@ -859,24 +868,24 @@ array_setProperty(JSContext *cx, JSObjec
         return js_SetProperty(cx, obj, id, vp);
 
     if (!js_IdIsIndex(id, &i) || INDEX_TOO_SPARSE(obj, i)) {
         if (!js_MakeArraySlow(cx, obj))
             return JS_FALSE;
         return js_SetProperty(cx, obj, id, vp);
     }
 
-    if (!EnsureCapacity(cx, obj, i + 1))
+    if (!obj->ensureDenseArrayElements(cx, i + 1))
         return JS_FALSE;
 
     if (i >= obj->getArrayLength())
-        obj->setArrayLength(i + 1);
-    if (obj->dslots[i] == JSVAL_HOLE)
-        obj->incArrayCountBy(1);
-    obj->dslots[i] = *vp;
+        obj->setDenseArrayLength(i + 1);
+    if (obj->getDenseArrayElement(i) == JSVAL_HOLE)
+        obj->incDenseArrayCountBy(1);
+    obj->setDenseArrayElement(i, *vp);
     return JS_TRUE;
 }
 
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
 {
     /*
      * Walk up the prototype chain and see if this indexed element already
@@ -914,30 +923,30 @@ dense_grow(JSContext* cx, JSObject* obj,
         if (i < 0)
             return JS_FALSE;
     }
 
     /*
      * If needed, grow the array as long it remains dense, otherwise fall off trace.
      */
     jsuint u = jsuint(i);
-    jsuint capacity = js_DenseArrayCapacity(obj);
-    if ((u >= capacity) && (INDEX_TOO_SPARSE(obj, u) || !EnsureCapacity(cx, obj, u + 1)))
+    jsuint capacity = obj->getDenseArrayCapacity();
+    if ((u >= capacity) && (INDEX_TOO_SPARSE(obj, u) || !obj->ensureDenseArrayElements(cx, u + 1)))
         return JS_FALSE;
 
-    if (obj->dslots[u] == JSVAL_HOLE) {
+    if (obj->getDenseArrayElement(u) == JSVAL_HOLE) {
         if (js_PrototypeHasIndexedProperties(cx, obj))
             return JS_FALSE;
 
         if (u >= obj->getArrayLength())
-            obj->setArrayLength(u + 1);
-        obj->incArrayCountBy(1);
+            obj->setDenseArrayLength(u + 1);
+        obj->incDenseArrayCountBy(1);
     }
 
-    obj->dslots[u] = v;
+    obj->setDenseArrayElement(u, v);
     return JS_TRUE;
 }
 
 
 JSBool FASTCALL
 js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, jsval v)
 {
     JS_ASSERT(obj->isDenseArray());
@@ -1032,20 +1041,20 @@ array_deleteProperty(JSContext *cx, JSOb
     if (!obj->isDenseArray())
         return js_DeleteProperty(cx, obj, id, rval);
 
     if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
         *rval = JSVAL_FALSE;
         return JS_TRUE;
     }
 
-    if (js_IdIsIndex(id, &i) && i < js_DenseArrayCapacity(obj) &&
-        obj->dslots[i] != JSVAL_HOLE) {
-        obj->decArrayCountBy(1);
-        obj->dslots[i] = JSVAL_HOLE;
+    if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity() &&
+        obj->getDenseArrayElement(i) != JSVAL_HOLE) {
+        obj->decDenseArrayCountBy(1);
+        obj->setDenseArrayElement(i, JSVAL_HOLE);
     }
 
     *rval = JSVAL_TRUE;
     return JS_TRUE;
 }
 
 /*
  * JSObjectOps.enumerate implementation.
@@ -1113,22 +1122,22 @@ array_enumerate(JSContext *cx, JSObject 
                 jsval *statep, jsid *idp)
 {
     uint32 capacity, i;
     JSIndexIterState *ii;
 
     switch (enum_op) {
       case JSENUMERATE_INIT:
         JS_ASSERT(obj->isDenseArray());
-        capacity = js_DenseArrayCapacity(obj);
+        capacity = obj->getDenseArrayCapacity();
         if (idp)
-            *idp = INT_TO_JSVAL(obj->getArrayCount());
+            *idp = INT_TO_JSVAL(obj->getDenseArrayCount());
         ii = NULL;
         for (i = 0; i != capacity; ++i) {
-            if (obj->dslots[i] == JSVAL_HOLE) {
+            if (obj->getDenseArrayElement(i) == JSVAL_HOLE) {
                 if (!ii) {
                     ii = (JSIndexIterState *)
                          cx->malloc(offsetof(JSIndexIterState, holes) +
                                    JS_BITMAP_SIZE(capacity));
                     if (!ii)
                         return JS_FALSE;
                     ii->hasHoles = JS_TRUE;
                     memset(ii->holes, 0, JS_BITMAP_SIZE(capacity));
@@ -1209,36 +1218,34 @@ slowarray_enumerate(JSContext *cx, JSObj
     ok = js_Enumerate(cx, obj, enum_op, statep, idp);
     JS_ASSERT(*statep == JSVAL_NULL || (*statep & INDEX_ITER_TAG) == JSVAL_INT);
     return ok;
 }
 
 static void
 array_finalize(JSContext *cx, JSObject *obj)
 {
-    if (obj->dslots)
-        cx->free(obj->dslots - 1);
-    obj->dslots = NULL;
+    obj->freeDenseArrayElements(cx);
 }
 
 static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
     uint32 capacity;
     size_t i;
     jsval v;
 
     JS_ASSERT(obj->isDenseArray());
     obj->traceProtoAndParent(trc);
 
-    capacity = js_DenseArrayCapacity(obj);
+    capacity = obj->getDenseArrayCapacity();
     for (i = 0; i < capacity; i++) {
-        v = obj->dslots[i];
+        v = obj->getDenseArrayElement(i);
         if (JSVAL_IS_TRACEABLE(v)) {
-            JS_SET_TRACING_INDEX(trc, "array_dslots", i);
+            JS_SET_TRACING_INDEX(trc, "dense_array_elems", i);
             js_CallGCMarker(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
         }
     }
 }
 
 extern JSObjectOps js_ArrayObjectOps;
 
 static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS);
@@ -1261,17 +1268,18 @@ array_getObjectOps(JSContext *cx, JSClas
 {
     return &js_ArrayObjectOps;
 }
 
 JSClass js_ArrayClass = {
     "Array",
     JSCLASS_HAS_RESERVED_SLOTS(2) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Array) |
-    JSCLASS_NEW_ENUMERATE,
+    JSCLASS_NEW_ENUMERATE |
+    JSCLASS_CONSTRUCT_PROTOTYPE,
     JS_PropertyStub,    JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
     JS_EnumerateStub,   JS_ResolveStub,    js_TryValueOf,     array_finalize,
     array_getObjectOps, NULL,              NULL,              NULL,
     NULL,               NULL,              NULL,              NULL
 };
 
 JSClass js_SlowArrayClass = {
     "Array",
@@ -1284,17 +1292,17 @@ JSClass js_SlowArrayClass = {
 };
 
 /*
  * Convert an array object from fast-and-dense to slow-and-flexible.
  */
 JSBool
 js_MakeArraySlow(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(obj->getClass() == &js_ArrayClass);
+    JS_ASSERT(obj->isDenseArray());
 
     /*
      * Create a native scope. All slow arrays other than Array.prototype get
      * the same initial shape.
      */
     uint32 emptyShape;
     JSObject *arrayProto = obj->getProto();
     if (arrayProto->getClass() == &js_ObjectClass) {
@@ -1305,51 +1313,53 @@ js_MakeArraySlow(JSContext *cx, JSObject
         JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass);
         emptyShape = arrayProto->scope()->emptyScope->shape;
     }
     JSScope *scope = JSScope::create(cx, &js_SlowArrayObjectOps, &js_SlowArrayClass, obj,
                                      emptyShape);
     if (!scope)
         return JS_FALSE;
 
-    uint32 capacity = js_DenseArrayCapacity(obj);
+    uint32 capacity = obj->getDenseArrayCapacity();
     if (capacity) {
         scope->freeslot = obj->numSlots() + JS_INITIAL_NSLOTS;
-        obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity;
+        // XXX: changing the capacity like this is awful.  Bug 558263 will remove
+        // the need for this.
+        obj->setDenseArrayCapacity(JS_INITIAL_NSLOTS + capacity);
     } else {
         scope->freeslot = obj->numSlots();
     }
 
-    /* Create new properties pointing to existing values in dslots */
+    /* Create new properties pointing to existing elements. */
     for (uint32 i = 0; i < capacity; i++) {
         jsid id;
         JSScopeProperty *sprop;
 
         if (!JS_ValueToId(cx, INT_TO_JSVAL(i), &id))
             goto out_bad;
 
-        if (obj->dslots[i] == JSVAL_HOLE) {
-            obj->dslots[i] = JSVAL_VOID;
+        if (obj->getDenseArrayElement(i) == JSVAL_HOLE) {
+            obj->setDenseArrayElement(i, JSVAL_VOID);
             continue;
         }
 
         sprop = scope->addDataProperty(cx, id, JS_INITIAL_NSLOTS + i,
                                        JSPROP_ENUMERATE);
         if (!sprop)
             goto out_bad;
     }
 
     /*
-     * Render our formerly-reserved count property GC-safe.  We do not need to
-     * make the length slot GC-safe because it is the private slot (this is
-     * statically asserted within JSObject) where the implementation can store
-     * an arbitrary value.
+     * Render our formerly-reserved non-private properties GC-safe.  We do not
+     * need to make the length slot GC-safe because it is the private slot
+     * (this is statically asserted within JSObject) where the implementation
+     * can store an arbitrary value.
      */
     JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE);
-    obj->voidDenseArrayCount();
+    obj->voidDenseOnlyArraySlots();
 
     /* Make sure we preserve any flags borrowing bits in classword. */
     obj->classword ^= (jsuword) &js_ArrayClass;
     obj->classword |= (jsuword) &js_SlowArrayClass;
 
     obj->map = scope;
     return JS_TRUE;
 
@@ -1649,44 +1659,44 @@ InitArrayElements(JSContext *cx, JSObjec
                     JS_ASSERT(!prop);
                 }
             }
         }
 #endif
 
         jsuint newlen = start + count;
         JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
-        if (!EnsureCapacity(cx, obj, newlen))
+        if (!obj->ensureDenseArrayElements(cx, newlen))
             return JS_FALSE;
 
         if (newlen > obj->getArrayLength())
-            obj->setArrayLength(newlen);
+            obj->setDenseArrayLength(newlen);
 
         JS_ASSERT(count < size_t(-1) / sizeof(jsval));
         if (targetType == TargetElementsMayContainValues) {
             jsuint valueCount = 0;
             for (jsuint i = 0; i < count; i++) {
-                 if (obj->dslots[start + i] != JSVAL_HOLE)
+                 if (obj->getDenseArrayElement(start + i) != JSVAL_HOLE)
                      valueCount++;
             }
-            JS_ASSERT(obj->getArrayCount() >= valueCount);
-            obj->decArrayCountBy(valueCount);
+            JS_ASSERT(obj->getDenseArrayCount() >= valueCount);
+            obj->decDenseArrayCountBy(valueCount);
         }
-        memcpy(obj->dslots + start, vector, sizeof(jsval) * count);
+        memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count);
         if (vectorType == SourceVectorAllValues) {
-            obj->incArrayCountBy(count);
+            obj->incDenseArrayCountBy(count);
         } else {
             jsuint valueCount = 0;
             for (jsuint i = 0; i < count; i++) {
-                 if (obj->dslots[start + i] != JSVAL_HOLE)
+                 if (obj->getDenseArrayElement(start + i) != JSVAL_HOLE)
                      valueCount++;
             }
-            obj->incArrayCountBy(valueCount);
+            obj->incDenseArrayCountBy(valueCount);
         }
-        JS_ASSERT_IF(count != 0, obj->dslots[newlen - 1] != JSVAL_HOLE);
+        JS_ASSERT_IF(count != 0, obj->getDenseArrayElement(newlen - 1) != JSVAL_HOLE);
         return JS_TRUE;
     }
 
     jsval* end = vector + count;
     while (vector != end && start < MAXINDEX) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !SetArrayElement(cx, obj, start++, *vector++)) {
             return JS_FALSE;
@@ -1721,63 +1731,44 @@ InitArrayElements(JSContext *cx, JSObjec
 }
 
 static JSBool
 InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const jsval *vector,
                 bool holey = false)
 {
     JS_ASSERT(obj->isArray());
 
-    obj->setArrayLength(length);
-
     if (vector) {
-        if (!EnsureCapacity(cx, obj, length))
+        JS_ASSERT(obj->isDenseArray());
+        obj->setDenseArrayLength(length);
+        if (!obj->ensureDenseArrayElements(cx, length))
             return JS_FALSE;
 
         jsuint count = length;
         if (!holey) {
-            memcpy(obj->dslots, vector, length * sizeof (jsval));
+            memcpy(obj->getDenseArrayElements(), vector, length * sizeof (jsval));
         } else {
             for (jsuint i = 0; i < length; i++) {
                 if (vector[i] == JSVAL_HOLE)
                     --count;
-                obj->dslots[i] = vector[i];
+                obj->setDenseArrayElement(i, vector[i]);
             }
         }
-        obj->setArrayCount(count);
+        obj->setDenseArrayCount(count);
     } else {
-        obj->setArrayCount(0);
+        if (obj->isDenseArray()) {
+            obj->setDenseArrayLength(length);
+            obj->setDenseArrayCount(0);
+        } else {
+            obj->setSlowArrayLength(length);
+        }
     }
     return JS_TRUE;
 }
 
-#ifdef JS_TRACER
-static JSString* FASTCALL
-Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
-{
-    AutoValueRooter tvr(cx);
-    if (!array_toString_sub(cx, obj, JS_FALSE, str, tvr.addr())) {
-        SetBuiltinError(cx);
-        return NULL;
-    }
-    return JSVAL_TO_STRING(tvr.value());
-}
-
-static JSString* FASTCALL
-Array_p_toString(JSContext* cx, JSObject* obj)
-{
-    AutoValueRooter tvr(cx);
-    if (!array_toString_sub(cx, obj, JS_FALSE, NULL, tvr.addr())) {
-        SetBuiltinError(cx);
-        return NULL;
-    }
-    return JSVAL_TO_STRING(tvr.value());
-}
-#endif
-
 /*
  * Perl-inspired join, reverse, and sort.
  */
 static JSBool
 array_join(JSContext *cx, uintN argc, jsval *vp)
 {
     JSString *str;
     JSObject *obj;
@@ -1800,37 +1791,36 @@ array_reverse(JSContext *cx, uintN argc,
     jsuint len;
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     if (!obj || !js_GetLengthProperty(cx, obj, &len))
         return JS_FALSE;
     *vp = OBJECT_TO_JSVAL(obj);
 
     if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
         /* An empty array or an array with no elements is already reversed. */
-        if (len == 0 || !obj->dslots)
+        if (len == 0 || obj->getDenseArrayCapacity() == 0)
             return JS_TRUE;
 
         /*
          * It's actually surprisingly complicated to reverse an array due to the
          * orthogonality of array length and array capacity while handling
          * leading and trailing holes correctly.  Reversing seems less likely to
          * be a common operation than other array mass-mutation methods, so for
          * now just take a probably-small memory hit (in the absence of too many
          * holes in the array at its start) and ensure that the capacity is
          * sufficient to hold all the elements in the array if it were full.
          */
-        if (!EnsureCapacity(cx, obj, len))
+        if (!obj->ensureDenseArrayElements(cx, len))
             return JS_FALSE;
 
-        jsval* lo = &obj->dslots[0];
-        jsval* hi = &obj->dslots[len - 1];
+        uint32 lo = 0, hi = len - 1;
         for (; lo < hi; lo++, hi--) {
-             jsval tmp = *lo;
-             *lo = *hi;
-             *hi = tmp;
+            jsval tmp = obj->getDenseArrayElement(lo);
+            obj->setDenseArrayElement(lo, obj->getDenseArrayElement(hi));
+            obj->setDenseArrayElement(hi, tmp);
         }
 
         /*
          * Per ECMA-262, don't update the length of the array, even if the new
          * array has trailing holes (and thus the original array began with
          * holes).
          */
         return JS_TRUE;
@@ -2352,66 +2342,51 @@ array_push1_dense(JSContext* cx, JSObjec
 {
     uint32 length = obj->getArrayLength();
     if (INDEX_TOO_SPARSE(obj, length)) {
         if (!js_MakeArraySlow(cx, obj))
             return JS_FALSE;
         return array_push_slowly(cx, obj, 1, &v, rval);
     }
 
-    if (!EnsureCapacity(cx, obj, length + 1))
+    if (!obj->ensureDenseArrayElements(cx, length + 1))
         return JS_FALSE;
-    obj->setArrayLength(length + 1);
-
-    JS_ASSERT(obj->dslots[length] == JSVAL_HOLE);
-    obj->incArrayCountBy(1);
-    obj->dslots[length] = v;
+    obj->setDenseArrayLength(length + 1);
+
+    JS_ASSERT(obj->getDenseArrayElement(length) == JSVAL_HOLE);
+    obj->incDenseArrayCountBy(1);
+    obj->setDenseArrayElement(length, v);
     return IndexToValue(cx, obj->getArrayLength(), rval);
 }
 
 JSBool JS_FASTCALL
 js_ArrayCompPush(JSContext *cx, JSObject *obj, jsval v)
 {
     JS_ASSERT(obj->isDenseArray());
     uint32_t length = obj->getArrayLength();
-    JS_ASSERT(length <= js_DenseArrayCapacity(obj));
-
-    if (length == js_DenseArrayCapacity(obj)) {
+    JS_ASSERT(length <= obj->getDenseArrayCapacity());
+
+    if (length == obj->getDenseArrayCapacity()) {
         if (length > JS_ARGS_LENGTH_MAX) {
             JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
                                    JSMSG_ARRAY_INIT_TOO_BIG);
             return JS_FALSE;
         }
 
-        if (!EnsureCapacity(cx, obj, length + 1))
+        if (!obj->ensureDenseArrayElements(cx, length + 1))
             return JS_FALSE;
     }
-    obj->setArrayLength(length + 1);
-    obj->incArrayCountBy(1);
-    obj->dslots[length] = v;
+    obj->setDenseArrayLength(length + 1);
+    obj->incDenseArrayCountBy(1);
+    obj->setDenseArrayElement(length, v);
     return JS_TRUE;
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_ArrayCompPush, CONTEXT, OBJECT, JSVAL, 0,
                      nanojit::ACC_STORE_ANY)
 
-#ifdef JS_TRACER
-static jsval FASTCALL
-Array_p_push1(JSContext* cx, JSObject* obj, jsval v)
-{
-    AutoValueRooter tvr(cx, v);
-    if (obj->isDenseArray()
-        ? array_push1_dense(cx, obj, v, tvr.addr())
-        : array_push_slowly(cx, obj, 1, tvr.addr(), tvr.addr())) {
-        return tvr.value();
-    }
-    SetBuiltinError(cx);
-    return JSVAL_VOID;
-}
-#endif
-
 static JSBool
 array_push(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
 
     /* Insist on one argument and obj of the expected class. */
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
@@ -2455,35 +2430,20 @@ array_pop_dense(JSContext *cx, JSObject*
         *vp = JSVAL_VOID;
         return JS_TRUE;
     }
     index--;
     if (!GetArrayElement(cx, obj, index, &hole, vp))
         return JS_FALSE;
     if (!hole && !DeleteArrayElement(cx, obj, index))
         return JS_FALSE;
-    obj->setArrayLength(index);
+    obj->setDenseArrayLength(index);
     return JS_TRUE;
 }
 
-#ifdef JS_TRACER
-static jsval FASTCALL
-Array_p_pop(JSContext* cx, JSObject* obj)
-{
-    AutoValueRooter tvr(cx);
-    if (obj->isDenseArray()
-        ? array_pop_dense(cx, obj, tvr.addr())
-        : array_pop_slowly(cx, obj, tvr.addr())) {
-        return tvr.value();
-    }
-    SetBuiltinError(cx);
-    return JSVAL_VOID;
-}
-#endif
-
 static JSBool
 array_pop(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
 
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
         return JS_FALSE;
@@ -2504,25 +2464,26 @@ array_shift(JSContext *cx, uintN argc, j
         return JS_FALSE;
 
     if (length == 0) {
         *vp = JSVAL_VOID;
     } else {
         length--;
 
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
-            length < js_DenseArrayCapacity(obj)) {
-            *vp = obj->dslots[0];
+            length < obj->getDenseArrayCapacity()) {
+            *vp = obj->getDenseArrayElement(0);
             if (*vp == JSVAL_HOLE)
                 *vp = JSVAL_VOID;
             else
-                obj->decArrayCountBy(1);
-            memmove(obj->dslots, obj->dslots + 1, length * sizeof(jsval));
-            obj->dslots[length] = JSVAL_HOLE;
-            obj->setArrayLength(length);
+                obj->decDenseArrayCountBy(1);
+            jsval *elems = obj->getDenseArrayElements();
+            memmove(elems, elems + 1, length * sizeof(jsval));
+            obj->setDenseArrayElement(length, JSVAL_HOLE);
+            obj->setDenseArrayLength(length);
             return JS_TRUE;
         }
 
         /* Get the to-be-deleted property's value into vp ASAP. */
         if (!GetArrayElement(cx, obj, 0, &hole, vp))
             return JS_FALSE;
 
         /* Slide down the array above the first element. */
@@ -2557,21 +2518,22 @@ array_unshift(JSContext *cx, uintN argc,
     newlen = length;
     if (argc > 0) {
         /* Slide up the array to make room for argc at the bottom. */
         argv = JS_ARGV(cx, vp);
         if (length > 0) {
             if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
                 !INDEX_TOO_SPARSE(obj, unsigned(newlen + argc))) {
                 JS_ASSERT(newlen + argc == length + argc);
-                if (!EnsureCapacity(cx, obj, length + argc))
+                if (!obj->ensureDenseArrayElements(cx, length + argc))
                     return JS_FALSE;
-                memmove(obj->dslots + argc, obj->dslots, length * sizeof(jsval));
+                jsval *elems = obj->getDenseArrayElements();
+                memmove(elems + argc, elems, length * sizeof(jsval));
                 for (uint32 i = 0; i < argc; i++)
-                    obj->dslots[i] = JSVAL_HOLE;
+                    obj->setDenseArrayElement(i, JSVAL_HOLE);
             } else {
                 last = length;
                 jsdouble upperIndex = last + argc;
                 AutoValueRooter tvr(cx);
                 do {
                     --last, --upperIndex;
                     if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                         !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
@@ -2659,19 +2621,19 @@ array_splice(JSContext *cx, uintN argc, 
     }
 
     AutoValueRooter tvr(cx, JSVAL_NULL);
 
     /* If there are elements to remove, put them into the return value. */
     if (count > 0) {
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             !js_PrototypeHasIndexedProperties(cx, obj2) &&
-            end <= js_DenseArrayCapacity(obj)) {
-            if (!InitArrayObject(cx, obj2, count, obj->dslots + begin,
-                                 obj->getArrayCount() != obj->getArrayLength())) {
+            end <= obj->getDenseArrayCapacity()) {
+            if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin,
+                                 obj->getDenseArrayCount() != obj->getArrayLength())) {
                 return JS_FALSE;
             }
         } else {
             for (last = begin; last < end; last++) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr())) {
                     return JS_FALSE;
                 }
@@ -2686,51 +2648,51 @@ array_splice(JSContext *cx, uintN argc, 
         }
     }
 
     /* Find the direction (up or down) to copy and make way for argv. */
     if (argc > count) {
         delta = (jsuint)argc - count;
         last = length;
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
-            length <= js_DenseArrayCapacity(obj) &&
-            (length == 0 || obj->dslots[length - 1] != JSVAL_HOLE)) {
-            if (!EnsureCapacity(cx, obj, length + delta))
+            length <= obj->getDenseArrayCapacity() &&
+            (length == 0 || obj->getDenseArrayElement(length - 1) != JSVAL_HOLE)) {
+            if (!obj->ensureDenseArrayElements(cx, length + delta))
                 return JS_FALSE;
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             while (last-- > end) {
-                jsval srcval = obj->dslots[last];
-                jsval* dest = &obj->dslots[last + delta];
-                if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
-                    obj->incArrayCountBy(1);
-                *dest = srcval;
+                jsval srcval = obj->getDenseArrayElement(last);
+                jsval dest = obj->getDenseArrayElement(last + delta);
+                if (dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
+                    obj->incDenseArrayCountBy(1);
+                obj->setDenseArrayElement(last + delta, srcval);
             }
-            obj->setArrayLength(obj->getArrayLength() + delta);
+            obj->setDenseArrayLength(obj->getArrayLength() + delta);
         } else {
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             while (last-- > end) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) {
                     return JS_FALSE;
                 }
             }
         }
         length += delta;
     } else if (argc < count) {
         delta = count - (jsuint)argc;
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
-            length <= js_DenseArrayCapacity(obj)) {
+            length <= obj->getDenseArrayCapacity()) {
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             for (last = end; last < length; last++) {
-                jsval srcval = obj->dslots[last];
-                jsval* dest = &obj->dslots[last - delta];
-                if (*dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
-                    obj->incArrayCountBy(1);
-                *dest = srcval;
+                jsval srcval = obj->getDenseArrayElement(last);
+                jsval dest = obj->getDenseArrayElement(last - delta);
+                if (dest == JSVAL_HOLE && srcval != JSVAL_HOLE)
+                    obj->incDenseArrayCountBy(1);
+                obj->setDenseArrayElement(last - delta, srcval);
             }
         } else {
             for (last = end; last < length; last++) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) {
                     return JS_FALSE;
                 }
@@ -2771,22 +2733,22 @@ array_concat(JSContext *cx, uintN argc, 
          * Clone aobj but pass the minimum of its length and capacity, to
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
          * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which
          * will cause nobj to be over-allocated to 16. But in the normal case
          * where length is <= capacity, nobj and aobj will have the same
          * capacity.
          */
         length = aobj->getArrayLength();
-        jsuint capacity = js_DenseArrayCapacity(aobj);
-        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->dslots,
-                                 aobj->getArrayCount() != length);
+        jsuint capacity = aobj->getDenseArrayCapacity();
+        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements(),
+                                 aobj->getDenseArrayCount() != length);
         if (!nobj)
             return JS_FALSE;
-        nobj->setArrayLength(length);
+        nobj->setDenseArrayLength(length);
         *vp = OBJECT_TO_JSVAL(nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         argv++;
     } else {
         nobj = js_NewArrayObject(cx, 0, NULL);
         if (!nobj)
@@ -2885,20 +2847,20 @@ array_slice(JSContext *cx, uintN argc, j
             }
             end = (jsuint)d;
         }
     }
 
     if (begin > end)
         begin = end;
 
-    if (obj->isDenseArray() && end <= js_DenseArrayCapacity(obj) &&
+    if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
-        nobj = js_NewArrayObject(cx, end - begin, obj->dslots + begin,
-                                 obj->getArrayCount() != obj->getArrayLength());
+        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin,
+                                 obj->getDenseArrayCount() != obj->getArrayLength());
         if (!nobj)
             return JS_FALSE;
         *vp = OBJECT_TO_JSVAL(nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
     nobj = js_NewArrayObject(cx, 0, NULL);
@@ -3252,38 +3214,29 @@ array_isArray(JSContext *cx, uintN argc,
 }
 
 static JSPropertySpec array_props[] = {
     {js_length_str,   -1,   JSPROP_SHARED | JSPROP_PERMANENT,
                             array_length_getter,    array_length_setter},
     {0,0,0,0,0}
 };
 
-JS_DEFINE_TRCINFO_1(array_toString,
-    (2, (static, STRING_FAIL, Array_p_toString, CONTEXT, THIS,      0, nanojit::ACC_STORE_ANY)))
-JS_DEFINE_TRCINFO_1(array_join,
-    (3, (static, STRING_FAIL, Array_p_join, CONTEXT, THIS, STRING,  0, nanojit::ACC_STORE_ANY)))
-JS_DEFINE_TRCINFO_1(array_push,
-    (3, (static, JSVAL_FAIL, Array_p_push1, CONTEXT, THIS, JSVAL,   0, nanojit::ACC_STORE_ANY)))
-JS_DEFINE_TRCINFO_1(array_pop,
-    (2, (static, JSVAL_FAIL, Array_p_pop, CONTEXT, THIS,            0, nanojit::ACC_STORE_ANY)))
-
 static JSFunctionSpec array_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,      array_toSource,     0,0),
 #endif
-    JS_TN(js_toString_str,      array_toString,     0,0, &array_toString_trcinfo),
+    JS_FN(js_toString_str,      array_toString,     0,0),
     JS_FN(js_toLocaleString_str,array_toLocaleString,0,0),
 
     /* Perl-ish methods. */
-    JS_TN("join",               array_join,         1,JSFUN_GENERIC_NATIVE, &array_join_trcinfo),
+    JS_FN("join",               array_join,         1,JSFUN_GENERIC_NATIVE),
     JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
     JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
-    JS_TN("push",               array_push,         1,JSFUN_GENERIC_NATIVE, &array_push_trcinfo),
-    JS_TN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE, &array_pop_trcinfo),
+    JS_FN("push",               array_push,         1,JSFUN_GENERIC_NATIVE),
+    JS_FN("pop",                array_pop,          0,JSFUN_GENERIC_NATIVE),
     JS_FN("shift",              array_shift,        0,JSFUN_GENERIC_NATIVE),
     JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
     JS_FN("splice",             array_splice,       2,JSFUN_GENERIC_NATIVE),
 
     /* Pythonic sequence methods. */
     JS_FN("concat",             array_concat,       1,JSFUN_GENERIC_NATIVE),
     JS_FN("slice",              array_slice,        2,JSFUN_GENERIC_NATIVE),
 
@@ -3345,54 +3298,50 @@ 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 *>(&SharedArrayMap);
-    obj->classword = jsuword(&js_ArrayClass);
-    obj->setProto(proto);
-    obj->setParent(proto->getParent());
-
-    obj->setArrayLength(0);
-    obj->setArrayCount(0);
-    obj->voidArrayUnused();
-    obj->dslots = NULL;
+
+    obj->init(&js_ArrayClass, proto, proto->getParent(), JSVAL_NULL);
+    obj->setDenseArrayLength(0);
+    obj->setDenseArrayCount(0);
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
 #endif
 
 JSObject* JS_FASTCALL
 js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len)
 {
     if (len < 0)
         return NULL;
     JSObject *obj = js_NewEmptyArray(cx, proto);
     if (!obj)
         return NULL;
-    obj->setArrayLength(len);
+    obj->setDenseArrayLength(len);
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArrayWithLength, CONTEXT, OBJECT, INT32, 0,
                      nanojit::ACC_STORE_ANY)
 #endif
 
 JSObject* JS_FASTCALL
 js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
 {
     JSObject* obj = js_NewEmptyArray(cx, proto);
     if (!obj)
         return NULL;
-    obj->setArrayLength(len);
-    if (!ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
+    obj->setDenseArrayLength(len);
+    if (!obj->resizeDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
         return NULL;
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArrayWithSlots, CONTEXT, OBJECT, UINT32, 0,
                      nanojit::ACC_STORE_ANY)
 #endif
 
@@ -3432,17 +3381,17 @@ js_NewArrayObject(JSContext *cx, jsuint 
     return obj;
 }
 
 JSObject *
 js_NewSlowArrayObject(JSContext *cx)
 {
     JSObject *obj = NewObject(cx, &js_SlowArrayClass, NULL, NULL);
     if (obj)
-        obj->setArrayLength(0);
+        obj->setSlowArrayLength(0);
     return obj;
 }
 
 #ifdef DEBUG_ARRAYS
 JSBool
 js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     uintN i;
@@ -3461,18 +3410,18 @@ js_ArrayInfo(JSContext *cx, JSObject *ob
             cx->free(bytes);
             continue;
         }
         fprintf(stderr, "%s: %s (len %lu", bytes,
                 array->isDenseArray()) ? "dense" : "sparse",
                 array->getArrayLength());
         if (array->isDenseArray()) {
             fprintf(stderr, ", count %lu, capacity %lu",
-                    array->getArrayCount(),
-                    js_DenseArrayCapacity(array));
+                    array->getDenseArrayCount(),
+                    array->getDenseArrayCapacity());
         }
         fputs(")\n", stderr);
         cx->free(bytes);
     }
     return JS_TRUE;
 }
 #endif
 
@@ -3486,17 +3435,17 @@ js_CoerceArrayToCanvasImageData(JSObject
         return JS_FALSE;
 
     length = obj->getArrayLength();
     if (length < offset + count)
         return JS_FALSE;
 
     JSUint8 *dp = dest;
     for (uintN i = offset; i < offset+count; i++) {
-        jsval v = obj->dslots[i];
+        jsval v = obj->getDenseArrayElement(i);
         if (JSVAL_IS_INT(v)) {
             jsint vi = JSVAL_TO_INT(v);
             if (jsuint(vi) > 255)
                 vi = (vi < 0) ? 0 : 255;
             *dp++ = JSUint8(vi);
         } else if (JSVAL_IS_DOUBLE(v)) {
             jsdouble vd = *JSVAL_TO_DOUBLE(v);
             if (!(vd >= 0)) /* Not < so that NaN coerces to 0 */
@@ -3537,26 +3486,26 @@ js_CoerceArrayToCanvasImageData(JSObject
 JS_FRIEND_API(JSObject *)
 js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector)
 {
     JSObject *obj = js_NewArrayObject(cx, capacity, NULL);
     if (!obj)
         return NULL;
 
     AutoValueRooter tvr(cx, obj);
-    if (!EnsureCapacity(cx, obj, capacity, JS_FALSE))
+    if (!obj->ensureDenseArrayElements(cx, capacity, JS_FALSE))
         obj = NULL;
 
     /* Set/clear newborn root, in case we lost it.  */
     cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
     if (!obj)
         return NULL;
 
-    obj->setArrayCount(capacity);
-    *vector = obj->dslots;
+    obj->setDenseArrayCount(capacity);
+    *vector = obj->getDenseArrayElements();
     return obj;
 }
 
 JS_FRIEND_API(JSBool)
 js_IsDensePrimitiveArray(JSObject *obj)
 {
     if (!obj || !obj->isDenseArray())
         return JS_FALSE;
@@ -3587,17 +3536,17 @@ js_CloneDensePrimitiveArray(JSContext *c
 
     /*
      * Must use the minimum of original array's length and capacity, to handle
      * |a = [1,2,3]; a.length = 10000| "dense" cases efficiently. In such a case
      * we would use ARRAY_CAPACITY_MIN (not 3), which will cause the clone to be
      * over-allocated. In the normal case where length is <= capacity the
      * clone and original array will have the same capacity.
      */
-    jsuint jsvalCount = JS_MIN(js_DenseArrayCapacity(obj), length);
+    jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length);
 
     js::AutoValueVector vector(cx);
     if (!vector.reserve(jsvalCount))
         return JS_FALSE;
 
     jsuint holeCount = 0;
 
     for (jsuint i = 0; i < jsvalCount; i++) {
@@ -3624,13 +3573,13 @@ js_CloneDensePrimitiveArray(JSContext *c
     jsval *buffer;
     *clone = js_NewArrayObjectWithCapacity(cx, jsvalCount, &buffer);
     if (!*clone)
         return JS_FALSE;
 
     AutoObjectRooter cloneRoot(cx, *clone);
 
     memcpy(buffer, vector.buffer(), jsvalCount * sizeof (jsval));
-    (*clone)->setArrayLength(length);
-    (*clone)->setArrayCount(length - holeCount);
+    (*clone)->setDenseArrayLength(length);
+    (*clone)->setDenseArrayCount(length - holeCount);
 
     return JS_TRUE;
 }
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -116,31 +116,16 @@ js_NewArrayObject(JSContext *cx, jsuint 
 
 /* Create an array object that starts out already made slow/sparse. */
 extern JSObject *
 js_NewSlowArrayObject(JSContext *cx);
 
 extern JSBool
 js_MakeArraySlow(JSContext *cx, JSObject *obj);
 
-static JS_INLINE uint32
-js_DenseArrayCapacity(JSObject *obj)
-{
-    JS_ASSERT(obj->isDenseArray());
-    return obj->dslots ? (uint32) obj->dslots[-1] : 0;
-}
-
-static JS_INLINE void
-js_SetDenseArrayCapacity(JSObject *obj, uint32 capacity)
-{
-    JS_ASSERT(obj->isDenseArray());
-    JS_ASSERT(obj->dslots);
-    obj->dslots[-1] = (jsval) capacity;
-}
-
 extern JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
 
 extern JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, jsdouble length);
 
 extern JSBool
 js_HasLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -97,44 +97,40 @@ js_imod(int32 a, int32 b)
 {
     if (a < 0 || b <= 0)
         return -1;
     int r = a % b;
     return r;
 }
 JS_DEFINE_CALLINFO_2(extern, INT32, js_imod, INT32, INT32, 1, ACC_NONE)
 
-/* The following boxing/unboxing primitives we can't emit inline because
-   they either interact with the GC and depend on Spidermonkey's 32-bit
-   integer representation. */
-
 jsval FASTCALL
 js_BoxDouble(JSContext* cx, jsdouble d)
 {
     int32 i;
     if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i))
         return INT_TO_JSVAL(i);
     JS_ASSERT(JS_ON_TRACE(cx));
     jsval v; /* not rooted but ok here because we know GC won't run */
     if (!js_NewDoubleInRootedValue(cx, d, &v))
-        return JSVAL_ERROR_COOKIE;
+        return JSVAL_NULL;
     return v;
 }
 JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxDouble, CONTEXT, DOUBLE, 1, ACC_NONE)
 
 jsval FASTCALL
 js_BoxInt32(JSContext* cx, int32 i)
 {
     if (JS_LIKELY(INT_FITS_IN_JSVAL(i)))
         return INT_TO_JSVAL(i);
     JS_ASSERT(JS_ON_TRACE(cx));
     jsval v; /* not rooted but ok here because we know GC won't run */
     jsdouble d = (jsdouble)i;
     if (!js_NewDoubleInRootedValue(cx, d, &v))
-        return JSVAL_ERROR_COOKIE;
+        return JSVAL_NULL;
     return v;
 }
 JS_DEFINE_CALLINFO_2(extern, JSVAL, js_BoxInt32, CONTEXT, INT32, 1, ACC_NONE)
 
 jsdouble FASTCALL
 js_UnboxDouble(jsval v)
 {
     if (JS_LIKELY(JSVAL_IS_INT(v)))
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -43,17 +43,17 @@
 #ifdef JS_TRACER
 
 #include "nanojit/nanojit.h"
 
 #ifdef THIS
 #undef THIS
 #endif
 
-enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_VOID, FAIL_COOKIE };
+enum JSTNErrType { INFALLIBLE, FAIL_STATUS, FAIL_NULL, FAIL_NEG, FAIL_VOID };
 enum { JSTN_ERRTYPE_MASK = 0x07, JSTN_UNBOX_AFTER = 0x08, JSTN_MORE = 0x10,
        JSTN_CONSTRUCTOR = 0x20 };
 
 #define JSTN_ERRTYPE(jstn)  ((jstn)->flags & JSTN_ERRTYPE_MASK)
 
 /*
  * Type describing a type specialization of a JSFastNative.
  *
@@ -96,24 +96,16 @@ struct JSSpecializedNative {
  * 'specializations' points to a static array of available specializations
  * terminated by the lack of having the JSTN_MORE flag set.
  */
 struct JSNativeTraceInfo {
     JSFastNative            native;
     JSSpecializedNative     *specializations;
 };
 
-/*
- * We use a magic boxed pointer value to represent error conditions that
- * trigger a side exit. The address is so low that it should never be actually
- * in use. If it is, a performance regression occurs, not an actual runtime
- * error.
- */
-#define JSVAL_ERROR_COOKIE OBJECT_TO_JSVAL((JSObject*)0x10)
-
 /* Macros used by JS_DEFINE_CALLINFOn. */
 #ifdef DEBUG
 #define _JS_CI_NAME(op) ,#op
 #else
 #define _JS_CI_NAME(op)
 #endif
 
 #define _JS_I32_ARGTYPE    nanojit::ARGTYPE_I
@@ -155,17 +147,16 @@ struct ClosureVarInfo;
  *
  *     _RETRY builtins indicate failure with a special return value that
  *     depends on the return type:
  *
  *         BOOL_RETRY: JSVAL_TO_BOOLEAN(JSVAL_VOID)
  *         INT32_RETRY: any negative value
  *         STRING_RETRY: NULL
  *         OBJECT_RETRY_NULL: NULL
- *         JSVAL_RETRY: JSVAL_ERROR_COOKIE
  *
  *     _RETRY function calls are faster than _FAIL calls.  Each _RETRY call
  *     saves two writes to cx->bailExit and a read from state->builtinStatus.
  *
  *   - All other traceable natives are infallible (e.g. Date.now, Math.log).
  *
  * Special builtins known to the tracer can have their own idiosyncratic
  * error codes.
@@ -189,17 +180,16 @@ struct ClosureVarInfo;
 #define _JS_CTYPE_THIS_DOUBLE       _JS_CTYPE(jsdouble,               _JS_F64,"D", "", INFALLIBLE)
 #define _JS_CTYPE_THIS_STRING       _JS_CTYPE(JSString *,             _JS_PTR,"S", "", INFALLIBLE)
 #define _JS_CTYPE_CALLEE            _JS_CTYPE(JSObject *,             _JS_PTR,"f","",  INFALLIBLE)
 #define _JS_CTYPE_CALLEE_PROTOTYPE  _JS_CTYPE(JSObject *,             _JS_PTR,"p","",  INFALLIBLE)
 #define _JS_CTYPE_FUNCTION          _JS_CTYPE(JSFunction *,           _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_PC                _JS_CTYPE(jsbytecode *,           _JS_PTR,"P", "", INFALLIBLE)
 #define _JS_CTYPE_JSVALPTR          _JS_CTYPE(jsval *,                _JS_PTR,"P", "", INFALLIBLE)
 #define _JS_CTYPE_JSVAL             _JS_JSVAL_CTYPE(                  _JS_PTR, "","v", INFALLIBLE)
-#define _JS_CTYPE_JSVAL_RETRY       _JS_JSVAL_CTYPE(                  _JS_PTR, --, --, FAIL_COOKIE)
 #define _JS_CTYPE_JSVAL_FAIL        _JS_JSVAL_CTYPE(                  _JS_PTR, --, --, FAIL_STATUS)
 #define _JS_CTYPE_JSID              _JS_CTYPE(jsid,                   _JS_PTR, --, --, INFALLIBLE)
 #define _JS_CTYPE_BOOL              _JS_CTYPE(JSBool,                 _JS_I32, "","i", INFALLIBLE)
 #define _JS_CTYPE_BOOL_RETRY        _JS_CTYPE(JSBool,                 _JS_I32, --, --, FAIL_VOID)
 #define _JS_CTYPE_BOOL_FAIL         _JS_CTYPE(JSBool,                 _JS_I32, --, --, FAIL_STATUS)
 #define _JS_CTYPE_INT32             _JS_CTYPE(int32,                  _JS_I32, "","i", INFALLIBLE)
 #define _JS_CTYPE_INT32_RETRY       _JS_CTYPE(int32,                  _JS_I32, --, --, FAIL_NEG)
 #define _JS_CTYPE_INT32_FAIL        _JS_CTYPE(int32,                  _JS_I32, --, --, FAIL_STATUS)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -175,16 +175,18 @@ JSThreadData::purge(JSContext *cx)
     if (cx->runtime->gcRegenShapes)
         traceMonitor.needFlush = JS_TRUE;
 #endif
 
     /* Destroy eval'ed scripts. */
     js_DestroyScriptsToGC(cx, this);
 
     js_PurgeCachedNativeEnumerators(cx, this);
+
+    dtoaCache.s = NULL;
 }
 
 void
 JSThreadData::purgeGCFreeLists()
 {
     if (!localRootStack) {
         gcFreeLists.purge();
     } else {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -329,16 +329,18 @@ class CallStack
 };
 
 /* Holds the number of recording attemps for an address. */
 typedef HashMap<jsbytecode*,
                 size_t,
                 DefaultHasher<jsbytecode*>,
                 SystemAllocPolicy> RecordAttemptMap;
 
+class Oracle;
+
 /*
  * Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not
  * JS_THREADSAFE) has an associated trace monitor that keeps track of loop
  * frequencies for all JavaScript code loaded into that runtime.
  */
 struct TraceMonitor {
     /*
      * The context currently executing JIT-compiled code on this thread, or
@@ -390,16 +392,17 @@ struct TraceMonitor {
     VMAllocator*            dataAlloc;
     VMAllocator*            traceAlloc;
     VMAllocator*            tempAlloc;
     VMAllocator*            reTempAlloc;
     nanojit::CodeAlloc*     codeAlloc;
     nanojit::Assembler*     assembler;
     FrameInfoCache*         frameCache;
 
+    Oracle*                 oracle;
     TraceRecorder*          recorder;
 
     GlobalState             globalStates[MONITOR_N_GLOBAL_STATES];
     TreeFragment*           vmfragments[FRAGMENT_TABLE_SIZE];
     RecordAttemptMap*       recordAttempts;
 
     /*
      * Maximum size of the code cache before we start flushing. 1/16 of this
@@ -548,16 +551,27 @@ struct JSThreadData {
 
 #ifdef JS_EVAL_CACHE_METERING
     JSEvalCacheMeter    evalCacheMeter;
 #endif
 
     /* State used by dtoa.c. */
     DtoaState           *dtoaState;
 
+    /* 
+     * State used to cache some double-to-string conversions.  A stupid
+     * optimization aimed directly at v8-splay.js, which stupidly converts
+     * many doubles multiple times in a row.
+     */
+    struct {
+        jsdouble d;
+        jsint    base;
+        JSString *s;        // if s==NULL, d and base are not valid
+    } dtoaCache;
+
     /*
      * Cache of reusable JSNativeEnumerators mapped by shape identifiers (as
      * stored in scope->shape). This cache is nulled by the GC and protected
      * by gcLock.
      */
 #define NATIVE_ENUM_CACHE_LOG2  8
 #define NATIVE_ENUM_CACHE_MASK  JS_BITMASK(NATIVE_ENUM_CACHE_LOG2)
 #define NATIVE_ENUM_CACHE_SIZE  JS_BIT(NATIVE_ENUM_CACHE_LOG2)
@@ -606,21 +620,16 @@ struct JSThread {
      * This thread is inside js_GC, either waiting until it can start GC, or
      * waiting for GC to finish on another thread. This thread holds no locks;
      * other threads may steal titles from it.
      *
      * Protected by rt->gcLock.
      */
     bool                gcWaiting;
 
-    /*
-     * Deallocator task for this thread.
-     */
-    JSFreePointerListTask *deallocatorTask;
-
     /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
     JSThreadData        data;
 };
 
 /*
  * Only when JSThread::gcThreadMallocBytes exhausts the following limit we
  * update JSRuntime::gcMallocBytes.
  * .
@@ -806,16 +815,20 @@ struct JSRuntime {
     ptrdiff_t           gcMallocBytes;
 
     /* See comments before DelayMarkingChildren is jsgc.cpp. */
     JSGCArena           *gcUnmarkedArenaStackTop;
 #ifdef DEBUG
     size_t              gcMarkLaterCount;
 #endif
 
+#ifdef JS_THREADSAFE
+    JSBackgroundThread  gcHelperThread;
+#endif
+
     /*
      * The trace operation and its data argument to trace embedding-specific
      * GC roots.
      */
     JSTraceDataOp       gcExtraRootsTraceOp;
     void                *gcExtraRootsData;
 
     /*
@@ -847,17 +860,17 @@ struct JSRuntime {
     JSCList             contextList;
 
     /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */
     JSDebugHooks        globalDebugHooks;
 
 #ifdef JS_TRACER
     /* True if any debug hooks not supported by the JIT are enabled. */
     bool debuggerInhibitsJIT() const {
-        return (globalDebugHooks.interruptHandler ||
+        return (globalDebugHooks.interruptHook ||
                 globalDebugHooks.callHook ||
                 globalDebugHooks.objectHook);
     }
 #endif
 
     /* More debugging state, see jsdbgapi.c. */
     JSCList             trapList;
     JSCList             watchPointList;
@@ -979,20 +992,16 @@ struct JSRuntime {
      * disabled until a triggered GC at some later moment compresses live
      * types, minimizing rt->shapeGen in the process.
      */
     volatile uint32     shapeGen;
 
     /* Literal table maintained by jsatom.c functions. */
     JSAtomState         atomState;
 
-#ifdef JS_THREADSAFE
-    JSBackgroundThread    *deallocatorThread;
-#endif
-
     JSEmptyScope          *emptyArgumentsScope;
     JSEmptyScope          *emptyBlockScope;
 
     /*
      * Various metering fields are defined at the end of JSRuntime. In this
      * way there is no need to recompile all the code that refers to other
      * fields of JSRuntime after enabling the corresponding metering macro.
      */
@@ -1175,19 +1184,37 @@ typedef struct JSResolvingEntry {
 #define JSRESOLVE_INFER         0xffff  /* infer bits from current bytecode */
 
 extern const JSDebugHooks js_NullDebugHooks;  /* defined in jsdbgapi.cpp */
 
 namespace js {
 class AutoGCRooter;
 }
 
+struct JSRegExpStatics {
+    JSContext   *cx;
+    JSString    *input;         /* input string to match (perl $_, GC root) */
+    JSBool      multiline;      /* whether input contains newlines (perl $*) */
+    JSSubString lastMatch;      /* last string matched (perl $&) */
+    JSSubString lastParen;      /* last paren matched (perl $+) */
+    JSSubString leftContext;    /* input to left of last match (perl $`) */
+    JSSubString rightContext;   /* input to right of last match (perl $') */
+    js::Vector<JSSubString> parens; /* last set of parens matched (perl $1, $2) */
+
+    JSRegExpStatics(JSContext *cx) : cx(cx), parens(cx) {}
+
+    bool copy(const JSRegExpStatics& other);
+    void clearRoots();
+    void clear();
+};
+
 struct JSContext
 {
-    explicit JSContext(JSRuntime *rt) : runtime(rt), busyArrays(this) {}
+    explicit JSContext(JSRuntime *rt) :
+      runtime(rt), regExpStatics(this), busyArrays(this) {}
 
     /*
      * If this flag is set, we were asked to call back the operation callback
      * as soon as possible.
      */
     volatile jsint      operationCallbackFlag;
 
     /* JSRuntime contextList linkage. */
@@ -1263,17 +1290,17 @@ struct JSContext
     JSArenaPool         tempPool;
 
     /* Top-level object and pointer to top stack frame's scope chain. */
     JSObject            *globalObject;
 
     /* Storage to root recently allocated GC things and script result. */
     JSWeakRoots         weakRoots;
 
-    /* Regular expression class statics (XXX not shared globally). */
+    /* Regular expression class statics. */
     JSRegExpStatics     regExpStatics;
 
     /* State for object and array toSource conversion. */
     JSSharpObjectMap    sharpObjectMap;
     js::HashSet<JSObject *> busyArrays;
 
     /* Argument formatter support for JS_{Convert,Push}Arguments{,VA}. */
     JSArgumentFormatMap *argumentFormatMap;
@@ -1409,41 +1436,33 @@ struct JSContext
      * in runtime->gcLock, to avoid race conditions that would leave the wrong
      * value in jitEnabled. (But the interpreter reads this without
      * locking. That can race against another thread setting debug hooks, but
      * we always read cx->debugHooks without locking anyway.)
      */
     bool                 jitEnabled;
 #endif
 
-    JSClassProtoCache    classProtoCache;
-
     /* Caller must be holding runtime->gcLock. */
     void updateJITEnabled() {
 #ifdef JS_TRACER
         jitEnabled = ((options & JSOPTION_JIT) &&
                       (debugHooks == &js_NullDebugHooks ||
                        (debugHooks == &runtime->globalDebugHooks &&
                         !runtime->debuggerInhibitsJIT())));
 #endif
     }
 
+    JSClassProtoCache    classProtoCache;
+
 #ifdef JS_THREADSAFE
-    inline void createDeallocatorTask() {
-        JS_ASSERT(!thread->deallocatorTask);
-        if (runtime->deallocatorThread && !runtime->deallocatorThread->busy())
-            thread->deallocatorTask = new JSFreePointerListTask();
-    }
-
-    inline void submitDeallocatorTask() {
-        if (thread->deallocatorTask) {
-            runtime->deallocatorThread->schedule(thread->deallocatorTask);
-            thread->deallocatorTask = NULL;
-        }
-    }
+    /*
+     * The sweep task for this context.
+     */
+    js::BackgroundSweepTask *gcSweepTask;
 #endif
 
     ptrdiff_t &getMallocCounter() {
 #ifdef JS_THREADSAFE
         return thread->gcThreadMallocBytes;
 #else
         return runtime->gcMallocBytes;
 #endif
@@ -1508,36 +1527,25 @@ struct JSContext
         /*
          * For compatibility we do not account for realloc that increases
          * previously allocated memory.
          */
         updateMallocCounter(p, orig ? 0 : bytes);
         return p;
     }
 
+    inline void free(void* p) {
 #ifdef JS_THREADSAFE
-    inline void free(void* p) {
-        if (!p)
+        if (gcSweepTask) {
+            gcSweepTask->freeLater(p);
             return;
-        if (thread) {
-            JSFreePointerListTask* task = thread->deallocatorTask;
-            if (task) {
-                task->add(p);
-                return;
-            }
         }
+#endif
         runtime->free(p);
     }
-#else
-    inline void free(void* p) {
-        if (!p)
-            return;
-        runtime->free(p);
-    }
-#endif
 
     /*
      * In the common case that we'd like to allocate the memory for an object
      * with cx->malloc/free, we cannot use overloaded C++ operators (no
      * placement delete).  Factor the common workaround into one place.
      */
 #define CREATE_BODY(parms)                                                    \
     void *memory = this->malloc(sizeof(T));                                   \
@@ -1672,27 +1680,27 @@ class AutoGCRooter {
     };
 
     private:
     /* No copy or assignment semantics. */
     AutoGCRooter(AutoGCRooter &ida);
     void operator=(AutoGCRooter &ida);
 };
 
-class AutoSaveRestoreWeakRoots : private AutoGCRooter
+class AutoPreserveWeakRoots : private AutoGCRooter
 {
   public:
-    explicit AutoSaveRestoreWeakRoots(JSContext *cx
-                                      JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    explicit AutoPreserveWeakRoots(JSContext *cx
+                                   JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, WEAKROOTS), savedRoots(cx->weakRoots)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
-    ~AutoSaveRestoreWeakRoots()
+    ~AutoPreserveWeakRoots()
     {
         context->weakRoots = savedRoots;
     }
 
     friend void AutoGCRooter::trace(JSTracer *trc);
 
   private:
     JSWeakRoots savedRoots;
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -73,17 +73,17 @@ AutoGCRooter::trace(JSTracer *trc)
         js_CallValueTracerIfGCThing(trc, static_cast<AutoValueRooter *>(this)->val);
         return;
 
       case SPROP:
         static_cast<AutoScopePropertyRooter *>(this)->sprop->trace(trc);
         return;
 
       case WEAKROOTS:
-        static_cast<AutoSaveRestoreWeakRoots *>(this)->savedRoots.mark(trc);
+        static_cast<AutoPreserveWeakRoots *>(this)->savedRoots.mark(trc);
         return;
 
       case PARSER:
         static_cast<Parser *>(this)->trace(trc);
         return;
 
       case SCRIPT:
         if (JSScript *script = static_cast<AutoScriptRooter *>(this)->script)
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2194,36 +2194,16 @@ date_toString(JSContext *cx, uintN argc,
 {
     jsdouble utctime;
 
     if (!GetUTCTime(cx, JS_THIS_OBJECT(cx, vp), vp, &utctime))
         return JS_FALSE;
     return date_format(cx, utctime, FORMATSPEC_FULL, vp);
 }
 
-#ifdef JS_TRACER
-static jsval FASTCALL
-date_valueOf_tn(JSContext* cx, JSObject* obj, JSString* str)
-{
-    JS_ASSERT(JS_InstanceOf(cx, obj, &js_DateClass, NULL));
-    jsdouble t = *JSVAL_TO_DOUBLE(obj->getDateUTCTime());
-
-    JSString* number_str = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_NUMBER]);
-    jsval v;
-    if (js_EqualStrings(str, number_str)) {
-        if (!js_NewNumberInRootedValue(cx, t, &v))
-            return JSVAL_ERROR_COOKIE;
-    } else {
-        if (!date_format(cx, t, FORMATSPEC_FULL, &v))
-            return JSVAL_ERROR_COOKIE;
-    }
-    return v;
-}
-#endif
-
 static JSBool
 date_valueOf(JSContext *cx, uintN argc, jsval *vp)
 {
     JSString *str, *number_str;
 
     /* It is an error to call date_valueOf on a non-date object, but we don't
      * need to check for that explicitly here because every path calls
      * GetUTCTime, which does the check.
@@ -2249,19 +2229,16 @@ JS_DEFINE_TRCINFO_1(date_now,
 
 static JSFunctionSpec date_static_methods[] = {
     JS_FN("UTC",                 date_UTC,                MAXARGS,0),
     JS_FN("parse",               date_parse,              1,0),
     JS_TN("now",                 date_now,                0,0, &date_now_trcinfo),
     JS_FS_END
 };
 
-JS_DEFINE_TRCINFO_1(date_valueOf,
-    (3, (static, JSVAL_RETRY, date_valueOf_tn, CONTEXT, THIS, STRING, 0, nanojit::ACC_STORE_ANY)))
-
 static JSFunctionSpec date_methods[] = {
     JS_FN("getTime",             date_getTime,            0,0),
     JS_FN("getTimezoneOffset",   date_getTimezoneOffset,  0,0),
     JS_FN("getYear",             date_getYear,            0,0),
     JS_FN("getFullYear",         date_getFullYear,        0,0),
     JS_FN("getUTCFullYear",      date_getUTCFullYear,     0,0),
     JS_FN("getMonth",            date_getMonth,           0,0),
     JS_FN("getUTCMonth",         date_getUTCMonth,        0,0),
@@ -2297,22 +2274,21 @@ static JSFunctionSpec date_methods[] = {
     JS_FN(js_toLocaleString_str, date_toLocaleString,     0,0),
     JS_FN("toLocaleDateString",  date_toLocaleDateString, 0,0),
     JS_FN("toLocaleTimeString",  date_toLocaleTimeString, 0,0),
     JS_FN("toLocaleFormat",      date_toLocaleFormat,     0,0),
     JS_FN("toDateString",        date_toDateString,       0,0),
     JS_FN("toTimeString",        date_toTimeString,       0,0),
     JS_FN("toISOString",         date_toISOString,        0,0),
     JS_FN(js_toJSON_str,         date_toISOString,        0,0),
-
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,       date_toSource,           0,0),
 #endif
     JS_FN(js_toString_str,       date_toString,           0,0),
-    JS_TN(js_valueOf_str,        date_valueOf,            0,0, &date_valueOf_trcinfo),
+    JS_FN(js_valueOf_str,        date_valueOf,            0,0),
     JS_FS_END
 };
 
 JSBool
 js_Date(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     /* Date called as function. */
     if (!JS_IsConstructing(cx))
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -72,17 +72,17 @@
 using namespace js;
 
 typedef struct JSTrap {
     JSCList         links;
     JSScript        *script;
     jsbytecode      *pc;
     JSOp            op;
     JSTrapHandler   handler;
-    void            *closure;
+    jsval           closure;
 } JSTrap;
 
 #define DBG_LOCK(rt)            JS_ACQUIRE_LOCK((rt)->debuggerLock)
 #define DBG_UNLOCK(rt)          JS_RELEASE_LOCK((rt)->debuggerLock)
 #define DBG_LOCK_EVAL(rt,expr)  (DBG_LOCK(rt), (expr), DBG_UNLOCK(rt))
 
 /*
  * NB: FindTrap must be called with rt->debuggerLock acquired.
@@ -137,17 +137,17 @@ js_UntrapScriptCode(JSContext *cx, JSScr
         }
     }
     DBG_UNLOCK(rt);
     return code;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
-           JSTrapHandler handler, void *closure)
+           JSTrapHandler handler, jsval closure)
 {
     JSTrap *junk, *trap, *twin;
     JSRuntime *rt;
     uint32 sample;
 
     if (script == JSScript::emptyScript()) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
                                      NULL, JSMSG_READ_ONLY, "empty script");
@@ -163,17 +163,17 @@ JS_SetTrap(JSContext *cx, JSScript *scri
         JS_ASSERT(trap->script == script && trap->pc == pc);
         JS_ASSERT(*pc == JSOP_TRAP);
     } else {
         sample = rt->debuggerMutations;
         DBG_UNLOCK(rt);
         trap = (JSTrap *) cx->malloc(sizeof *trap);
         if (!trap)
             return JS_FALSE;
-        trap->closure = NULL;
+        trap->closure = JSVAL_NULL;
         DBG_LOCK(rt);
         twin = (rt->debuggerMutations != sample)
                ? FindTrap(rt, script, pc)
                : NULL;
         if (twin) {
             junk = trap;
             trap = twin;
         } else {
@@ -215,26 +215,26 @@ DestroyTrapAndUnlock(JSContext *cx, JSTr
     JS_REMOVE_LINK(&trap->links);
     *trap->pc = (jsbytecode)trap->op;
     DBG_UNLOCK(cx->runtime);
     cx->free(trap);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
-             JSTrapHandler *handlerp, void **closurep)
+             JSTrapHandler *handlerp, jsval *closurep)
 {
     JSTrap *trap;
 
     DBG_LOCK(cx->runtime);
     trap = FindTrap(cx->runtime, script, pc);
     if (handlerp)
         *handlerp = trap ? trap->handler : NULL;
     if (closurep)
-        *closurep = trap ? trap->closure : NULL;
+        *closurep = trap ? trap->closure : JSVAL_NULL;
     if (trap)
         DestroyTrapAndUnlock(cx, trap);
     else
         DBG_UNLOCK(cx->runtime);
 }
 
 JS_PUBLIC_API(void)
 JS_ClearScriptTraps(JSContext *cx, JSScript *script)
@@ -290,20 +290,18 @@ JS_ClearAllTraps(JSContext *cx)
 void
 js_MarkTraps(JSTracer *trc)
 {
     JSRuntime *rt = trc->context->runtime;
 
     for (JSTrap *trap = (JSTrap *) rt->trapList.next;
          &trap->links != &rt->trapList;
          trap = (JSTrap *) trap->links.next) {
-        if (trap->closure) {
-            JS_SET_TRACING_NAME(trc, "trap->closure");
-            js_CallValueTracerIfGCThing(trc, (jsval) trap->closure);
-        }
+        JS_SET_TRACING_NAME(trc, "trap->closure");
+        js_CallValueTracerIfGCThing(trc, trap->closure);
     }
 }
 
 JS_PUBLIC_API(JSTrapStatus)
 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval)
 {
     JSTrap *trap;
     jsint op;
@@ -370,46 +368,46 @@ LeaveTraceRT(JSRuntime *rt)
     JS_UNLOCK_GC(rt);
 
     if (cx)
         LeaveTrace(cx);
 }
 #endif
 
 JS_PUBLIC_API(JSBool)
-JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure)
+JS_SetInterrupt(JSRuntime *rt, JSInterruptHook hook, void *closure)
 {
 #ifdef JS_TRACER
     {
         AutoLockGC lock(rt);
         bool wasInhibited = rt->debuggerInhibitsJIT();
 #endif
-        rt->globalDebugHooks.interruptHandler = handler;
-        rt->globalDebugHooks.interruptHandlerData = closure;
+        rt->globalDebugHooks.interruptHook = hook;
+        rt->globalDebugHooks.interruptHookData = closure;
 #ifdef JS_TRACER
         JITInhibitingHookChange(rt, wasInhibited);
     }
     LeaveTraceRT(rt);
 #endif
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep)
+JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *hoop, void **closurep)
 {
 #ifdef JS_TRACER
     AutoLockGC lock(rt);
     bool wasInhibited = rt->debuggerInhibitsJIT();
 #endif
-    if (handlerp)
-        *handlerp = rt->globalDebugHooks.interruptHandler;
+    if (hoop)
+        *hoop = rt->globalDebugHooks.interruptHook;
     if (closurep)
-        *closurep = rt->globalDebugHooks.interruptHandlerData;
-    rt->globalDebugHooks.interruptHandler = 0;
-    rt->globalDebugHooks.interruptHandlerData = 0;
+        *closurep = rt->globalDebugHooks.interruptHookData;
+    rt->globalDebugHooks.interruptHook = 0;
+    rt->globalDebugHooks.interruptHookData = 0;
 #ifdef JS_TRACER
     JITInhibitingHookChange(rt, wasInhibited);
 #endif
     return JS_TRUE;
 }
 
 /************************************************************************/
 
@@ -501,20 +499,18 @@ js_TraceWatchPoints(JSTracer *trc, JSObj
 
     rt = trc->context->runtime;
 
     for (wp = (JSWatchPoint *)rt->watchPointList.next;
          &wp->links != &rt->watchPointList;
          wp = (JSWatchPoint *)wp->links.next) {
         if (wp->object == obj) {
             wp->sprop->trace(trc);
-            if (wp->sprop->hasSetterValue() && wp->setter) {
-                JS_CALL_OBJECT_TRACER(trc, js_CastAsObject(wp->setter),
-                                      "wp->setter");
-            }
+            if (wp->sprop->hasSetterValue() && wp->setter)
+                JS_CALL_OBJECT_TRACER(trc, CastAsObject(wp->setter), "wp->setter");
             JS_SET_TRACING_NAME(trc, "wp->closure");
             js_CallValueTracerIfGCThing(trc, OBJECT_TO_JSVAL(wp->closure));
         }
     }
 }
 
 void
 js_SweepWatchPoints(JSContext *cx)
@@ -730,17 +726,17 @@ js_watch_set(JSContext *cx, JSObject *ob
                 }
 #ifdef __GNUC__
                 else
                     argv = NULL;    /* suppress bogus gcc warnings */
 #endif
                 ok = !wp->setter ||
                      (sprop->hasSetterValue()
                       ? js_InternalCall(cx, obj,
-                                        js_CastAsObjectJSVal(wp->setter),
+                                        CastAsObjectJSVal(wp->setter),
                                         1, vp, vp)
                       : wp->setter(cx, obj, userid, vp));
                 if (injectFrame) {
                     /* Evil code can cause us to have an arguments object. */
                     frame.putActivationObjects(cx);
                     cx->fp = frame.down;
                     if (argv != smallv)
                         cx->free(argv);
@@ -769,17 +765,17 @@ js_watch_set_wrapper(JSContext *cx, JSOb
     return js_watch_set(cx, obj, userid, rval);
 }
 
 static bool
 IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop)
 {
     if (sprop->hasSetterValue()) {
         JSObject *funobj = sprop->setterObject();
-        if (!funobj->isFunction())
+        if (!funobj || !funobj->isFunction())
             return false;
 
         JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
         return FUN_NATIVE(fun) == js_watch_set_wrapper;
     }
     return sprop->setterOp() == js_watch_set;
 }
 
@@ -798,20 +794,20 @@ js_WrapWatchedSetter(JSContext *cx, jsid
         if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &id))
             return NULL;
         atom = JSID_TO_ATOM(id);
     } else {
         atom = NULL;
     }
 
     wrapper = js_NewFunction(cx, NULL, js_watch_set_wrapper, 1, 0,
-                             setter ? js_CastAsObject(setter)->getParent() : NULL, atom);
+                             setter ? CastAsObject(setter)->getParent() : NULL, atom);
     if (!wrapper)
         return NULL;
-    return js_CastAsPropertyOp(FUN_OBJECT(wrapper));
+    return CastAsPropertyOp(FUN_OBJECT(wrapper));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval idval,
                  JSWatchPointHandler handler, void *closure)
 {
     JSObject *origobj;
     jsval v;
@@ -1558,17 +1554,17 @@ JS_PutPropertyDescArray(JSContext *cx, J
             js_RemoveRoot(cx->runtime, &pd[i].alias);
     }
     cx->free(pd);
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(JSBool)
-JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure)
+JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler handler, void *closure)
 {
     rt->globalDebugHooks.debuggerHandler = handler;
     rt->globalDebugHooks.debuggerHandlerData = closure;
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure)
@@ -1620,17 +1616,17 @@ JS_SetObjectHook(JSRuntime *rt, JSObject
     }
     if (hook)
         LeaveTraceRT(rt);
 #endif
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
-JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure)
+JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure)
 {
     rt->globalDebugHooks.throwHook = hook;
     rt->globalDebugHooks.throwHookData = closure;
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure)
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -52,41 +52,42 @@ JS_BEGIN_EXTERN_C
 /*
  * Unexported library-private helper used to unpatch all traps in a script.
  * Returns script->code if script has no traps, else a JS_malloc'ed copy of
  * script->code which the caller must JS_free, or null on JS_malloc OOM.
  */
 extern jsbytecode *
 js_UntrapScriptCode(JSContext *cx, JSScript *script);
 
+/* The closure argument will be marked. */
 extern JS_PUBLIC_API(JSBool)
 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
-           JSTrapHandler handler, void *closure);
+           JSTrapHandler handler, jsval closure);
 
 extern JS_PUBLIC_API(JSOp)
 JS_GetTrapOpcode(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 extern JS_PUBLIC_API(void)
 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
-             JSTrapHandler *handlerp, void **closurep);
+             JSTrapHandler *handlerp, jsval *closurep);
 
 extern JS_PUBLIC_API(void)
 JS_ClearScriptTraps(JSContext *cx, JSScript *script);
 
 extern JS_PUBLIC_API(void)
 JS_ClearAllTraps(JSContext *cx);
 
 extern JS_PUBLIC_API(JSTrapStatus)
 JS_HandleTrap(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval);
 
 extern JS_PUBLIC_API(JSBool)
-JS_SetInterrupt(JSRuntime *rt, JSTrapHandler handler, void *closure);
+JS_SetInterrupt(JSRuntime *rt, JSInterruptHook handler, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
-JS_ClearInterrupt(JSRuntime *rt, JSTrapHandler *handlerp, void **closurep);
+JS_ClearInterrupt(JSRuntime *rt, JSInterruptHook *handlerp, void **closurep);
 
 /************************************************************************/
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id,
                  JSWatchPointHandler handler, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
@@ -332,32 +333,32 @@ extern JS_PUBLIC_API(JSBool)
 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda);
 
 extern JS_PUBLIC_API(void)
 JS_PutPropertyDescArray(JSContext *cx, JSPropertyDescArray *pda);
 
 /************************************************************************/
 
 extern JS_PUBLIC_API(JSBool)
-JS_SetDebuggerHandler(JSRuntime *rt, JSTrapHandler handler, void *closure);
+JS_SetDebuggerHandler(JSRuntime *rt, JSDebuggerHandler hook, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetSourceHandler(JSRuntime *rt, JSSourceHandler handler, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetExecuteHook(JSRuntime *rt, JSInterpreterHook hook, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetCallHook(JSRuntime *rt, JSInterpreterHook hook, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetObjectHook(JSRuntime *rt, JSObjectHook hook, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
-JS_SetThrowHook(JSRuntime *rt, JSTrapHandler hook, void *closure);
+JS_SetThrowHook(JSRuntime *rt, JSThrowHook hook, void *closure);
 
 extern JS_PUBLIC_API(JSBool)
 JS_SetDebugErrorHook(JSRuntime *rt, JSDebugErrorHook hook, void *closure);
 
 /************************************************************************/
 
 extern JS_PUBLIC_API(size_t)
 JS_GetObjectTotalSize(JSContext *cx, JSObject *obj);
--- a/js/src/jsdtracef.cpp
+++ b/js/src/jsdtracef.cpp
@@ -47,17 +47,17 @@
 #include "jsdtracef.h"
 #include <sys/types.h>
 
 #define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
 
 static char dempty[] = "<null>";
 
 static char *
-jsdtrace_fun_classname(JSFunction *fun)
+jsdtrace_fun_classname(const JSFunction *fun)
 {
     return (fun &&
             !FUN_INTERPRETED(fun) &&
             !(fun->flags & JSFUN_TRCINFO) &&
             FUN_CLASP(fun))
            ? (char *)FUN_CLASP(fun)->name
            : dempty;
 }
@@ -66,17 +66,17 @@ static char *
 jsdtrace_filename(JSStackFrame *fp)
 {
     return (fp && fp->script && fp->script->filename)
            ? (char *)fp->script->filename
            : dempty;
 }
 
 static int
-jsdtrace_fun_linenumber(JSContext *cx, JSFunction *fun)
+jsdtrace_fun_linenumber(JSContext *cx, const JSFunction *fun)
 {
     if (fun && FUN_INTERPRETED(fun))
         return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun));
 
     return 0;
 }
 
 int
@@ -104,17 +104,17 @@ jsdtrace_frame_linenumber(JSContext *cx,
  * masking out the JavaScript type bits. This allows D scripts to use ints and
  * booleans directly and copyinstr() for string arguments, when types are known
  * beforehand.
  *
  * This is used by the function-args and function-rval probes, which also
  * provide raw (unmasked) jsvals should type info be useful from D scripts.
  */
 static void *
-jsdtrace_jsvaltovoid(JSContext *cx, jsval argval)
+jsdtrace_jsvaltovoid(JSContext *cx, const jsval argval)
 {
     JSType type = TYPEOF(cx, argval);
 
     switch (type) {
       case JSTYPE_NULL:
       case JSTYPE_VOID:
         return (void *)JS_TYPE_STR(type);
 
@@ -131,17 +131,17 @@ jsdtrace_jsvaltovoid(JSContext *cx, jsva
 
       default:
         return JSVAL_TO_GCTHING(argval);
     }
     /* NOTREACHED */
 }
 
 static char *
-jsdtrace_fun_name(JSContext *cx, JSFunction *fun)
+jsdtrace_fun_name(JSContext *cx, const JSFunction *fun)
 {
     JSAtom *atom;
     char *name;
 
     if (!fun)
         return dempty;
 
     atom = fun->atom;
@@ -161,17 +161,17 @@ jsdtrace_fun_name(JSContext *cx, JSFunct
 /*
  * These functions call the DTrace macros for the JavaScript USDT probes.
  * Originally this code was inlined in the JavaScript code; however since
  * a number of operations are called, these have been placed into functions
  * to reduce any negative compiler optimization effect that the addition of
  * a number of usually unused lines of code would cause.
  */
 void
-jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
+jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, const JSFunction *fun)
 {
     JAVASCRIPT_FUNCTION_ENTRY(
         jsdtrace_filename(fp),
         jsdtrace_fun_classname(fun),
         jsdtrace_fun_name(cx, fun)
     );
 }
 
@@ -185,17 +185,18 @@ jsdtrace_function_info(JSContext *cx, JS
         jsdtrace_fun_name(cx, fun),
         jsdtrace_fun_linenumber(cx, fun),
         jsdtrace_filename(dfp),
         jsdtrace_frame_linenumber(cx, dfp)
     );
 }
 
 void
-jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsuint argc, jsval *argv)
+jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                       jsuint argc, jsval *argv)
 {
     JAVASCRIPT_FUNCTION_ARGS(
         jsdtrace_filename(fp),
         jsdtrace_fun_classname(fun),
         jsdtrace_fun_name(cx, fun),
         argc, (void *)argv,
         (argc > 0) ? jsdtrace_jsvaltovoid(cx, argv[0]) : 0,
         (argc > 1) ? jsdtrace_jsvaltovoid(cx, argv[1]) : 0,
--- a/js/src/jsdtracef.h
+++ b/js/src/jsdtracef.h
@@ -28,54 +28,143 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#ifdef INCLUDE_MOZILLA_DTRACE
 #include "javascript-trace.h"
+#endif
 #include "jspubtd.h"
 #include "jsprvtd.h"
 
 #ifndef _JSDTRACEF_H
 #define _JSDTRACEF_H
 
 JS_BEGIN_EXTERN_C
 
 extern void
-jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, JSFunction *fun);
+jsdtrace_function_entry(JSContext *cx, JSStackFrame *fp, const JSFunction *fun);
 
 extern void
 jsdtrace_function_info(JSContext *cx, JSStackFrame *fp, JSStackFrame *dfp,
-                       JSFunction *fun);
+                       const JSFunction *fun);
 
 extern void
-jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsuint argc, jsval *argv);
+jsdtrace_function_args(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                       jsuint argc, jsval *argv);
 
 extern void
-jsdtrace_function_rval(JSContext *cx, JSStackFrame *fp, JSFunction *fun, jsval *rval);
+jsdtrace_function_rval(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                       jsval rval);
 
 extern void
-jsdtrace_function_return(JSContext *cx, JSStackFrame *fp, JSFunction *fun);
+jsdtrace_function_return(JSContext *cx, JSStackFrame *fp, const JSFunction *fun);
 
 extern void
-jsdtrace_object_create_start(JSStackFrame *fp, JSClass *clasp);
+jsdtrace_object_create_start(JSStackFrame *fp, const JSClass *clasp);
 
 extern void
-jsdtrace_object_create_done(JSStackFrame *fp, JSClass *clasp);
+jsdtrace_object_create_done(JSStackFrame *fp, const JSClass *clasp);
 
 extern void
-jsdtrace_object_create(JSContext *cx, JSClass *clasp, JSObject *obj);
+jsdtrace_object_create(JSContext *cx, const JSClass *clasp, const JSObject *obj);
 
 extern void
-jsdtrace_object_finalize(JSObject *obj);
+jsdtrace_object_finalize(const JSObject *obj);
 
 extern void
-jsdtrace_execute_start(JSScript *script);
+jsdtrace_execute_start(const JSScript *script);
 
 extern void
-jsdtrace_execute_done(JSScript *script);
+jsdtrace_execute_done(const JSScript *script);
 
 JS_END_EXTERN_C
 
+namespace js {
+
+class DTrace {
+  public:
+    /*
+     * If |lval| is provided to the enter/exit methods, it is tested to see if
+     * it is a function as a predicate to the dtrace event emission.
+     */
+    static void enterJSFun(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                           JSStackFrame *dfp, jsuint argc, jsval *argv, jsval *lval = NULL);
+    static void exitJSFun(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                          jsval rval, jsval *lval = NULL);
+
+    static void finalizeObject(const JSObject *obj);
+
+    class ExecutionScope {
+        const JSScript *script;
+        void startExecution();
+        void endExecution();
+      public:
+        explicit ExecutionScope(const JSScript *script) : script(script) { startExecution(); }
+        ~ExecutionScope() { endExecution(); }
+    };
+
+};
+
+inline void
+DTrace::enterJSFun(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                   JSStackFrame *dfp, jsuint argc, jsval *argv, jsval *lval)
+{
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (!lval || VALUE_IS_FUNCTION(cx, *lval)) {
+        if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
+            jsdtrace_function_entry(cx, fp, fun);
+        if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
+            jsdtrace_function_info(cx, fp, dfp, fun);
+        if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
+            jsdtrace_function_args(cx, fp, fun, argc, argv);
+    }
+#endif
+}
+
+inline void
+DTrace::exitJSFun(JSContext *cx, JSStackFrame *fp, const JSFunction *fun,
+                  jsval rval, jsval *lval)
+{
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (!lval || VALUE_IS_FUNCTION(cx, *lval)) {
+        if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
+            jsdtrace_function_rval(cx, fp, fun, rval);
+        if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
+            jsdtrace_function_return(cx, fp, fun);
+    }
+#endif
+}
+
+inline void
+DTrace::finalizeObject(const JSObject *obj)
+{
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED())
+        jsdtrace_object_finalize(obj);
+#endif
+}
+
+inline void
+DTrace::ExecutionScope::startExecution()
+{
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_EXECUTE_START_ENABLED())
+        jsdtrace_execute_start(script);
+#endif
+}
+
+inline void
+DTrace::ExecutionScope::endExecution()
+{
+#ifdef INCLUDE_MOZILLA_DTRACE
+    if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
+        jsdtrace_execute_done(script);
+#endif
+}
+
+} /* namespace js */
+    
 #endif /* _JSDTRACE_H */
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -91,27 +91,33 @@ JSCodeGenerator::JSCodeGenerator(Parser 
   : JSTreeContext(parser),
     codePool(cpool), notePool(npool),
     codeMark(JS_ARENA_MARK(cpool)), noteMark(JS_ARENA_MARK(npool)),
     stackDepth(0), maxStackDepth(0),
     ntrynotes(0), lastTryNode(NULL),
     spanDeps(NULL), jumpTargets(NULL), jtFreeList(NULL),
     numSpanDeps(0), numJumpTargets(0), spanDepTodo(0),
     arrayCompDepth(0),
-    emitLevel(0)
+    emitLevel(0),
+    constMap(parser->context)
 {
     flags = TCF_COMPILING;
     memset(&prolog, 0, sizeof prolog);
     memset(&main, 0, sizeof main);
     current = &main;
     firstLine = prolog.currentLine = main.currentLine = lineno;
     prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1;
     memset(&upvarMap, 0, sizeof upvarMap);
 }
 
+bool JSCodeGenerator::init()
+{
+    return constMap.init();
+}
+
 JSCodeGenerator::~JSCodeGenerator()
 {
     JS_ARENA_RELEASE(codePool, codeMark);
     JS_ARENA_RELEASE(notePool, noteMark);
 
     /* NB: non-null only after OOM. */
     if (spanDeps)
         parser->context->free(spanDeps);
@@ -1252,17 +1258,17 @@ JSTreeContext::ensureSharpSlots()
     JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
 
     if (sharpSlotBase >= 0) {
         JS_ASSERT(flags & TCF_HAS_SHARPS);
         return true;
     }
 
     JS_ASSERT(!(flags & TCF_HAS_SHARPS));
-    if (flags & TCF_IN_FUNCTION) {
+    if (inFunction()) {
         JSContext *cx = parser->context;
         JSAtom *sharpArrayAtom = js_Atomize(cx, "#array", 6, 0);
         JSAtom *sharpDepthAtom = js_Atomize(cx, "#depth", 6, 0);
         if (!sharpArrayAtom || !sharpDepthAtom)
             return false;
 
         sharpSlotBase = fun->u.i.nvars;
         if (!js_AddLocal(cx, fun, sharpArrayAtom, JSLOCAL_VAR))
@@ -1539,17 +1545,16 @@ js_PopStatementCG(JSContext *cx, JSCodeG
 JSBool
 js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
                              JSParseNode *pn)
 {
     jsdouble dval;
     jsint ival;
     JSAtom *valueAtom;
     jsval v;
-    JSAtomListElement *ale;
 
     /* XXX just do numbers for now */
     if (pn->pn_type == TOK_NUMBER) {
         dval = pn->pn_dval;
         if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
             v = INT_TO_JSVAL(ival);
         } else {
             /*
@@ -1557,20 +1562,18 @@ js_DefineCompileTimeConstant(JSContext *
              * jsval and store in cg->constList. This works because atoms are
              * protected from GC during compilation.
              */
             valueAtom = js_AtomizeDouble(cx, dval);
             if (!valueAtom)
                 return JS_FALSE;
             v = ATOM_KEY(valueAtom);
         }
-        ale = cg->constList.add(cg->parser, atom);
-        if (!ale)
+        if (!cg->constMap.put(atom, v))
             return JS_FALSE;
-        ALE_SET_VALUE(ale, v);
     }
     return JS_TRUE;
 }
 
 JSStmtInfo *
 js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSStmtInfo *stmt)
 {
     JSObject *obj;
@@ -1621,53 +1624,51 @@ js_LexicalLookup(JSTreeContext *tc, JSAt
  * name defining a constant.
  */
 static JSBool
 LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
                           jsval *vp)
 {
     JSBool ok;
     JSStmtInfo *stmt;
-    JSAtomListElement *ale;
     JSObject *obj, *objbox;
     JSProperty *prop;
     uintN attrs;
 
     /*
      * Chase down the cg stack, but only until we reach the outermost cg.
      * This enables propagating consts from top-level into switch cases in a
      * function compiled along with the top-level script.
      */
     *vp = JSVAL_HOLE;
     do {
-        if (cg->flags & (TCF_IN_FUNCTION | TCF_COMPILE_N_GO)) {
+        if (cg->inFunction() && cg->compileAndGo()) {
             /* XXX this will need revising if 'const' becomes block-scoped. */
             stmt = js_LexicalLookup(cg, atom, NULL);
             if (stmt)
                 return JS_TRUE;
 
-            ale = cg->constList.lookup(atom);
-            if (ale) {
-                JS_ASSERT(ALE_VALUE(ale) != JSVAL_HOLE);
-                *vp = ALE_VALUE(ale);
+            if (JSCodeGenerator::ConstMap::Ptr p = cg->constMap.lookup(atom)) {
+                JS_ASSERT(p->value != JSVAL_HOLE);
+                *vp = p->value;
                 return JS_TRUE;
             }
 
             /*
              * Try looking in the variable object for a direct property that
              * is readonly and permanent.  We know such a property can't be
              * shadowed by another property on obj's prototype chain, or a
              * with object or catch variable; nor can prop's value be changed,
              * nor can prop be deleted.
              */
-            if (cg->flags & TCF_IN_FUNCTION) {
+            if (cg->inFunction()) {
                 if (js_LookupLocal(cx, cg->fun, atom, NULL) != JSLOCAL_NONE)
                     break;
             } else {
-                JS_ASSERT(cg->flags & TCF_COMPILE_N_GO);
+                JS_ASSERT(cg->compileAndGo());
                 obj = cg->scopeChain;
                 ok = obj->lookupProperty(cx, ATOM_TO_JSID(atom), &objbox, &prop);
                 if (!ok)
                     return JS_FALSE;
                 if (objbox == obj) {
                     /*
                      * We're compiling code that will be executed immediately,
                      * not re-executed against a different scope chain and/or
@@ -1829,17 +1830,17 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, 
  * Compiler::compileScript; see comments there.
  *
  * The function returns -1 on failures.
  */
 static jsint
 AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot)
 {
     JS_ASSERT((jsuint) slot < cg->maxStackDepth);
-    if (cg->flags & TCF_IN_FUNCTION) {
+    if (cg->inFunction()) {
         slot += cg->fun->u.i.nvars;
         if ((uintN) slot >= SLOTNO_LIMIT) {
             ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
             slot = -1;
         }
     }
     return slot;
 }
@@ -1937,17 +1938,17 @@ MakeUpvarForEval(JSParseNode *pn, JSCode
         return true;
 
     JS_ASSERT(cg->staticLevel > upvarLevel);
     if (cg->staticLevel >= JS_DISPLAY_SIZE || upvarLevel >= JS_DISPLAY_SIZE)
         return true;
 
     JSAtomListElement *ale = cg->upvarList.lookup(atom);
     if (!ale) {
-        if ((cg->flags & TCF_IN_FUNCTION) &&
+        if (cg->inFunction() &&
             !js_AddLocal(cx, cg->fun, atom, JSLOCAL_UPVAR)) {
             return false;
         }
 
         ale = cg->upvarList.add(cg->parser, atom);
         if (!ale)
             return false;
         JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
@@ -2059,32 +2060,32 @@ BindNameToSlot(JSContext *cx, JSCodeGene
      */
     switch (op) {
       case JSOP_NAME:
       case JSOP_SETCONST:
         break;
       case JSOP_DELNAME:
         if (dn_kind != JSDefinition::UNKNOWN) {
             if (cg->parser->callerFrame && !cg->funbox)
-                JS_ASSERT(cg->flags & TCF_COMPILE_N_GO);
+                JS_ASSERT(cg->compileAndGo());
             else
                 pn->pn_op = JSOP_FALSE;
             pn->pn_dflags |= PND_BOUND;
             return JS_TRUE;
         }
         break;
       default:
         if (pn->isConst())
             pn->pn_op = op = JSOP_NAME;
     }
 
     if (cookie == FREE_UPVAR_COOKIE) {
         JSStackFrame *caller = cg->parser->callerFrame;
         if (caller) {
-            JS_ASSERT(cg->flags & TCF_COMPILE_N_GO);
+            JS_ASSERT(cg->compileAndGo());
 
             /*
              * Don't generate upvars on the left side of a for loop. See
              * bug 470758.
              */
             if (cg->flags & TCF_IN_FOR_INIT)
                 return JS_TRUE;
 
@@ -2093,17 +2094,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
                 return JS_TRUE;
 
             /*
              * Make sure the variable object used by the compiler to initialize
              * parent links matches the caller's varobj. Compile-n-go compiler-
              * created function objects have the top-level cg's scopeChain set
              * as their parent by Parser::newFunction.
              */
-            JSObject *scopeobj = (cg->flags & TCF_IN_FUNCTION)
+            JSObject *scopeobj = cg->inFunction()
                                  ? FUN_OBJECT(cg->fun)->getParent()
                                  : cg->scopeChain;
             if (scopeobj != cg->parser->callerVarObj)
                 return JS_TRUE;
 
             /*
              * We are compiling eval or debug script inside a function frame
              * and the scope chain matches the function's variable object.
@@ -2129,19 +2130,20 @@ BindNameToSlot(JSContext *cx, JSCodeGene
         return JS_TRUE;
     }
 
     if (dn->pn_dflags & PND_GVAR) {
         /*
          * If this is a global reference from within a function, leave pn_op as
          * JSOP_NAME, etc. We could emit JSOP_*GVAR ops within function code if
          * only we could depend on the global frame's slots being valid for all
-         * calls to the function.
+         * calls to the function, and if we could equate the atom index in the
+         * function's atom map for every global name with its frame slot.
          */
-        if (cg->flags & TCF_IN_FUNCTION)
+        if (cg->inFunction())
             return JS_TRUE;
 
         /*
          * We are optimizing global variables and there may be no pre-existing
          * global property named atom when this global script runs. If atom was
          * declared via const or var, optimize pn to access fp->vars using the
          * appropriate JSOP_*GVAR op.
          *
@@ -2187,20 +2189,20 @@ BindNameToSlot(JSContext *cx, JSCodeGene
         JSStackFrame *caller = cg->parser->callerFrame;
 #endif
         JS_ASSERT(caller);
         JS_ASSERT(caller->script);
 
         JSTreeContext *tc = cg;
         while (tc->staticLevel != level)
             tc = tc->parent;
-        JS_ASSERT(tc->flags & TCF_COMPILING);
+        JS_ASSERT(tc->compiling());
 
         JSCodeGenerator *evalcg = (JSCodeGenerator *) tc;
-        JS_ASSERT(evalcg->flags & TCF_COMPILE_N_GO);
+        JS_ASSERT(evalcg->compileAndGo());
         JS_ASSERT(caller->fun && cg->parser->callerVarObj == evalcg->scopeChain);
 
         /*
          * Don't generate upvars on the left side of a for loop. See
          * bug 470758 and bug 520513.
          */
         if (evalcg->flags & TCF_IN_FOR_INIT)
             return JS_TRUE;
@@ -2212,17 +2214,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
             return JS_TRUE;
         }
 
         return MakeUpvarForEval(pn, cg);
     }
 
     uintN skip = cg->staticLevel - level;
     if (skip != 0) {
-        JS_ASSERT(cg->flags & TCF_IN_FUNCTION);
+        JS_ASSERT(cg->inFunction());
         JS_ASSERT_IF(UPVAR_FRAME_SLOT(cookie) != CALLEE_UPVAR_SLOT,
                      cg->lexdeps.lookup(atom));
         JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
         JS_ASSERT(cg->fun->u.i.skipmin <= skip);
 
         /*
          * If op is a mutating opcode, this upvar's static level is too big to
          * index into the display, or the function is heavyweight, we fall back
@@ -2286,17 +2288,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
             }
 
             uintN slot = UPVAR_FRAME_SLOT(cookie);
             if (slot != CALLEE_UPVAR_SLOT && dn_kind != JSDefinition::ARG) {
                 JSTreeContext *tc = cg;
                 do {
                     tc = tc->parent;
                 } while (tc->staticLevel != level);
-                if (tc->flags & TCF_IN_FUNCTION)
+                if (tc->inFunction())
                     slot += tc->fun->nargs;
             }
 
             vector[index] = MAKE_UPVAR_COOKIE(skip, slot);
         }
 
         pn->pn_op = op;
         pn->pn_cookie = index;
@@ -3642,17 +3644,17 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGe
     } else {
         ale = cg->atomList.add(cg->parser, pn->pn_atom);
         if (!ale)
             return JS_FALSE;
         atomIndex = ALE_INDEX(ale);
     }
 
     if (JOF_OPTYPE(pn->pn_op) == JOF_ATOM &&
-        (!(cg->flags & TCF_IN_FUNCTION) || (cg->flags & TCF_FUN_HEAVYWEIGHT))) {
+        (!cg->inFunction() || (cg->flags & TCF_FUN_HEAVYWEIGHT))) {
         CG_SWITCH_TO_PROLOG(cg);
         if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno))
             return JS_FALSE;
         EMIT_INDEX_OP(prologOp, atomIndex);
         CG_SWITCH_TO_MAIN(cg);
     }
 
     if (result)
@@ -4365,17 +4367,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         JS_ASSERT(FUN_INTERPRETED(fun));
         if (fun->u.i.script) {
             /*
              * This second pass is needed to emit JSOP_NOP with a source note
              * for the already-emitted function definition prolog opcode. See
              * comments in the TOK_LC case.
              */
             JS_ASSERT(pn->pn_op == JSOP_NOP);
-            JS_ASSERT(cg->flags & TCF_IN_FUNCTION);
+            JS_ASSERT(cg->inFunction());
             if (!EmitFunctionDefNop(cx, cg, pn->pn_index))
                 return JS_FALSE;
             break;
         }
 
         JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
                      pn->pn_defn ||
                      (!pn->pn_used && !pn->isTopLevel()) ||
@@ -4391,16 +4393,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         if (!cg2space) {
             js_ReportOutOfScriptQuota(cx);
             return JS_FALSE;
         }
         JSCodeGenerator *cg2 =
             new (cg2space) JSCodeGenerator(cg->parser,
                                            cg->codePool, cg->notePool,
                                            pn->pn_pos.begin.lineno);
+
+        if (!cg2->init())
+            return JS_FALSE;
+
         cg2->flags = pn->pn_funbox->tcflags | TCF_IN_FUNCTION;
 #if JS_HAS_SHARP_VARS
         if (cg2->flags & TCF_HAS_SHARPS) {
             cg2->sharpSlotBase = fun->sharpSlotBase(cx);
             if (cg2->sharpSlotBase < 0)
                 return JS_FALSE;
         }
 #endif
@@ -4445,17 +4451,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
          * For a script we emit the code as we parse. Thus the bytecode for
          * top-level functions should go in the prolog to predefine their
          * names in the variable object before the already-generated main code
          * is executed. This extra work for top-level scripts is not necessary
          * when we emit the code for a function. It is fully parsed prior to
          * invocation of the emitter and calls to js_EmitTree for function
          * definitions can be scheduled before generating the rest of code.
          */
-        if (!(cg->flags & TCF_IN_FUNCTION)) {
+        if (!cg->inFunction()) {
             JS_ASSERT(!cg->topStmt);
             CG_SWITCH_TO_PROLOG(cg);
             op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFFUN_FC : JSOP_DEFFUN;
             EMIT_INDEX_OP(op, index);
             CG_SWITCH_TO_MAIN(cg);
 
             /* Emit NOP for the decompiler. */
             if (!EmitFunctionDefNop(cx, cg, index))
@@ -4509,17 +4515,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
              * else-if chain without bracing, overindenting, or incorrectly
              * scoping let declarations.
              */
             JS_ASSERT(stmtInfo.type == STMT_ELSE);
             stmtInfo.type = STMT_IF;
             stmtInfo.update = top;
             if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
                 return JS_FALSE;
-            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp))
+            if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - beq))
                 return JS_FALSE;
         }
 
         /* Emit an annotated branch-if-false around the then part. */
         pn3 = pn->pn_kid3;
         noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
         if (noteIndex < 0)
             return JS_FALSE;
@@ -5479,17 +5485,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
             CG_BASE(cg)[top] = JSOP_SETRVAL;
             if (js_Emit1(cx, cg, JSOP_RETRVAL) < 0)
                 return JS_FALSE;
         }
         break;
 
 #if JS_HAS_GENERATORS
       case TOK_YIELD:
-        if (!(cg->flags & TCF_IN_FUNCTION)) {
+        if (!cg->inFunction()) {
             ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR,
                                      JSMSG_BAD_RETURN_OR_YIELD,
                                      js_yield_str);
             return JS_FALSE;
         }
         if (pn->pn_kid) {
             if (!js_EmitTree(cx, cg, pn->pn_kid))
                 return JS_FALSE;
@@ -5536,17 +5542,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
              * in the block we use a separate pass over functions. During the
              * main pass later the emitter will add JSOP_NOP with source notes
              * for the function to preserve the original functions position
              * when decompiling.
              *
              * Currently this is used only for functions, as compile-as-we go
              * mode for scripts does not allow separate emitter passes.
              */
-            JS_ASSERT(cg->flags & TCF_IN_FUNCTION);
+            JS_ASSERT(cg->inFunction());
             if (pn->pn_xflags & PNX_DESTRUCT) {
                 /*
                  * Assign the destructuring arguments before defining any
                  * functions, see bug 419662.
                  */
                 JS_ASSERT(pnchild->pn_type == TOK_SEMI);
                 JS_ASSERT(pnchild->pn_kid->pn_type == TOK_COMMA);
                 if (!js_EmitTree(cx, cg, pnchild))
@@ -6001,46 +6007,53 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                 top += tmp;
             } while ((pn2 = pn2->pn_next)->pn_next);
         }
         break;
 
       case TOK_PLUS:
         /* For TCF_IN_FUNCTION test, see TOK_RB concerning JSOP_NEWARRAY. */
         if (pn->pn_arity == PN_LIST && pn->pn_count < JS_BIT(16) &&
-            (cg->flags & TCF_IN_FUNCTION)) {
+            cg->inFunction()) {
             /* Emit up to the first string literal conventionally. */
             for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
                 if (pn2->pn_type == TOK_STRING)
                     break;
                 if (!js_EmitTree(cx, cg, pn2))
                     return JS_FALSE;
                 if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
                     return JS_FALSE;
             }
 
-            /* Emit remainder as a single JSOP_CONCATN. */
-            for (index = 0; pn2; pn2 = pn2->pn_next, index++) {
+            if (!pn2)
+                break;
+
+            /*
+             * Having seen a string literal, we know statically that the rest
+             * of the additions are string concatenation, so we emit them as a
+             * single concatn. First, do string conversion on the result of the
+             * preceding zero or more additions so that any side effects of
+             * string conversion occur before the next operand begins.
+             */
+            if (pn2 == pn->pn_head) {
+                index = 0;
+            } else {
+                if (!js_Emit1(cx, cg, JSOP_OBJTOSTR))
+                    return JS_FALSE;
+                index = 1;
+            }
+
+            for (; pn2; pn2 = pn2->pn_next, index++) {
                 if (!js_EmitTree(cx, cg, pn2))
                     return JS_FALSE;
-                if (!pn2->isLiteral() &&
-                    js_Emit1(cx, cg, JSOP_OBJTOSTR) < 0) {
+                if (!pn2->isLiteral() && js_Emit1(cx, cg, JSOP_OBJTOSTR) < 0)
                     return JS_FALSE;
-                }
             }
 
-            if (index != 0) {
-                EMIT_UINT16_IMM_OP(JSOP_CONCATN, index);
-
-                /* If we had a prefix, we need to be added to it now. */
-                if (pn->pn_head->pn_type != TOK_STRING &&
-                    js_Emit1(cx, cg, JSOP_ADD) < 0) {
-                    return JS_FALSE;
-                }
-            }
+            EMIT_UINT16_IMM_OP(JSOP_CONCATN, index);
             break;
         }
       case TOK_BITOR:
       case TOK_BITXOR:
       case TOK_BITAND:
       case TOK_EQOP:
       case TOK_RELOP:
       case TOK_IN:
@@ -6410,17 +6423,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
          * and if our container is top-level but not a function body, or else
          * a block statement; then emit a SRC_BRACE note.  All other container
          * statements get braces by default from the decompiler.
          */
         noteIndex = -1;
         type = PN_TYPE(pn->expr());
         if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR &&
             (!(stmt = stmtInfo.down)
-             ? !(cg->flags & TCF_IN_FUNCTION)
+             ? !cg->inFunction()
              : stmt->type == STMT_BLOCK)) {
 #if defined DEBUG_brendan || defined DEBUG_mrbkap
             /* There must be no source note already output for the next op. */
             JS_ASSERT(CG_NOTE_COUNT(cg) == 0 ||
                       CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) ||
                       !GettableNoteForNextOp(cg));
 #endif
             noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
@@ -6522,17 +6535,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
          * array using a fast, all-at-once process rather than a slow, element-
          * by-element process.
          */
 #if JS_HAS_SHARP_VARS
         sharpnum = -1;
       do_emit_array:
 #endif
 
-        op = (JS_LIKELY(pn->pn_count < JS_BIT(16)) && (cg->flags & TCF_IN_FUNCTION))
+        op = (JS_LIKELY(pn->pn_count < JS_BIT(16)) && cg->inFunction())
              ? JSOP_NEWARRAY
              : JSOP_NEWINIT;
 
 #if JS_HAS_GENERATORS
         if (pn->pn_type == TOK_ARRAYCOMP)
             op = JSOP_NEWINIT;
 #endif
 #if JS_HAS_SHARP_VARS
@@ -6740,17 +6753,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
       case TOK_REGEXP: {
         /*
          * If the regexp's script is one-shot and the regexp is not used in a
          * loop, we can avoid the extra fork-on-exec costs of JSOP_REGEXP by
          * selecting JSOP_OBJECT. Otherwise, to avoid incorrect proto, parent,
          * and lastIndex sharing, select JSOP_REGEXP.
          */
         JS_ASSERT(pn->pn_op == JSOP_REGEXP);
-        bool singleton = !cg->fun && (cg->flags & TCF_COMPILE_N_GO);
+        bool singleton = !cg->fun && cg->compileAndGo();
         if (singleton) {
             for (JSStmtInfo *stmt = cg->topStmt; stmt; stmt = stmt->down) {
                 if (STMT_IS_LOOP(stmt)) {
                     singleton = false;
                     break;
                 }
             }
         }
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -158,18 +158,112 @@ struct JSStmtInfo {
 #ifdef JS_SCOPE_DEPTH_METER
 # define JS_SCOPE_DEPTH_METERING(code) ((void) (code))
 # define JS_SCOPE_DEPTH_METERING_IF(cond, code) ((cond) ? (void) (code) : (void) 0)
 #else
 # define JS_SCOPE_DEPTH_METERING(code) ((void) 0)
 # define JS_SCOPE_DEPTH_METERING_IF(code, x) ((void) 0)
 #endif
 
+#define TCF_COMPILING           0x01 /* JSTreeContext is JSCodeGenerator */
+#define TCF_IN_FUNCTION         0x02 /* parsing inside function body */
+#define TCF_RETURN_EXPR         0x04 /* function has 'return expr;' */
+#define TCF_RETURN_VOID         0x08 /* function has 'return;' */
+#define TCF_IN_FOR_INIT         0x10 /* parsing init expr of for; exclude 'in' */
+#define TCF_FUN_SETS_OUTER_NAME 0x20 /* function set outer name (lexical or free) */
+#define TCF_FUN_PARAM_ARGUMENTS 0x40 /* function has parameter named arguments */
+#define TCF_FUN_USES_ARGUMENTS  0x80 /* function uses arguments except as a
+                                        parameter name */
+#define TCF_FUN_HEAVYWEIGHT    0x100 /* function needs Call object per call */
+#define TCF_FUN_IS_GENERATOR   0x200 /* parsed yield statement in function */
+#define TCF_FUN_USES_OWN_NAME  0x400 /* named function expression that uses its
+                                        own name */
+#define TCF_HAS_FUNCTION_STMT  0x800 /* block contains a function statement */
+#define TCF_GENEXP_LAMBDA     0x1000 /* flag lambda from generator expression */
+#define TCF_COMPILE_N_GO      0x2000 /* compile-and-go mode of script, can
+                                        optimize name references based on scope
+                                        chain */
+#define TCF_NO_SCRIPT_RVAL    0x4000 /* API caller does not want result value
+                                        from global script */
+#define TCF_HAS_SHARPS        0x8000 /* source contains sharp defs or uses */
+
+/*
+ * Set when parsing a declaration-like destructuring pattern.  This
+ * flag causes PrimaryExpr to create PN_NAME parse nodes for variable
+ * references which are not hooked into any definition's use chain,
+ * added to any tree context's AtomList, etc. etc.  CheckDestructuring
+ * will do that work later.
+ *
+ * The comments atop CheckDestructuring explain the distinction
+ * between assignment-like and declaration-like destructuring
+ * patterns, and why they need to be treated differently.
+ */
+#define TCF_DECL_DESTRUCTURING  0x10000
+
+/*
+ * A request flag passed to Compiler::compileScript and then down via
+ * JSCodeGenerator to js_NewScriptFromCG, from script_compile_sub and any
+ * kindred functions that need to make mutable scripts (even empty ones;
+ * i.e., they can't share the const JSScript::emptyScript() singleton).
+ */
+#define TCF_NEED_MUTABLE_SCRIPT 0x20000
+
+/*
+ * This function/global/eval code body contained a Use Strict Directive. Treat
+ * certain strict warnings as errors, and forbid the use of 'with'. See also
+ * TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and JSREPORT_STRICT_ERROR.
+ */
+#define TCF_STRICT_MODE_CODE    0x40000
+
+/* Function has parameter named 'eval'. */
+#define TCF_FUN_PARAM_EVAL      0x80000
+
+/*
+ * Flag signifying that the current function seems to be a constructor that
+ * sets this.foo to define "methods", at least one of which can't be a null
+ * closure, so we should avoid over-specializing property cache entries and
+ * trace inlining guards to method function object identity, which will vary
+ * per instance.
+ */
+#define TCF_FUN_UNBRAND_THIS   0x100000
+
+/*
+ * "Module pattern", i.e., a lambda that is immediately applied and the whole
+ * of an expression statement.
+ */
+#define TCF_FUN_MODULE_PATTERN 0x200000
+
+/*
+ * Flag to prevent a non-escaping function from being optimized into a null
+ * closure (i.e., a closure that needs only its global object for free variable
+ * resolution, thanks to JSOP_{GET,CALL}UPVAR), because this function contains
+ * a closure that needs one or more scope objects surrounding it (i.e., Call
+ * object for a heavyweight outer function). See bug 560234.
+ */
+#define TCF_FUN_ENTRAINS_SCOPES 0x400000
+
+/*
+ * Flags to check for return; vs. return expr; in a function.
+ */
+#define TCF_RETURN_FLAGS        (TCF_RETURN_EXPR | TCF_RETURN_VOID)
+
+/*
+ * Sticky deoptimization flags to propagate from FunctionBody.
+ */
+#define TCF_FUN_FLAGS           (TCF_FUN_SETS_OUTER_NAME |                    \
+                                 TCF_FUN_USES_ARGUMENTS  |                    \
+                                 TCF_FUN_PARAM_ARGUMENTS |                    \
+                                 TCF_FUN_HEAVYWEIGHT     |                    \
+                                 TCF_FUN_IS_GENERATOR    |                    \
+                                 TCF_FUN_USES_OWN_NAME   |                    \
+                                 TCF_HAS_SHARPS          |                    \
+                                 TCF_STRICT_MODE_CODE)
+
 struct JSTreeContext {              /* tree context for semantic checks */
-    uint32          flags;          /* statement state flags, see below */
+    uint32          flags;          /* statement state flags, see above */
     uint16          ngvars;         /* max. no. of global variables/regexps */
     uint32          bodyid;         /* block number of program/function body */
     uint32          blockidGen;     /* preincremented block number generator */
     JSStmtInfo      *topStmt;       /* top of statement info stack */
     JSStmtInfo      *topScopeStmt;  /* top lexical scope statement */
     JSObject        *blockChain;    /* compile time block scope chain (NB: one
                                        deeper than the topScopeStmt/downScope
                                        chain when in head of let block/expr) */
@@ -238,103 +332,22 @@ struct JSTreeContext {              /* t
      */
     int sharpSlotBase;
     bool ensureSharpSlots();
 
     // Return true there is a generator function within |skip| lexical scopes
     // (going upward) from this context's lexical scope. Always return true if
     // this context is itself a generator.
     bool skipSpansGenerator(unsigned skip);
+
+    bool compileAndGo() { return !!(flags & TCF_COMPILE_N_GO); }
+    bool inFunction() { return !!(flags & TCF_IN_FUNCTION); }
+    bool compiling() { return !!(flags & TCF_COMPILING); }
 };
 
-#define TCF_COMPILING           0x01 /* JSTreeContext is JSCodeGenerator */
-#define TCF_IN_FUNCTION         0x02 /* parsing inside function body */
-#define TCF_RETURN_EXPR         0x04 /* function has 'return expr;' */
-#define TCF_RETURN_VOID         0x08 /* function has 'return;' */
-#define TCF_IN_FOR_INIT         0x10 /* parsing init expr of for; exclude 'in' */
-#define TCF_FUN_SETS_OUTER_NAME 0x20 /* function set outer name (lexical or free) */
-#define TCF_FUN_PARAM_ARGUMENTS 0x40 /* function has parameter named arguments */
-#define TCF_FUN_USES_ARGUMENTS  0x80 /* function uses arguments except as a
-                                        parameter name */
-#define TCF_FUN_HEAVYWEIGHT    0x100 /* function needs Call object per call */
-#define TCF_FUN_IS_GENERATOR   0x200 /* parsed yield statement in function */
-#define TCF_FUN_USES_OWN_NAME  0x400 /* named function expression that uses its
-                                        own name */
-#define TCF_HAS_FUNCTION_STMT  0x800 /* block contains a function statement */
-#define TCF_GENEXP_LAMBDA     0x1000 /* flag lambda from generator expression */
-#define TCF_COMPILE_N_GO      0x2000 /* compile-and-go mode of script, can
-                                        optimize name references based on scope
-                                        chain */
-#define TCF_NO_SCRIPT_RVAL    0x4000 /* API caller does not want result value
-                                        from global script */
-#define TCF_HAS_SHARPS        0x8000 /* source contains sharp defs or uses */
-
-/*
- * Set when parsing a declaration-like destructuring pattern.  This
- * flag causes PrimaryExpr to create PN_NAME parse nodes for variable
- * references which are not hooked into any definition's use chain,
- * added to any tree context's AtomList, etc. etc.  CheckDestructuring
- * will do that work later.
- *
- * The comments atop CheckDestructuring explain the distinction
- * between assignment-like and declaration-like destructuring
- * patterns, and why they need to be treated differently.
- */
-#define TCF_DECL_DESTRUCTURING  0x10000
-
-/*
- * A request flag passed to Compiler::compileScript and then down via
- * JSCodeGenerator to js_NewScriptFromCG, from script_compile_sub and any
- * kindred functions that need to make mutable scripts (even empty ones;
- * i.e., they can't share the const JSScript::emptyScript() singleton).
- */
-#define TCF_NEED_MUTABLE_SCRIPT 0x20000
-
-/*
- * This function/global/eval code body contained a Use Strict Directive. Treat
- * certain strict warnings as errors, and forbid the use of 'with'. See also
- * TSF_STRICT_MODE_CODE, JSScript::strictModeCode, and JSREPORT_STRICT_ERROR.
- */
-#define TCF_STRICT_MODE_CODE    0x40000
-
-/* Function has parameter named 'eval'. */
-#define TCF_FUN_PARAM_EVAL      0x80000
-
-/*
- * Flag signifying that the current function seems to be a constructor that
- * sets this.foo to define "methods", at least one of which can't be a null
- * closure, so we should avoid over-specializing property cache entries and
- * trace inlining guards to method function object identity, which will vary
- * per instance.
- */
-#define TCF_FUN_UNBRAND_THIS   0x100000
-
-/*
- * "Module pattern", i.e., a lambda that is immediately applied and the whole
- * of an expression statement.
- */
-#define TCF_FUN_MODULE_PATTERN 0x200000
-
-/*
- * Flags to check for return; vs. return expr; in a function.
- */
-#define TCF_RETURN_FLAGS        (TCF_RETURN_EXPR | TCF_RETURN_VOID)
-
-/*
- * Sticky deoptimization flags to propagate from FunctionBody.
- */
-#define TCF_FUN_FLAGS           (TCF_FUN_SETS_OUTER_NAME |                    \
-                                 TCF_FUN_USES_ARGUMENTS  |                    \
-                                 TCF_FUN_PARAM_ARGUMENTS |                    \
-                                 TCF_FUN_HEAVYWEIGHT     |                    \
-                                 TCF_FUN_IS_GENERATOR    |                    \
-                                 TCF_FUN_USES_OWN_NAME   |                    \
-                                 TCF_HAS_SHARPS          |                    \
-                                 TCF_STRICT_MODE_CODE)
-
 /*
  * Return true if we need to check for conditions that elicit
  * JSOPTION_STRICT warnings or strict mode errors.
  */
 inline bool JSTreeContext::needStrictChecks() {
     return JS_HAS_STRICT_OPTION(parser->context) ||
            (flags & TCF_STRICT_MODE_CODE);
 }
@@ -450,17 +463,19 @@ struct JSCodeGenerator : public JSTreeCo
     uintN           numSpanDeps;    /* number of span dependencies */
     uintN           numJumpTargets; /* number of jump targets */
     ptrdiff_t       spanDepTodo;    /* offset from main.base of potentially
                                        unoptimized spandeps */
 
     uintN           arrayCompDepth; /* stack depth of array in comprehension */
 
     uintN           emitLevel;      /* js_EmitTree recursion level */
-    JSAtomList      constList;      /* compile time constants */
+
+    typedef js::HashMap<JSAtom *, jsval> ConstMap;
+    ConstMap        constMap;       /* compile time constants */
 
     JSCGObjectList  objectList;     /* list of emitted objects */
     JSCGObjectList  regexpList;     /* list of emitted regexp that will be
                                        cloned during execution */
 
     JSAtomList      upvarList;      /* map of atoms to upvar indexes */
     JSUpvarArray    upvarMap;       /* indexed upvar pairs (JS_realloc'ed) */
 
@@ -468,16 +483,18 @@ struct JSCodeGenerator : public JSTreeCo
      * Initialize cg to allocate bytecode space from codePool, source note
      * space from notePool, and all other arena-allocated temporaries from
      * parser->context->tempPool.
      */
     JSCodeGenerator(js::Parser *parser,
                     JSArenaPool *codePool, JSArenaPool *notePool,
                     uintN lineno);
 
+    bool init();
+
     /*
      * Release cg->codePool, cg->notePool, and parser->context->tempPool to
      * marks set by JSCodeGenerator's ctor. Note that cgs are magic: they own
      * the arena pool "tops-of-stack" space above their codeMark, noteMark, and
      * tempMark points.  This means you cannot alloc from tempPool and save the
      * pointer beyond the next JSCodeGenerator destructor call.
      */
     ~JSCodeGenerator();
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -132,17 +132,17 @@ js_GetArgsProperty(JSContext *cx, JSStac
     }
 
     *vp = JSVAL_VOID;
     if (JSID_IS_INT(id)) {
         uint32 arg = uint32(JSID_TO_INT(id));
         JSObject *argsobj = JSVAL_TO_OBJECT(fp->argsobj);
         if (arg < fp->argc) {
             if (argsobj) {
-                jsval v = GetArgsSlot(argsobj, arg);
+                jsval v = argsobj->getArgsElement(arg);
                 if (v == JSVAL_HOLE)
                     return argsobj->getProperty(cx, id, vp);
             }
             *vp = fp->argv[arg];
         } else {
             /*
              * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
              * storage between the formal parameter and arguments[k] for all
@@ -192,19 +192,19 @@ NewArguments(JSContext *cx, JSObject *pa
     return argsobj;
 }
 
 static void
 PutArguments(JSContext *cx, JSObject *argsobj, jsval *args)
 {
     uint32 argc = argsobj->getArgsLength();
     for (uint32 i = 0; i != argc; ++i) {
-        jsval v = argsobj->dslots[i];
+        jsval v = argsobj->getArgsElement(i);
         if (v != JSVAL_HOLE)
-            argsobj->dslots[i] = args[i];
+            argsobj->setArgsElement(i, args[i]);
     }
 }
 
 JSObject *
 js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
 {
     /*
      * We must be in a function activation; the function must be lightweight
@@ -296,17 +296,17 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_Pu
 static JSBool
 args_delProperty(JSContext *cx, JSObject *obj, jsval idval, jsval *vp)
 {
     JS_ASSERT(obj->isArguments());
 
     if (JSVAL_IS_INT(idval)) {
         uintN arg = uintN(JSVAL_TO_INT(idval));
         if (arg < obj->getArgsLength())
-            SetArgsSlot(obj, arg, JSVAL_HOLE);
+            obj->setArgsElement(arg, JSVAL_HOLE);
     } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
         obj->setArgsLengthOverridden();
     } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) {
         obj->setArgsCallee(JSVAL_HOLE);
     }
     return true;
 }
 
@@ -503,17 +503,17 @@ ArgGetter(JSContext *cx, JSObject *obj, 
                 return false;
             }
 #endif
 
             JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
             if (fp) {
                 *vp = fp->argv[arg];
             } else {
-                jsval v = GetArgsSlot(obj, arg);
+                jsval v = obj->getArgsElement(arg);
                 if (v != JSVAL_HOLE)
                     *vp = v;
             }
         }
     } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
         if (!obj->isArgsLengthOverridden())
             *vp = INT_TO_JSVAL(obj->getArgsLength());
     } else {
@@ -590,17 +590,17 @@ args_resolve(JSContext *cx, JSObject *ob
              JSObject **objp)
 {
     JS_ASSERT(obj->isArguments());
 
     *objp = NULL;
     jsid id = 0;
     if (JSVAL_IS_INT(idval)) {
         uint32 arg = uint32(JSVAL_TO_INT(idval));
-        if (arg < obj->getArgsLength() && GetArgsSlot(obj, arg) != JSVAL_HOLE)
+        if (arg < obj->getArgsLength() && obj->getArgsElement(arg) != JSVAL_HOLE)
             id = INT_JSVAL_TO_JSID(idval);
     } else if (idval == ATOM_KEY(cx->runtime->atomState.lengthAtom)) {
         if (!obj->isArgsLengthOverridden())
             id = ATOM_TO_JSID(cx->runtime->atomState.lengthAtom);
     } else if (idval == ATOM_KEY(cx->runtime->atomState.calleeAtom)) {
         if (obj->getArgsCallee() != JSVAL_HOLE)
             id = ATOM_TO_JSID(cx->runtime->atomState.calleeAtom);
     }
@@ -2035,29 +2035,29 @@ js_fun_apply(JSContext *cx, uintN argc, 
     /* Push fval, obj, and aobj's elements as args. */
     sp = invokevp;
     *sp++ = fval;
     *sp++ = OBJECT_TO_JSVAL(obj);
     if (aobj && aobj->isArguments() && !aobj->isArgsLengthOverridden()) {
         /*
          * Two cases, two loops: note how in the case of an active stack frame
          * backing aobj, even though we copy from fp->argv, we still must check
-         * aobj->dslots[i] for a hole, to handle a delete on the corresponding
-         * arguments element. See args_delProperty.
+         * aobj->getArgsElement(i) for a hole, to handle a delete on the
+         * corresponding arguments element. See args_delProperty.
          */
         JSStackFrame *fp = (JSStackFrame *) aobj->getPrivate();
         if (fp) {
             memcpy(sp, fp->argv, argc * sizeof(jsval));
             for (i = 0; i < argc; i++) {
-                if (aobj->dslots[i] == JSVAL_HOLE) // suppress deleted element
+                if (aobj->getArgsElement(i) == JSVAL_HOLE) // suppress deleted element
                     sp[i] = JSVAL_VOID;
             }
         } else {
-            memcpy(sp, aobj->dslots, argc * sizeof(jsval));
             for (i = 0; i < argc; i++) {
+                sp[i] = aobj->getArgsElement(i);
                 if (sp[i] == JSVAL_HOLE)
                     sp[i] = JSVAL_VOID;
             }
         }
     } else {
         for (i = 0; i < argc; i++) {
             ok = aobj->getProperty(cx, INT_TO_JSID(jsint(i)), sp);
             if (!ok)
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -424,32 +424,16 @@ const uint32 JS_ARGS_LENGTH_MAX = JS_BIT
 /*
  * JSSLOT_ARGS_LENGTH stores ((argc << 1) | overwritten_flag) as int jsval.
  * Thus (JS_ARGS_LENGTH_MAX << 1) | 1 must fit JSVAL_INT_MAX. To assert that
  * we check first that the shift does not overflow uint32.
  */
 JS_STATIC_ASSERT(JS_ARGS_LENGTH_MAX <= JS_BIT(30));
 JS_STATIC_ASSERT(jsval((JS_ARGS_LENGTH_MAX << 1) | 1) <= JSVAL_INT_MAX);
 
-namespace js {
-
-inline jsval
-GetArgsSlot(JSObject *argsobj, uint32 arg)
-{
-    return argsobj->dslots[arg];
-}
-
-inline void
-SetArgsSlot(JSObject *argsobj, uint32 arg, jsval v)
-{
-    argsobj->dslots[arg] = v;
-}
-
-} /* namespace js */
-
 extern JSBool
 js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp);
 
 typedef enum JSLocalKind {
     JSLOCAL_NONE,
     JSLOCAL_ARG,
     JSLOCAL_VAR,
     JSLOCAL_CONST,
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -79,20 +79,17 @@
 #include "jsstr.h"
 #include "jstask.h"
 #include "jstracer.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
-#ifdef INCLUDE_MOZILLA_DTRACE
 #include "jsdtracef.h"
-#endif
-
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 /*
  * Include the headers for mmap.
  */
 #if defined(XP_WIN)
 # include <windows.h>
@@ -932,16 +929,21 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes
         return false;
     }
     if (!JS_DHashTableInit(&rt->gcLocksHash, JS_DHashGetStubOps(), NULL,
                            sizeof(JSGCLockHashEntry), GC_ROOTS_SIZE)) {
         rt->gcLocksHash.ops = NULL;
         return false;
     }
 
+#ifdef JS_THREADSAFE
+    if (!rt->gcHelperThread.init())
+        return false;
+#endif
+
     /*
      * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
      * for default backward API compatibility.
      */
     rt->gcMaxBytes = maxbytes;
     rt->setGCMaxMallocBytes(maxbytes);
 
     rt->gcEmptyArenaPoolLifespan = 30000;
@@ -1146,16 +1148,19 @@ js_FinishGC(JSRuntime *rt)
 #ifdef JS_ARENAMETER
     JS_DumpArenaStats(stdout);
 #endif
 #ifdef JS_GCMETER
     if (JS_WANT_GC_METER_PRINT)
         js_DumpGCStats(rt, stdout);
 #endif
 
+#ifdef JS_THREADSAFE
+    rt->gcHelperThread.cancel();
+#endif
     FinishGCArenaLists(rt);
 
     if (rt->gcRootsHash.ops) {
 #ifdef DEBUG
         CheckLeakedRoots(rt);
 #endif
         JS_DHashTableFinish(&rt->gcRootsHash);
         rt->gcRootsHash.ops = NULL;
@@ -1403,17 +1408,17 @@ GetGCFreeLists(JSContext *cx)
 }
 
 static void
 LastDitchGC(JSContext *cx)
 {
     JS_ASSERT(!JS_ON_TRACE(cx));
 
     /* The last ditch GC preserves weak roots and all atoms. */
-    AutoSaveRestoreWeakRoots save(cx);
+    AutoPreserveWeakRoots save(cx);
     AutoKeepAtoms keep(cx->runtime);
 
     /*
      * Keep rt->gcLock across the call into the GC so we don't starve and
      * lose to racing threads who deplete the heap just after the GC has
      * replenished it (or has synchronized with a racing GC that collected a
      * bunch of garbage).  This unfair scheduling can happen on certain
      * operating systems. For the gory details, see bug 162779.
@@ -2507,20 +2512,17 @@ FinalizeObject(JSContext *cx, JSObject *
     if (!obj->map)
         return;
 
     /* Finalize obj first, in case it needs map and slots. */
     JSClass *clasp = obj->getClass();
     if (clasp->finalize)
         clasp->finalize(cx, obj);
 
-#ifdef INCLUDE_MOZILLA_DTRACE
-    if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED())
-        jsdtrace_object_finalize(obj);
-#endif
+    DTrace::finalizeObject(obj);
 
     if (JS_LIKELY(obj->isNative())) {
         JSScope *scope = obj->scope();
         if (scope->isSharedEmpty())
             static_cast<JSEmptyScope *>(scope)->dropFromGC(cx);
         else
             scope->destroy(cx);
     }
@@ -2864,16 +2866,59 @@ SweepDoubles(JSRuntime *rt)
             ap = &ainfo->prev;
         }
     }
     METER(UpdateArenaStats(&rt->gcStats.doubleArenaStats,
                            nlivearenas, nkilledarenas, nthings));
     rt->gcDoubleArenaList.cursor = rt->gcDoubleArenaList.head;
 }
 
+#ifdef JS_THREADSAFE
+
+namespace js {
+
+JS_FRIEND_API(void)
+BackgroundSweepTask::replenishAndFreeLater(void *ptr)
+{
+    JS_ASSERT(freeCursor == freeCursorEnd);
+    do {
+        if (freeCursor && !freeVector.append(freeCursorEnd - FREE_ARRAY_LENGTH))
+            break;
+        freeCursor = (void **) js_malloc(FREE_ARRAY_SIZE);
+        if (!freeCursor) {
+            freeCursorEnd = NULL;
+            break;
+        }
+        freeCursorEnd = freeCursor + FREE_ARRAY_LENGTH;
+        *freeCursor++ = ptr;
+        return;
+    } while (false);
+    js_free(ptr);
+}
+
+void
+BackgroundSweepTask::run()
+{
+    if (freeCursor) {
+        void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
+        freeElementsAndArray(array, freeCursor);
+        freeCursor = freeCursorEnd = NULL;
+    } else {
+        JS_ASSERT(!freeCursorEnd);
+    }
+    for (void ***iter = freeVector.begin(); iter != freeVector.end(); ++iter) {
+        void **array = *iter;
+        freeElementsAndArray(array, array + FREE_ARRAY_LENGTH);
+    }
+}
+
+}
+
+#endif /* JS_THREADSAFE */
+
 /*
  * Common cache invalidation and so forth that must be done before GC. Even if
  * GCUntilDone calls GC several times, this work only needs to be done once.
  */
 static void
 PreGCCleanup(JSContext *cx, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
@@ -2886,20 +2931,16 @@ PreGCCleanup(JSContext *cx, JSGCInvocati
 
 #ifdef JS_DUMP_SCOPE_METERS
     {
         extern void js_DumpScopeMeters(JSRuntime *rt);
         js_DumpScopeMeters(rt);
     }
 #endif
 
-#ifdef JS_TRACER
-    PurgeJITOracle();
-#endif
-
     /*
      * Reset the property cache's type id generator so we can compress ids.
      * Same for the protoHazardShape proxy-shape standing in for all object
      * prototypes having readonly or setter properties.
      */
     if (rt->shapeGen & SHAPE_OVERFLOW_BIT
 #ifdef JS_GC_ZEAL
         || rt->gcZeal >= 1
@@ -2973,17 +3014,19 @@ GC(JSContext *cx  GCTIMER_PARAM)
         JS_ASSERT(cx->insideGCMarkCallback);
         cx->insideGCMarkCallback = JS_FALSE;
     }
     JS_ASSERT(rt->gcMarkLaterCount == 0);
 
     rt->gcMarkingTracer = NULL;
 
 #ifdef JS_THREADSAFE
-    cx->createDeallocatorTask();
+    JS_ASSERT(!cx->gcSweepTask);
+    if (!rt->gcHelperThread.busy())
+        cx->gcSweepTask = new js::BackgroundSweepTask();
 #endif
 
     /*
      * Sweep phase.
      *
      * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
      * so that any attempt to allocate a GC-thing from a finalizer will fail,
      * rather than nest badly and leave the unmarked newborn to be swept.
@@ -3066,17 +3109,20 @@ GC(JSContext *cx  GCTIMER_PARAM)
     /*
      * Destroy arenas after we finished the sweeping so finalizers can safely
      * use js_IsAboutToBeFinalized().
      */
     FreeGCChunks(rt);
     TIMESTAMP(sweepDestroyEnd);
 
 #ifdef JS_THREADSAFE
-    cx->submitDeallocatorTask();
+    if (cx->gcSweepTask) {
+        rt->gcHelperThread.schedule(cx->gcSweepTask);
+        cx->gcSweepTask = NULL;
+    }
 #endif
 
     if (rt->gcCallback)
         (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
 #ifdef DEBUG_srcnotesize
   { extern void DumpSrcNoteSizeHist();
     DumpSrcNoteSizeHist();
     printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes);
@@ -3354,17 +3400,17 @@ FireGCEnd(JSContext *cx, JSGCInvocationK
     JSGCCallback callback = rt->gcCallback;
 
     /*
      * Execute JSGC_END callback outside the lock. Again, sample the callback
      * pointer in case it changes, since we are outside of the GC vs. requests
      * interlock mechanism here.
      */
     if (gckind != GC_SET_SLOT_REQUEST && callback) {
-        Conditionally<AutoUnlockGC> unlockIf(gckind & GC_LOCK_HELD, rt);
+        Conditionally<AutoUnlockGC> unlockIf(!!(gckind & GC_LOCK_HELD), rt);
 
         (void) callback(cx, JSGC_END);
 
         /*
          * On shutdown, iterate until the JSGC_END callback stops creating
          * garbage.
          */
         if (gckind == GC_LAST_CONTEXT && rt->gcPoke)
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -43,16 +43,17 @@
  * JS Garbage Collector.
  */
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsdhash.h"
 #include "jsbit.h"
 #include "jsutil.h"
 #include "jstask.h"
+#include "jsvector.h"
 #include "jsversion.h"
 
 JS_BEGIN_EXTERN_C
 
 #define JSTRACE_XML         3
 
 /*
  * One past the maximum trace kind.
@@ -356,35 +357,62 @@ struct JSWeakRoots {
     jsval           lastInternalResult;
 
     void mark(JSTracer *trc);
 };
 
 #define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots)))
 
 #ifdef JS_THREADSAFE
-class JSFreePointerListTask : public JSBackgroundTask {
-    void *head;
-  public:
-    JSFreePointerListTask() : head(NULL) {}
+
+namespace js {
 
-    void add(void* ptr) {
-        *(void**)ptr = head;
-        head = ptr;
+/*
+ * During the finalization we do not free immediately. Rather we add the
+ * corresponding pointers to a buffer which we later release on the
+ * background thread.
+ *
+ * The buffer is implemented as a vector of 64K arrays of pointers, not as a
+ * simple vector, to avoid realloc calls during the vector growth and to not
+ * bloat the binary size of the inlined freeLater method. Any OOM during
+ * buffer growth results in the pointer being freed immediately.
+ */
+class BackgroundSweepTask : public JSBackgroundTask {
+    static const size_t FREE_ARRAY_SIZE = size_t(1) << 16;
+    static const size_t FREE_ARRAY_LENGTH = FREE_ARRAY_SIZE / sizeof(void *);
+
+    Vector<void **, 16, js::SystemAllocPolicy> freeVector;
+    void            **freeCursor;
+    void            **freeCursorEnd;
+
+    JS_FRIEND_API(void)
+    replenishAndFreeLater(void *ptr);
+
+    static void freeElementsAndArray(void **array, void **end) {
+        JS_ASSERT(array <= end);
+        for (void **p = array; p != end; ++p)
+            js_free(*p);
+        js_free(array);
     }
 
-    void run() {
-        void *ptr = head;
-        while (ptr) {
-            void *next = *(void **)ptr;
-            js_free(ptr);
-            ptr = next;
-        }
+  public:
+    BackgroundSweepTask()
+        : freeCursor(NULL), freeCursorEnd(NULL) { }
+
+    void freeLater(void* ptr) {
+        if (freeCursor != freeCursorEnd)
+            *freeCursor++ = ptr;
+        else
+            replenishAndFreeLater(ptr);
     }
+
+    virtual void run();
 };
+
+}
 #endif
 
 extern void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str);
 
 #if defined JS_GCMETER
 const bool JS_WANT_GC_METER_PRINT = true;
 #elif defined DEBUG
--- a/js/src/jsgcchunk.cpp
+++ b/js/src/jsgcchunk.cpp
@@ -198,17 +198,17 @@ UnmapPages(void *addr, size_t size)
 
 static void *
 MapAlignedPages(size_t size, size_t alignment)
 {
     /*
      * We don't use MAP_FIXED here, because it can cause the *replacement*
      * of existing mappings, and we only want to create new mappings.
      */
-    void *p = mmap((void *) alignment, size, PROT_READ | PROT_WRITE,
+    void *p = mmap((caddr_t) alignment, size, PROT_READ | PROT_WRITE,
                      MAP_PRIVATE | MAP_NOSYNC | MAP_ALIGN | MAP_ANON, -1, 0);
     if (p == MAP_FAILED)
         return NULL;
     return p;
 }
 
 # else /* JS_GC_HAS_MAP_ALIGN */
 
@@ -231,17 +231,17 @@ MapPages(void *addr, size_t size)
     return p;
 }
 
 # endif /* !JS_GC_HAS_MAP_ALIGN */
 
 static void
 UnmapPages(void *addr, size_t size)
 {
-    JS_ALWAYS_TRUE(munmap(addr, size) == 0);
+    JS_ALWAYS_TRUE(munmap((caddr_t) addr, size) == 0);
 }
 
 #endif
 
 namespace js {
 
 inline void *
 FindChunkStart(void *p)
@@ -251,17 +251,17 @@ FindChunkStart(void *p)
     return reinterpret_cast<void *>(addr);
 }
 
 void *
 AllocGCChunk()
 {
     void *p;
 
-#if JS_GC_HAS_MAP_ALIGN
+#ifdef JS_GC_HAS_MAP_ALIGN
     p = MapAlignedPages(GC_CHUNK_SIZE, GC_CHUNK_SIZE);
     if (!p)
         return NULL;
 #else
     /*
      * Windows requires that there be a 1:1 mapping between VM allocation
      * and deallocation operations.  Therefore, take care here to acquire the
      * final result via one mapping operation.  This means unmapping any
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -76,20 +76,17 @@
 #include "jsvector.h"
 
 #include "jsatominlines.h"
 #include "jspropertycacheinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 #include "jsstrinlines.h"
-
-#ifdef INCLUDE_MOZILLA_DTRACE
 #include "jsdtracef.h"
-#endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #include "jsautooplen.h"
 
 using namespace js;
@@ -809,25 +806,17 @@ js_Invoke(JSContext *cx, uintN argc, jsv
         }
         frame.slots = sp - fun->u.i.nvars;
     }
 
     /* Call the hook if present after we fully initialized the frame. */
     if (hook)
         hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
 
-#ifdef INCLUDE_MOZILLA_DTRACE
-    /* DTrace function entry, non-inlines */
-    if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
-        jsdtrace_function_entry(cx, &frame, fun);
-    if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
-        jsdtrace_function_info(cx, &frame, frame.down, fun);
-    if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
-        jsdtrace_function_args(cx, &frame, fun, frame.argc, frame.argv);
-#endif
+    DTrace::enterJSFun(cx, &frame, fun, frame.down, frame.argc, frame.argv);
 
     /* Call the function, either a native method or an interpreted script. */
     if (native) {
 #ifdef DEBUG_NOT_THROWING
         JSBool alreadyThrowing = cx->throwing;
 #endif
         /* Primitive |this| should not be passed to slow natives. */
         JSObject *thisp = JSVAL_TO_OBJECT(frame.thisv);
@@ -837,23 +826,17 @@ js_Invoke(JSContext *cx, uintN argc, jsv
         if (ok && !alreadyThrowing)
             ASSERT_NOT_THROWING(cx);
 #endif
     } else {
         JS_ASSERT(script);
         ok = js_Interpret(cx);
     }
 
-#ifdef INCLUDE_MOZILLA_DTRACE
-    /* DTrace function return, non-inlines */
-    if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
-        jsdtrace_function_rval(cx, &frame, fun, &frame.rval);
-    if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
-        jsdtrace_function_return(cx, &frame, fun);
-#endif
+    DTrace::exitJSFun(cx, &frame, fun, frame.rval);
 
 out:
     if (hookData) {
         hook = cx->debugHooks->callHook;
         if (hook)
             hook(cx, &frame, JS_FALSE, &ok, hookData);
     }
 
@@ -943,30 +926,17 @@ js_Execute(JSContext *cx, JSObject *chai
     if (script->isEmpty()) {
         if (result)
             *result = JSVAL_VOID;
         return JS_TRUE;
     }
 
     LeaveTrace(cx);
 
-#ifdef INCLUDE_MOZILLA_DTRACE
-    struct JSDNotifyGuard {
-        JSScript *script;
-        JSDNotifyGuard(JSScript *s) : script(s) {
-            if (JAVASCRIPT_EXECUTE_START_ENABLED())
-                jsdtrace_execute_start(script);
-        }
-        ~JSDNotifyGuard() {
-            if (JAVASCRIPT_EXECUTE_DONE_ENABLED())
-                jsdtrace_execute_done(script);
-        }
-
-    } jsdNotifyGuard(script);
-#endif
+    DTrace::ExecutionScope executionScope(script);
 
     JSInterpreterHook hook = cx->debugHooks->executeHook;
     void *hookData = NULL;
     JSStackFrame frame;
     CallStack callStack(cx);
     frame.script = script;
     if (down) {
         /* Propagate arg state for eval and the debugger API. */
@@ -2459,29 +2429,31 @@ js_Interpret(JSContext *cx)
 
 #define RESTORE_INTERP_VARS()                                                 \
     JS_BEGIN_MACRO                                                            \
         fp = cx->fp;                                                          \
         script = fp->script;                                                  \
         atoms = FrameAtomBase(cx, fp);                                        \
         currentVersion = (JSVersion) script->version;                         \
         JS_ASSERT(fp->regs == &regs);                                         \
-        if (cx->throwing)                                                     \
-            goto error;                                                       \
     JS_END_MACRO
 
 #define MONITOR_BRANCH(reason)                                                \
     JS_BEGIN_MACRO                                                            \
         if (TRACING_ENABLED(cx)) {                                            \
-            if (MonitorLoopEdge(cx, inlineCallCount, reason)) {               \
+            MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, reason);   \
+            if (r == MONITOR_RECORDING) {                                     \
                 JS_ASSERT(TRACE_RECORDER(cx));                                \
                 MONITOR_BRANCH_TRACEVIS;                                      \
                 ENABLE_INTERRUPTS();                                          \
             }                                                                 \
             RESTORE_INTERP_VARS();                                            \
+            JS_ASSERT_IF(cx->throwing, r == MONITOR_ERROR);                   \
+            if (r == MONITOR_ERROR)                                           \
+                goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #else /* !JS_TRACER */
 
 #define MONITOR_BRANCH(reason) ((void) 0)
 
 #endif /* !JS_TRACER */
@@ -2542,17 +2514,17 @@ js_Interpret(JSContext *cx)
     if (script->staticLevel < JS_DISPLAY_SIZE) {
         JSStackFrame **disp = &cx->display[script->staticLevel];
         fp->displaySave = *disp;
         *disp = fp;
     }
 
 # define CHECK_INTERRUPT_HANDLER()                                            \
     JS_BEGIN_MACRO                                                            \
-        if (cx->debugHooks->interruptHandler)                                 \
+        if (cx->debugHooks->interruptHook)                                    \
             ENABLE_INTERRUPTS();                                              \
     JS_END_MACRO
 
     /*
      * Load the debugger's interrupt hook here and after calling out to native
      * functions (but not to getters, setters, or other native hooks), so we do
      * not have to reload it each time through the interpreter loop -- we hope
      * the compiler can keep it in a register when it is non-null.
@@ -2702,17 +2674,17 @@ js_Interpret(JSContext *cx)
     if (TRACE_RECORDER(cx))
         AbortRecording(cx, "error or exception while recording");
 #endif
 
     if (!cx->throwing) {
         /* This is an error, not a catchable exception, quit the frame ASAP. */
         ok = JS_FALSE;
     } else {
-        JSTrapHandler handler;
+        JSThrowHook handler;
         JSTryNote *tn, *tnlimit;
         uint32 offset;
 
         /* Call debugger throw hook if set. */
         handler = cx->debugHooks->throwHook;
         if (handler) {
             switch (handler(cx, script, regs.pc, &rval,
                             cx->debugHooks->throwHookData)) {
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -1408,23 +1408,24 @@ js_IsTitleLocked(JSContext *cx, JSTitle 
     if (CX_THREAD_IS_RUNNING_GC(cx))
         return JS_TRUE;
 
     /* Special case: locked object owning a sealed scope, see js_LockObj. */
     if (cx->lockedSealedTitle == title)
         return JS_TRUE;
 
     /*
-     * General case: the title is either exclusively owned (by cx), or it has
-     * a thin or fat lock to cope with shared (concurrent) ownership.
+     * General case: the title is either exclusively owned by some context, or
+     * it has a thin or fat lock to cope with shared (concurrent) ownership.
+     *
+     * js_LockTitle(cx, title) must set ownercx to cx when claiming the title
+     * from another context on the same thread.
      */
-    if (title->ownercx) {
-        JS_ASSERT(title->ownercx == cx || title->ownercx->thread == cx->thread);
-        return JS_TRUE;
-    }
+    if (title->ownercx)
+        return title->ownercx == cx;
     return js_CurrentThreadId() ==
            ((JSThread *)Thin_RemoveWait(ReadWord(title->lock.owner)))->id;
 }
 
 #ifdef JS_DEBUG_TITLE_LOCKS
 void
 js_SetScopeInfo(JSScope *scope, const char *file, int line)
 {
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -880,22 +880,28 @@ js_NumberToStringWithBase(JSContext *cx,
         if (base == 10 && jsuint(i) < INT_STRING_LIMIT)
             return JSString::intString(i);
         if (jsuint(i) < jsuint(base)) {
             if (i < 10)
                 return JSString::intString(i);
             return JSString::unitString(jschar('a' + i - 10));
         }
     }
+    JSThreadData *data = JS_THREAD_DATA(cx);
+    if (data->dtoaCache.s && data->dtoaCache.base == base && data->dtoaCache.d == d)
+        return data->dtoaCache.s;
     numStr = NumberToCString(cx, d, base, buf, sizeof buf);
     if (!numStr)
         return NULL;
     s = JS_NewStringCopyZ(cx, numStr);
     if (!(numStr >= buf && numStr < buf + sizeof buf))
         js_free(numStr);
+    data->dtoaCache.base = base;
+    data->dtoaCache.d = d;
+    data->dtoaCache.s = s;
     return s;
 }
 
 JSString * JS_FASTCALL
 js_NumberToString(JSContext *cx, jsdouble d)
 {
     return js_NumberToStringWithBase(cx, d, 10);
 }
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -437,16 +437,137 @@ js_DoubleToECMAInt32(jsdouble d)
             du.s.lo &= mask32;
         }
         two32.s.hi = 0x41f00000 ^ (du.s.hi & 0x80000000);
         two32.s.lo = 0;
         du.d -= two32.d;
     }
 
     return int32(du.d);
+#elif defined (__arm__) && defined (__GNUC__)
+    int32_t i;
+    uint32_t    tmp0;
+    uint32_t    tmp1;
+    uint32_t    tmp2;
+    asm (
+    // We use a pure integer solution here. In the 'softfp' ABI, the argument
+    // will start in r0 and r1, and VFP can't do all of the necessary ECMA
+    // conversions by itself so some integer code will be required anyway. A
+    // hybrid solution is faster on A9, but this pure integer solution is
+    // notably faster for A8.
+
+    // %0 is the result register, and may alias either of the %[QR]1 registers.
+    // %Q4 holds the lower part of the mantissa.
+    // %R4 holds the sign, exponent, and the upper part of the mantissa.
+    // %1, %2 and %3 are used as temporary values.
+
+    // Extract the exponent.
+"   mov     %1, %R4, LSR #20\n"
+"   bic     %1, %1, #(1 << 11)\n"  // Clear the sign.
+
+    // Set the implicit top bit of the mantissa. This clobbers a bit of the
+    // exponent, but we have already extracted that.
+"   orr     %R4, %R4, #(1 << 20)\n"
+
+    // Special Cases
+    //   We should return zero in the following special cases:
+    //    - Exponent is 0x000 - 1023: +/-0 or subnormal.
+    //    - Exponent is 0x7ff - 1023: +/-INFINITY or NaN
+    //      - This case is implicitly handled by the standard code path anyway,
+    //        as shifting the mantissa up by the exponent will result in '0'.
+    //
+    // The result is composed of the mantissa, prepended with '1' and
+    // bit-shifted left by the (decoded) exponent. Note that because the r1[20]
+    // is the bit with value '1', r1 is effectively already shifted (left) by
+    // 20 bits, and r0 is already shifted by 52 bits.
+    
+    // Adjust the exponent to remove the encoding offset. If the decoded
+    // exponent is negative, quickly bail out with '0' as such values round to
+    // zero anyway. This also catches +/-0 and subnormals.
+"   sub     %1, %1, #0xff\n"
+"   subs    %1, %1, #0x300\n"
+"   bmi     8f\n"
+
+    //  %1 = (decoded) exponent >= 0
+    //  %R4 = upper mantissa and sign
+
+    // ---- Lower Mantissa ----
+"   subs    %3, %1, #52\n"         // Calculate exp-52
+"   bmi     1f\n"
+
+    // Shift r0 left by exp-52.
+    // Ensure that we don't overflow ARM's 8-bit shift operand range.
+    // We need to handle anything up to an 11-bit value here as we know that
+    // 52 <= exp <= 1024 (0x400). Any shift beyond 31 bits results in zero
+    // anyway, so as long as we don't touch the bottom 5 bits, we can use
+    // a logical OR to push long shifts into the 32 <= (exp&0xff) <= 255 range.
+"   bic     %2, %3, #0xff\n"
+"   orr     %3, %3, %2, LSR #3\n"
+    // We can now perform a straight shift, avoiding the need for any
+    // conditional instructions or extra branches.
+"   mov     %Q4, %Q4, LSL %3\n"
+"   b       2f\n"
+"1:\n" // Shift r0 right by 52-exp.
+    // We know that 0 <= exp < 52, and we can shift up to 255 bits so 52-exp
+    // will always be a valid shift and we can sk%3 the range check for this case.
+"   rsb     %3, %1, #52\n"
+"   mov     %Q4, %Q4, LSR %3\n"
+
+    //  %1 = (decoded) exponent
+    //  %R4 = upper mantissa and sign
+    //  %Q4 = partially-converted integer
+
+"2:\n"
+    // ---- Upper Mantissa ----
+    // This is much the same as the lower mantissa, with a few different
+    // boundary checks and some masking to hide the exponent & sign bit in the
+    // upper word.
+    // Note that the upper mantissa is pre-shifted by 20 in %R4, but we shift
+    // it left more to remove the sign and exponent so it is effectively
+    // pre-shifted by 31 bits.
+"   subs    %3, %1, #31\n"          // Calculate exp-31
+"   mov     %1, %R4, LSL #11\n"     // Re-use %1 as a temporary register.
+"   bmi     3f\n"
+
+    // Shift %R4 left by exp-31.
+    // Avoid overflowing the 8-bit shift range, as before.
+"   bic     %2, %3, #0xff\n"
+"   orr     %3, %3, %2, LSR #3\n"
+    // Perform the shift.
+"   mov     %2, %1, LSL %3\n"
+"   b       4f\n"
+"3:\n" // Shift r1 right by 31-exp.
+    // We know that 0 <= exp < 31, and we can shift up to 255 bits so 31-exp
+    // will always be a valid shift and we can skip the range check for this case.
+"   rsb     %3, %3, #0\n"          // Calculate 31-exp from -(exp-31)
+"   mov     %2, %1, LSR %3\n"      // Thumb-2 can't do "LSR %3" in "orr".
+
+    //  %Q4 = partially-converted integer (lower)
+    //  %R4 = upper mantissa and sign
+    //  %2 = partially-converted integer (upper)
+
+"4:\n"
+    // Combine the converted parts.
+"   orr     %Q4, %Q4, %2\n"
+    // Negate the result if we have to, and move it to %0 in the process. To
+    // avoid conditionals, we can do this by inverting on %R4[31], then adding
+    // %R4[31]>>31.
+"   eor     %Q4, %Q4, %R4, ASR #31\n"
+"   add     %0, %Q4, %R4, LSR #31\n"
+"   b       9f\n"
+"8:\n"
+    // +/-INFINITY, +/-0, subnormals, NaNs, and anything else out-of-range that
+    // will result in a conversion of '0'.
+"   mov     %0, #0\n"
+"9:\n"
+    : "=r" (i), "=&r" (tmp0), "=&r" (tmp1), "=&r" (tmp2)
+    : "r" (d)
+    : "cc"
+        );
+    return i;
 #else
     int32 i;
     jsdouble two32, two31;
 
     if (!JSDOUBLE_IS_FINITE(d))
         return 0;
 
     i = (int32) d;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -88,20 +88,17 @@
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #if JS_HAS_XDR
 #include "jsxdrapi.h"
 #endif
 
-#ifdef INCLUDE_MOZILLA_DTRACE
 #include "jsdtracef.h"
-#endif
-
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 
@@ -1007,24 +1004,16 @@ obj_toLocaleString(JSContext *cx, uintN 
 
 static JSBool
 obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
 {
     *vp = JS_THIS(cx, vp);
     return !JSVAL_IS_NULL(*vp);
 }
 
-#ifdef JS_TRACER
-static jsval FASTCALL
-Object_p_valueOf(JSContext* cx, JSObject* obj, JSString *hint)
-{
-    return OBJECT_TO_JSVAL(obj);
-}
-#endif
-
 /*
  * Check if CSP allows new Function() or eval() to run in the current
  * principals.
  */
 JSBool
 js_CheckContentSecurityPolicy(JSContext *cx)
 {
     JSSecurityCallbacks *callbacks;
@@ -1628,36 +1617,16 @@ js_HasOwnProperty(JSContext *cx, JSLooku
         }
 
         (*objp)->dropProperty(cx, *propp);
         *propp = NULL;
     }
     return true;
 }
 
-#ifdef JS_TRACER
-static JSBool FASTCALL
-Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
-{
-    jsid id;
-
-    JSObject *pobj;
-    JSProperty *prop;
-    if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) ||
-        !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &pobj, &prop)) {
-        SetBuiltinError(cx);
-        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
-    }
-
-    if (prop)
-       pobj->dropProperty(cx, prop);
-    return !!prop;
-}
-#endif
-
 /* Proposed ECMA 15.2.4.6. */
 static JSBool
 obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
         return JS_FALSE;
     JSBool b;
@@ -1676,33 +1645,16 @@ obj_propertyIsEnumerable(JSContext *cx, 
 
     if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
         return JS_FALSE;
 
     obj = JS_THIS_OBJECT(cx, vp);
     return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
 }
 
-#ifdef JS_TRACER
-static JSBool FASTCALL
-Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str)
-{
-    jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str));
-    jsval v;
-
-    if (!js_PropertyIsEnumerable(cx, obj, id, &v)) {
-        SetBuiltinError(cx);
-        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
-    }
-
-    JS_ASSERT(JSVAL_IS_BOOLEAN(v));
-    return JSVAL_TO_BOOLEAN(v);
-}
-#endif
-
 JSBool
 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     JSObject *pobj;
     uintN attrs;
     JSProperty *prop;
     JSBool ok;
 
@@ -1771,17 +1723,17 @@ js_obj_defineGetter(JSContext *cx, uintN
     /*
      * Getters and setters are just like watchpoints from an access
      * control point of view.
      */
     if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs))
         return JS_FALSE;
     *vp = JSVAL_VOID;
     return obj->defineProperty(cx, id, JSVAL_VOID,
-                               js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), JS_PropertyStub,
+                               CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), JS_PropertyStub,
                                JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
 }
 
 JS_FRIEND_API(JSBool)
 js_obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval fval, junk;
     jsid id;
@@ -1804,17 +1756,17 @@ js_obj_defineSetter(JSContext *cx, uintN
     /*
      * Getters and setters are just like watchpoints from an access
      * control point of view.
      */
     if (!obj->checkAccess(cx, id, JSACC_WATCH, &junk, &attrs))
         return JS_FALSE;
     *vp = JSVAL_VOID;
     return obj->defineProperty(cx, id, JSVAL_VOID,
-                               JS_PropertyStub, js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)),
+                               JS_PropertyStub, CastAsPropertyOp(JSVAL_TO_OBJECT(fval)),
                                JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
 }
 
 static JSBool
 obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp)
 {
     jsid id;
     JSObject *obj, *pobj;
@@ -1996,17 +1948,17 @@ obj_keys(JSContext *cx, uintN argc, jsva
     JS_ASSERT(ida.length() <= UINT32_MAX);
     JSObject *aobj = js_NewArrayWithSlots(cx, proto, uint32(ida.length()));
     if (!aobj)
         return JS_FALSE;
     *vp = OBJECT_TO_JSVAL(aobj);
 
     jsval *slots = aobj->dslots;
     size_t len = ida.length();
-    JS_ASSERT(js_DenseArrayCapacity(aobj) >= len);
+    JS_ASSERT(aobj->getDenseArrayCapacity() >= len);
     for (size_t i = 0; i < len; i++) {
         jsid id = ida[i];
         if (JSID_IS_INT(id)) {
             if (!js_ValueToStringId(cx, INT_JSID_TO_JSVAL(id), &slots[i]))
                 return JS_FALSE;
         } else {
             /*
              * Object-valued ids are a possibility admitted by SpiderMonkey for
@@ -2015,17 +1967,17 @@ obj_keys(JSContext *cx, uintN argc, jsva
              * to by a QName, actually appears as a string jsid -- but in the
              * interests of fidelity we pass object jsids through unchanged.
              */
             slots[i] = ID_TO_VALUE(id);
         }
     }
 
     JS_ASSERT(len <= UINT32_MAX);
-    aobj->setArrayCount(len);
+    aobj->setDenseArrayCount(len);
 
     return JS_TRUE;
 }
 
 static JSBool
 HasProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp, JSBool* answerp)
 {
     if (!JS_HasPropertyById(cx, obj, id, answerp))
@@ -2177,18 +2129,18 @@ static JSBool
 Reject(JSContext *cx, JSObject *obj, JSProperty *prop, uintN errorNumber, bool throwError,
        jsid id, bool *rval)
 {
     obj->dropProperty(cx, prop);
     return Reject(cx, errorNumber, throwError, id, rval);
 }
 
 static JSBool
-DefinePropertyObject(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc,
-                     bool throwError, bool *rval)
+DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc,
+                       bool throwError, bool *rval)
 {
     /* 8.12.9 step 1. */
     JSProperty *current;
     JSObject *obj2;
     JS_ASSERT(obj->map->ops->lookupProperty == js_LookupProperty);
     if (!js_HasOwnProperty(cx, js_LookupProperty, obj, desc.id, &obj2, &current))
         return JS_FALSE;
 
@@ -2216,19 +2168,17 @@ DefinePropertyObject(JSContext *cx, JSOb
          */
         jsval dummy;
         uintN dummyAttrs;
         JS_ASSERT(obj->map->ops->checkAccess == js_CheckAccess);
         if (!js_CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
             return JS_FALSE;
 
         return js_DefineProperty(cx, obj, desc.id, JSVAL_VOID,
-                                 desc.getterObject() ? desc.getter() : JS_PropertyStub,
-                                 desc.setterObject() ? desc.setter() : JS_PropertyStub,
-                                 desc.attrs);
+                                 desc.getter(), desc.setter(), desc.attrs);
     }
 
     /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
     jsval v = JSVAL_VOID;
 
     /*
      * In the special case of shared permanent properties, the "own" property
      * can be found on a different object.  In that case the returned property
@@ -2242,23 +2192,25 @@ DefinePropertyObject(JSContext *cx, JSOb
     JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(current);
     do {
         if (desc.isAccessorDescriptor()) {
             if (!sprop->isAccessorDescriptor())
                 break;
 
             if (desc.hasGet &&
                 !js_SameValue(desc.getterValue(),
-                              sprop->hasGetterValue() ? sprop->getterValue() : JSVAL_VOID, cx)) {
+                              sprop->hasGetterValue() ? sprop->getterValue() : JSVAL_VOID,
+                              cx)) {
                 break;
             }
 
             if (desc.hasSet &&
                 !js_SameValue(desc.setterValue(),
-                              sprop->hasSetterValue() ? sprop->setterValue() : JSVAL_VOID, cx)) {
+                              sprop->hasSetterValue() ? sprop->setterValue() : JSVAL_VOID,
+                              cx)) {
                 break;
             }
         } else {
             /*
              * Determine the current value of the property once, if the current
              * value might actually need to be used or preserved later.  NB: we
              * guard on whether the current property is a data descriptor to
              * avoid calling a getter; we won't need the value if it's not a
@@ -2345,18 +2297,19 @@ DefinePropertyObject(JSContext *cx, JSOb
     if (desc.isGenericDescriptor()) {
         /* 8.12.9 step 8, no validation required */
     } else if (desc.isDataDescriptor() != sprop->isDataDescriptor()) {
         /* 8.12.9 step 9. */
         if (!sprop->configurable()) {
             return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
                           throwError, desc.id, rval);
         }
-    } else if (desc.isDataDescriptor() && sprop->isDataDescriptor()) {
+    } else if (desc.isDataDescriptor()) {
         /* 8.12.9 step 10. */
+        JS_ASSERT(sprop->isDataDescriptor());
         if (!sprop->configurable() && !sprop->writable()) {
             if ((desc.hasWritable && desc.writable()) ||
                 (desc.hasValue && !js_SameValue(desc.value, v, cx))) {
                 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
                               throwError, desc.id, rval);
             }
         }
     } else {
@@ -2364,17 +2317,18 @@ DefinePropertyObject(JSContext *cx, JSOb
         JS_ASSERT(desc.isAccessorDescriptor() && sprop->isAccessorDescriptor());
         if (!sprop->configurable()) {
             if ((desc.hasSet &&
                  !js_SameValue(desc.setterValue(),
                                sprop->hasSetterValue() ? sprop->setterValue() : JSVAL_VOID,
                                cx)) ||
                 (desc.hasGet &&
                  !js_SameValue(desc.getterValue(),
-                               sprop->hasGetterValue() ? sprop->getterValue() : JSVAL_VOID, cx)))
+                               sprop->hasGetterValue() ? sprop->getterValue() : JSVAL_VOID,
+                               cx)))
             {
                 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
                               throwError, desc.id, rval);
             }
         }
     }
 
     /* 8.12.9 step 12. */
@@ -2424,34 +2378,40 @@ DefinePropertyObject(JSContext *cx, JSOb
         if (desc.hasEnumerable)
             changed |= JSPROP_ENUMERATE;
         if (desc.hasGet)
             changed |= JSPROP_GETTER | JSPROP_SHARED;
         if (desc.hasSet)
             changed |= JSPROP_SETTER | JSPROP_SHARED;
 
         attrs = (desc.attrs & changed) | (sprop->attributes() & ~changed);
-        if (desc.hasGet)
-            getter = desc.getterObject() ? desc.getter() : JS_PropertyStub;
-        else
-            getter = sprop->hasDefaultGetter() ? JS_PropertyStub : sprop->getter();
-        if (desc.hasSet)
-            setter = desc.setterObject() ? desc.setter() : JS_PropertyStub;
-        else
-            setter = sprop->hasDefaultSetter() ? JS_PropertyStub : sprop->setter();
+        if (desc.hasGet) {
+            getter = desc.getter();
+        } else {
+            getter = (sprop->hasDefaultGetter() && !sprop->hasGetterValue())
+                     ? JS_PropertyStub
+                     : sprop->getter();
+        }
+        if (desc.hasSet) {
+            setter = desc.setter();
+        } else {
+            setter = (sprop->hasDefaultSetter() && !sprop->hasSetterValue())
+                     ? JS_PropertyStub
+                     : sprop->setter();
+        }
     }
 
     *rval = true;
     obj2->dropProperty(cx, current);
     return js_DefineProperty(cx, obj, desc.id, v, getter, setter, attrs);
 }
 
 static JSBool
-DefinePropertyArray(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc,
-                    bool throwError, bool *rval)
+DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc,
+                      bool throwError, bool *rval)
 {
     /*
      * We probably should optimize dense array property definitions where
      * the descriptor describes a traditional array property (enumerable,
      * configurable, writable, numeric index or length without altering its
      * attributes).  Such definitions are probably unlikely, so we don't bother
      * for now.
      */
@@ -2474,44 +2434,44 @@ DefinePropertyArray(JSContext *cx, JSObj
 
     uint32 index;
     if (js_IdIsIndex(desc.id, &index)) {
         /*
         // Disabled until we support defining "length":
         if (index >= oldLen && lengthPropertyNotWritable())
             return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
          */
-        if (!DefinePropertyObject(cx, obj, desc, false, rval))
+        if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
             return JS_FALSE;
         if (!*rval)
             return Reject(cx, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
 
         if (index >= oldLen) {
             JS_ASSERT(index != UINT32_MAX);
-            obj->setArrayLength(index + 1);
+            obj->setSlowArrayLength(index + 1);
         }
 
         *rval = true;
         return JS_TRUE;
     }
 
-    return DefinePropertyObject(cx, obj, desc, throwError, rval);
+    return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
 }
 
 static JSBool
 DefineProperty(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc, bool throwError,
                bool *rval)
 {
     if (obj->isArray())
-        return DefinePropertyArray(cx, obj, desc, throwError, rval);
-
-    if (!obj->isNative())
+        return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
+
+    if (obj->map->ops->lookupProperty != js_LookupProperty)
         return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
 
-    return DefinePropertyObject(cx, obj, desc, throwError, rval);
+    return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
 }
 
 JSBool
 js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, jsval descriptor, JSBool *bp)
 {
     AutoDescriptorArray descs(cx);
     PropertyDescriptor *desc = descs.append();
     if (!desc || !desc->initialize(cx, id, descriptor))
@@ -2666,40 +2626,30 @@ obj_create(JSContext *cx, uintN argc, js
 #if JS_HAS_OBJ_WATCHPOINT
 const char js_watch_str[] = "watch";
 const char js_unwatch_str[] = "unwatch";
 #endif
 const char js_hasOwnProperty_str[] = "hasOwnProperty";
 const char js_isPrototypeOf_str[] = "isPrototypeOf";
 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
 
-JS_DEFINE_TRCINFO_1(obj_valueOf,
-    (3, (static, JSVAL,     Object_p_valueOf,               CONTEXT, THIS, STRING,  0,
-         nanojit::ACC_STORE_ANY)))
-JS_DEFINE_TRCINFO_1(obj_hasOwnProperty,
-    (3, (static, BOOL_FAIL, Object_p_hasOwnProperty,        CONTEXT, THIS, STRING,  0,
-         nanojit::ACC_STORE_ANY)))
-JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable,
-    (3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable,  CONTEXT, THIS, STRING,  0,
-         nanojit::ACC_STORE_ANY)))
-
 static JSFunctionSpec object_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,             obj_toSource,                0,0),
 #endif
     JS_FN(js_toString_str,             obj_toString,                0,0),
     JS_FN(js_toLocaleString_str,       obj_toLocaleString,          0,0),
-    JS_TN(js_valueOf_str,              obj_valueOf,                 0,0, &obj_valueOf_trcinfo),
+    JS_FN(js_valueOf_str,              obj_valueOf,                 0,0),
 #if JS_HAS_OBJ_WATCHPOINT
     JS_FN(js_watch_str,                obj_watch,                   2,0),
     JS_FN(js_unwatch_str,              obj_unwatch,                 1,0),
 #endif
-    JS_TN(js_hasOwnProperty_str,       obj_hasOwnProperty,          1,0, &obj_hasOwnProperty_trcinfo),
+    JS_FN(js_hasOwnProperty_str,       obj_hasOwnProperty,          1,0),
     JS_FN(js_isPrototypeOf_str,        obj_isPrototypeOf,           1,0),
-    JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0, &obj_propertyIsEnumerable_trcinfo),
+    JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable,    1,0),
 #if OLD_GETTER_SETTER_METHODS
     JS_FN(js_defineGetter_str,         js_obj_defineGetter,         2,0),
     JS_FN(js_defineSetter_str,         js_obj_defineSetter,         2,0),
     JS_FN(js_lookupGetter_str,         obj_lookupGetter,            1,0),
     JS_FN(js_lookupSetter_str,         obj_lookupSetter,            1,0),
 #endif
     JS_FS_END
 };
@@ -4245,19 +4195,19 @@ js_DefineNativeProperty(JSContext *cx, J
         cx->runtime->protoHazardShape = js_GenerateShape(cx, false);
 
     /* Lock if object locking is required by this implementation. */
     JS_LOCK_OBJ(cx, obj);
 
     /* Use the object's class getter and setter by default. */
     clasp = obj->getClass();
     if (!(defineHow & JSDNP_SET_METHOD)) {
-        if (!getter)
+        if (!getter && !(attrs & JSPROP_GETTER))
             getter = clasp->getProperty;
-        if (!setter)
+        if (!setter && !(attrs & JSPROP_SETTER))
             setter = clasp->setProperty;
     }
 
     /* Get obj's own scope if it has one, or create a new one for obj. */
     scope = js_GetMutableScope(cx, obj);
     if (!scope)
         goto error;
 
@@ -4268,25 +4218,35 @@ js_DefineNativeProperty(JSContext *cx, J
             JS_ASSERT(clasp == &js_ObjectClass);
             JS_ASSERT(VALUE_IS_FUNCTION(cx, value));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
             JS_ASSERT(!getter && !setter);
 
             JSObject *funobj = JSVAL_TO_OBJECT(value);
             if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
                 flags |= JSScopeProperty::METHOD;
-                getter = js_CastAsPropertyOp(funobj);
+                getter = CastAsPropertyOp(funobj);
             }
         }
 
         added = !scope->hasProperty(id);
+        uint32 oldShape = scope->shape;
         sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT,
                                    attrs, flags, shortid);
         if (!sprop)
             goto error;
+
+        /*
+         * If sprop is a method, the above call to putProperty suffices to
+         * update the shape if necessary. But if scope->branded(), the shape
+         * may not have changed and we may be overwriting a function-valued
+         * property. See bug 560998.
+         */
+        if (scope->shape == oldShape && scope->branded())
+            scope->methodWriteBarrier(cx, sprop->slot, value);
     }
 
     /* Store value before calling addProperty, in case the latter GC's. */
     if (SPROP_HAS_VALID_SLOT(sprop, scope))
         obj->lockedSetSlot(sprop->slot, value);
 
     /* XXXbe called with lock held */
     if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, &value)) {
@@ -4612,42 +4572,48 @@ js_FindIdentifierBase(JSContext *cx, JSO
 
     JSObject *obj = scopeChain;
 
     /*
      * Loop over cacheable objects on the scope chain until we find a
      * property. We also stop when we reach the global object skipping any
      * farther checks or lookups. For details see the JSOP_BINDNAME case of
      * js_Interpret.
+     *
+     * The test order here matters because js_IsCacheableNonGlobalScope
+     * must not be passed a global object (i.e. one with null parent).
      */
-    for (int scopeIndex = 0; js_IsCacheableNonGlobalScope(obj); scopeIndex++) {
+    for (int scopeIndex = 0;
+         !obj->getParent() || js_IsCacheableNonGlobalScope(obj);
+         scopeIndex++) {
         JSObject *pobj;
         JSProperty *prop;
         int protoIndex = js_LookupPropertyWithFlags(cx, obj, id,
                                                     cx->resolveFlags,
                                                     &pobj, &prop);
         if (protoIndex < 0)
             return NULL;
         if (prop) {
             JS_ASSERT(pobj->isNative());
-            JS_ASSERT(pobj->getClass() == obj->getClass());
+            JS_ASSERT(!obj->getParent() ||
+                      pobj->getClass() == obj->getClass());
 #ifdef DEBUG
             PropertyCacheEntry *entry =
 #endif
                 JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
                                            (JSScopeProperty *) prop);
             JS_ASSERT(entry);
             JS_UNLOCK_OBJ(cx, pobj);
             return obj;
         }
 
-        /* Call and other cacheable objects always have a parent. */
-        obj = obj->getParent();
-        if (!obj->getParent())
+        JSObject *parent = obj->getParent();
+        if (!parent)
             return obj;
+        obj = parent;
     }
 
     /* Loop until we find a property or reach the global object. */
     do {
         JSObject *pobj;
         JSProperty *prop;
         if (!obj->lookupProperty(cx, id, &pobj, &prop))
             return NULL;
@@ -4898,38 +4864,29 @@ js_GetMethod(JSContext *cx, JSObject *ob
 #if JS_HAS_XML_SUPPORT
     if (OBJECT_IS_XML(cx, obj))
         return js_GetXMLMethod(cx, obj, id, vp);
 #endif
     return obj->getProperty(cx, id, vp);
 }
 
 JS_FRIEND_API(bool)
-js_CheckUndeclaredVarAssignment(JSContext *cx)
+js_CheckUndeclaredVarAssignment(JSContext *cx, jsval propname)
 {
     JSStackFrame *fp = js_GetTopStackFrame(cx);
     if (!fp)
         return true;
 
     /* If neither cx nor the code is strict, then no check is needed. */
     if (!(fp->script && fp->script->strictModeCode) &&
         !JS_HAS_STRICT_OPTION(cx)) {
         return true;
     }
 
-    /* This check is only appropriate when executing JSOP_SETNAME. */
-    if (!fp->regs ||
-        js_GetOpcode(cx, fp->script, fp->regs->pc) != JSOP_SETNAME) {
-        return true;
-    }
-
-    JSAtom *atom;
-    GET_ATOM_FROM_BYTECODE(fp->script, fp->regs->pc, 0, atom);
-
-    const char *bytes = js_AtomToPrintableString(cx, atom);
+    const char *bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(propname));
     return bytes &&
            JS_ReportErrorFlagsAndNumber(cx,
                                         (JSREPORT_WARNING | JSREPORT_STRICT
                                          | JSREPORT_STRICT_MODE_ERROR),
                                         js_GetErrorMessage, NULL,
                                         JSMSG_UNDECLARED_VAR, bytes);
 }
 
@@ -4960,17 +4917,18 @@ js_SetPropertyHelper(JSContext *cx, JSOb
     JSScopeProperty *sprop;
     JSScope *scope;
     uintN attrs, flags;
     intN shortid;
     JSClass *clasp;
     JSPropertyOp getter, setter;
     bool added;
 
-    JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD)) == 0);
+    JS_ASSERT((defineHow &
+               ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD | JSDNP_UNQUALIFIED)) == 0);
     if (defineHow & JSDNP_CACHE_RESULT)
         JS_ASSERT_NOT_ON_TRACE(cx);
 
     /* Convert string indices to integers if appropriate. */
     id = js_CheckForStringIndex(id);
 
     /*
      * We peek at obj->scope() without locking obj. Any race means a failure
@@ -4987,18 +4945,21 @@ js_SetPropertyHelper(JSContext *cx, JSOb
         if (!pobj->isNative()) {
             pobj->dropProperty(cx, prop);
             prop = NULL;
         }
     } else {
         /* We should never add properties to lexical blocks.  */
         JS_ASSERT(obj->getClass() != &js_BlockClass);
 
-        if (!obj->getParent() && !js_CheckUndeclaredVarAssignment(cx))
+        if (!obj->getParent() &&
+            (defineHow & JSDNP_UNQUALIFIED) &&
+            !js_CheckUndeclaredVarAssignment(cx, ID_TO_VALUE(id))) {
             return JS_FALSE;
+        }
     }
     sprop = (JSScopeProperty *) prop;
 
     /*
      * Now either sprop is null, meaning id was not found in obj or one of its
      * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
      * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
      * is held: we must JSObject::dropProperty or JS_UNLOCK_SCOPE before we
@@ -5134,17 +5095,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb
         if ((defineHow & JSDNP_SET_METHOD) &&
             obj->getClass() == &js_ObjectClass) {
             JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
             JSObject *funobj = JSVAL_TO_OBJECT(*vp);
             if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
                 flags |= JSScopeProperty::METHOD;
-                getter = js_CastAsPropertyOp(funobj);
+                getter = CastAsPropertyOp(funobj);
             }
         }
 
         sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT,
                                    attrs, flags, shortid);
         if (!sprop) {
             JS_UNLOCK_SCOPE(cx, scope);
             return JS_FALSE;
@@ -5178,17 +5139,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb
 
     JS_UNLOCK_SCOPE(cx, scope);
     return JS_TRUE;
 }
 
 JSBool
 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
-    return js_SetPropertyHelper(cx, obj, id, false, vp);
+    return js_SetPropertyHelper(cx, obj, id, 0, vp);
 }
 
 JSBool
 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
                  uintN *attrsp)
 {
     JSBool noprop, ok;
     JSScopeProperty *sprop;
@@ -6458,21 +6419,22 @@ js_GetReservedSlot(JSContext *cx, JSObje
 
 bool
 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
 {
     if (!obj->isNative())
         return true;
 
     JSClass *clasp = obj->getClass();
-    uint32 limit = JSCLASS_RESERVED_SLOTS(clasp);
 
     JS_LOCK_OBJ(cx, obj);
-    if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit))
-        return false;
+#ifdef DEBUG
+    uint32 limit = JSCLASS_RESERVED_SLOTS(clasp);
+    JS_ASSERT(index < limit || ReservedSlotIndexOK(cx, obj, clasp, index, limit));
+#endif
 
     uint32 slot = JSSLOT_START(clasp) + index;
     if (slot >= JS_INITIAL_NSLOTS && !obj->dslots) {
         /*
          * At this point, obj may or may not own scope, and we may or may not
          * need to allocate dslots. If scope is shared, scope->freeslot may not
          * be accurate for obj (see comment below).
          */
@@ -6706,21 +6668,21 @@ js_DumpObject(JSObject *obj)
     JSClass *clasp;
     jsuint reservedEnd;
 
     fprintf(stderr, "object %p\n", (void *) obj);
     clasp = obj->getClass();
     fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
 
     if (obj->isDenseArray()) {
-        slots = JS_MIN(obj->getArrayLength(), js_DenseArrayCapacity(obj));
+        slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity());
         fprintf(stderr, "elements\n");
         for (i = 0; i < slots; i++) {
             fprintf(stderr, " %3d: ", i);
-            dumpValue(obj->dslots[i]);
+            dumpValue(obj->getDenseArrayElement(i));
             fprintf(stderr, "\n");
             fflush(stderr);
         }
         return;
     }
 
     if (obj->isNative()) {
         JSScope *scope = obj->scope();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -92,34 +92,34 @@ struct PropertyDescriptor {
         return (attrs & JSPROP_ENUMERATE) != 0;
     }
 
     bool writable() const {
         return (attrs & JSPROP_READONLY) == 0;
     }
 
     JSObject* getterObject() const {
-        return get != JSVAL_VOID ? JSVAL_TO_OBJECT(get) : NULL;
+        return (get != JSVAL_VOID) ? JSVAL_TO_OBJECT(get) : NULL;
     }
     JSObject* setterObject() const {
-        return set != JSVAL_VOID ? JSVAL_TO_OBJECT(set) : NULL;
+        return (set != JSVAL_VOID) ? JSVAL_TO_OBJECT(set) : NULL;
     }
 
     jsval getterValue() const {
         return get;
     }
     jsval setterValue() const {
         return set;
     }
 
     JSPropertyOp getter() const {
-        return js_CastAsPropertyOp(getterObject());
+        return js::CastAsPropertyOp(getterObject());
     }
     JSPropertyOp setter() const {
-        return js_CastAsPropertyOp(setterObject());
+        return js::CastAsPropertyOp(setterObject());
     }
 
     static void traceDescriptorArray(JSTracer* trc, JSObject* obj);
     static void finalizeDescriptorArray(JSContext* cx, JSObject* obj);
 
     jsid id;
     jsval value, get, set;
 
@@ -287,17 +287,17 @@ struct JSObject {
     bool isSystem() const {
         return (classword & jsuword(2)) != jsuword(0);
     }
 
     void setSystem() {
         classword |= jsuword(2);
     }
 
-    uint32 numSlots(void) {
+    uint32 numSlots(void) const {
         return dslots ? (uint32)dslots[-1] : (uint32)JS_INITIAL_NSLOTS;
     }
 
     jsval& getSlotRef(uintN slot) {
         return (slot < JS_INITIAL_NSLOTS)
                ? fslots[slot]
                : (JS_ASSERT(slot < (uint32)dslots[-1]),
                   dslots[slot - JS_INITIAL_NSLOTS]);
@@ -400,70 +400,94 @@ struct JSObject {
     inline jsval getPrimitiveThis() const;
     inline void setPrimitiveThis(jsval pthis);
 
     /*
      * Array-specific getters and setters (for both dense and slow arrays).
      */
 
   private:
+    // Used by dense and slow arrays.
     static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE;
-    static const uint32 JSSLOT_ARRAY_COUNT  = JSSLOT_PRIVATE + 1;
-    static const uint32 JSSLOT_ARRAY_UNUSED = JSSLOT_PRIVATE + 2;
+
+    // Used only by dense arrays.
+    static const uint32 JSSLOT_DENSE_ARRAY_COUNT     = JSSLOT_PRIVATE + 1;
+    static const uint32 JSSLOT_DENSE_ARRAY_MINLENCAP = JSSLOT_PRIVATE + 2;
 
     // This assertion must remain true;  see comment in js_MakeArraySlow().
     // (Nb: This method is never called, it just contains a static assertion.
     // The static assertion isn't inline because that doesn't work on Mac.)
     inline void staticAssertArrayLengthIsInPrivateSlot();
 
+    inline bool isDenseArrayMinLenCapOk() const;
+
+    inline uint32 uncheckedGetArrayLength() const;
+    inline uint32 uncheckedGetDenseArrayCapacity() const;
+
   public:
     inline uint32 getArrayLength() const;
-    inline void setArrayLength(uint32 length);
+    inline void setDenseArrayLength(uint32 length);
+    inline void setSlowArrayLength(uint32 length);
+
+    inline uint32 getDenseArrayCount() const;
+    inline void setDenseArrayCount(uint32 count);
+    inline void incDenseArrayCountBy(uint32 posDelta);
+    inline void decDenseArrayCountBy(uint32 negDelta);
+
+    inline uint32 getDenseArrayCapacity() const;
+    inline void setDenseArrayCapacity(uint32 capacity); // XXX: bug 558263 will remove this
 
-    inline uint32 getArrayCount() const;
-    inline void voidDenseArrayCount();
-    inline void setArrayCount(uint32 count);
-    inline void incArrayCountBy(uint32 posDelta);
-    inline void decArrayCountBy(uint32 negDelta);
+    inline jsval getDenseArrayElement(uint32 i) const;
+    inline void setDenseArrayElement(uint32 i, jsval v);
 
-    inline void voidArrayUnused();
+    inline jsval *getDenseArrayElements() const;   // returns pointer to the Array's elements array
+    bool resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
+                               bool initializeAllSlots = true);
+    bool ensureDenseArrayElements(JSContext *cx, uint32 newcap,
+                               bool initializeAllSlots = true);
+    inline void freeDenseArrayElements(JSContext *cx);
+
+    inline void voidDenseOnlyArraySlots();  // used when converting a dense array to a slow array
 
     /*
      * Arguments-specific getters and setters.
      */
 
     /*
      * Reserved slot structure for Arguments objects:
      *
      * JSSLOT_PRIVATE       - the corresponding frame until the frame exits.
      * JSSLOT_ARGS_LENGTH   - the number of actual arguments and a flag
      *                        indicating whether arguments.length was
      *                        overwritten.
      * JSSLOT_ARGS_CALLEE   - the arguments.callee value or JSVAL_HOLE if that
      *                        was overwritten.
      *
-     * Argument index i is stored in dslots[i].  But future-proof your code by
-     * using {Get,Set}ArgsSlot instead of naked dslots references.
+     * Argument index i is stored in dslots[i], accessible via
+     * {get,set}ArgsElement().
      */
   private:
     static const uint32 JSSLOT_ARGS_LENGTH = JSSLOT_PRIVATE + 1;
     static const uint32 JSSLOT_ARGS_CALLEE = JSSLOT_PRIVATE + 2;
 
   public:
     /* Number of extra fixed slots besides JSSLOT_PRIVATE. */
     static const uint32 ARGS_FIXED_RESERVED_SLOTS = 2;
 
     inline uint32 getArgsLength() const;
     inline void setArgsLength(uint32 argc);
     inline void setArgsLengthOverridden();
-    inline bool isArgsLengthOverridden();
+    inline bool isArgsLengthOverridden() const;
 
     inline jsval getArgsCallee() const;
     inline void setArgsCallee(jsval callee);
 
+    inline jsval getArgsElement(uint32 i) const;
+    inline void setArgsElement(uint32 i, jsval v);
+
     /*
      * Date-specific getters and setters.
      */
 
   private:
     // The second slot caches the local time;  it's initialized to NaN.
     static const uint32 JSSLOT_DATE_UTC_TIME   = JSSLOT_PRIVATE;
     static const uint32 JSSLOT_DATE_LOCAL_TIME = JSSLOT_PRIVATE + 1;
@@ -918,16 +942,19 @@ js_DefineOwnProperty(JSContext *cx, JSOb
 /*
  * 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 */
 const uintN JSDNP_SET_METHOD   = 4; /* js_{DefineNativeProperty,SetPropertyHelper}
                                        must pass the JSScopeProperty::METHOD
                                        flag on to js_AddScopeProperty */
+const uintN JSDNP_UNQUALIFIED  = 8; /* Unqualified property set.  Only used in
+                                       the defineHow argument of
+                                       js_SetPropertyHelper. */
 
 /*
  * On error, return false.  On success, if propp is non-null, return true with
  * obj locked and with a held property in *propp; if propp is null, return true
  * but release obj's lock first.  Therefore all callers who pass non-null propp
  * result parameters must later call obj->dropProperty(cx, *propp) both to drop
  * the held property, and to release the lock on obj.
  */
@@ -1040,21 +1067,23 @@ js_GetProperty(JSContext *cx, JSObject *
 
 extern JSBool
 js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
 extern JSBool
 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, jsval *vp);
 
 /*
- * Check whether it is OK to assign an undeclared property of the global
- * object at the current script PC.
+ * Check whether it is OK to assign an undeclared property with name
+ * propname of the global object in the current script on cx.  Reports
+ * an error if one needs to be reported (in particular in all cases
+ * when it returns false).
  */
 extern JS_FRIEND_API(bool)
-js_CheckUndeclaredVarAssignment(JSContext *cx);
+js_CheckUndeclaredVarAssignment(JSContext *cx, jsval propname);
 
 extern JSBool
 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
                      jsval *vp);
 
 extern JSBool
 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -110,70 +110,148 @@ JSObject::setPrimitiveThis(jsval pthis)
     fslots[JSSLOT_PRIMITIVE_THIS] = pthis;
 }
 
 inline void JSObject::staticAssertArrayLengthIsInPrivateSlot()
 {
     JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE);
 }
 
+inline bool JSObject::isDenseArrayMinLenCapOk() const
+{
+    JS_ASSERT(isDenseArray());
+    uint32 length = uncheckedGetArrayLength();
+    uint32 capacity = uncheckedGetDenseArrayCapacity();
+    uint32 minLenCap = uint32(fslots[JSSLOT_DENSE_ARRAY_MINLENCAP]);
+    return minLenCap == JS_MIN(length, capacity);
+}
+
+inline uint32
+JSObject::uncheckedGetArrayLength() const
+{
+    return uint32(fslots[JSSLOT_ARRAY_LENGTH]);
+}
+
 inline uint32
 JSObject::getArrayLength() const
 {
     JS_ASSERT(isArray());
-    return uint32(fslots[JSSLOT_ARRAY_LENGTH]);
+    JS_ASSERT_IF(isDenseArray(), isDenseArrayMinLenCapOk());
+    return uncheckedGetArrayLength();
 }
 
 inline void 
-JSObject::setArrayLength(uint32 length)
+JSObject::setDenseArrayLength(uint32 length)
 {
-    JS_ASSERT(isArray());
+    JS_ASSERT(isDenseArray());
+    fslots[JSSLOT_ARRAY_LENGTH] = length;
+    uint32 capacity = uncheckedGetDenseArrayCapacity();
+    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP] = JS_MIN(length, capacity);
+}
+
+inline void 
+JSObject::setSlowArrayLength(uint32 length)
+{
+    JS_ASSERT(isSlowArray());
     fslots[JSSLOT_ARRAY_LENGTH] = length;
 }
 
 inline uint32 
-JSObject::getArrayCount() const
+JSObject::getDenseArrayCount() const
 {
-    JS_ASSERT(isArray());
-    return uint32(fslots[JSSLOT_ARRAY_COUNT]);
+    JS_ASSERT(isDenseArray());
+    return uint32(fslots[JSSLOT_DENSE_ARRAY_COUNT]);
 }
 
 inline void 
-JSObject::setArrayCount(uint32 count)
+JSObject::setDenseArrayCount(uint32 count)
 {
-    JS_ASSERT(isArray());
-    fslots[JSSLOT_ARRAY_COUNT] = count;
+    JS_ASSERT(isDenseArray());
+    fslots[JSSLOT_DENSE_ARRAY_COUNT] = count;
+}
+
+inline void 
+JSObject::incDenseArrayCountBy(uint32 posDelta)
+{
+    JS_ASSERT(isDenseArray());
+    fslots[JSSLOT_DENSE_ARRAY_COUNT] += posDelta;
 }
 
 inline void 
-JSObject::voidDenseArrayCount()
+JSObject::decDenseArrayCountBy(uint32 negDelta)
+{
+    JS_ASSERT(isDenseArray());
+    fslots[JSSLOT_DENSE_ARRAY_COUNT] -= negDelta;
+}
+
+inline uint32
+JSObject::uncheckedGetDenseArrayCapacity() const
+{
+    return dslots ? uint32(dslots[-1]) : 0;
+}
+
+inline uint32
+JSObject::getDenseArrayCapacity() const
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(isDenseArrayMinLenCapOk());
+    return uncheckedGetDenseArrayCapacity();
+}
+
+inline void
+JSObject::setDenseArrayCapacity(uint32 capacity)
 {
     JS_ASSERT(isDenseArray());
-    fslots[JSSLOT_ARRAY_COUNT] = JSVAL_VOID;
+    JS_ASSERT(dslots);
+    dslots[-1] = capacity;
+    uint32 length = uncheckedGetArrayLength();
+    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP] = JS_MIN(length, capacity);
+}
+
+inline jsval
+JSObject::getDenseArrayElement(uint32 i) const
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(i < getDenseArrayCapacity());
+    return dslots[i];
+}
+
+inline void
+JSObject::setDenseArrayElement(uint32 i, jsval v)
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(i < getDenseArrayCapacity());
+    dslots[i] = v;
+}
+
+inline jsval *
+JSObject::getDenseArrayElements() const
+{
+    JS_ASSERT(isDenseArray());
+    return dslots;
+}
+
+inline void
+JSObject::freeDenseArrayElements(JSContext *cx)
+{
+    JS_ASSERT(isDenseArray());
+    if (dslots) {
+        cx->free(dslots - 1);
+        dslots = NULL;
+    }
+    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP] = 0;
+    JS_ASSERT(isDenseArrayMinLenCapOk());
 }
 
 inline void 
-JSObject::incArrayCountBy(uint32 posDelta)
-{
-    JS_ASSERT(isArray());
-    fslots[JSSLOT_ARRAY_COUNT] += posDelta;
-}
-
-inline void 
-JSObject::decArrayCountBy(uint32 negDelta)
+JSObject::voidDenseOnlyArraySlots()
 {
-    JS_ASSERT(isArray());
-    fslots[JSSLOT_ARRAY_COUNT] -= negDelta;
-}
-
-inline void
-JSObject::voidArrayUnused()
-{
-    JS_ASSERT(isArray());
-    fslots[JSSLOT_ARRAY_UNUSED] = JSVAL_VOID;
+    JS_ASSERT(isDenseArray());
+    fslots[JSSLOT_DENSE_ARRAY_COUNT] = JSVAL_VOID;
+    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP] = JSVAL_VOID;
 }
 
 inline void
 JSObject::setArgsLength(uint32 argc)
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
     fslots[JSSLOT_ARGS_LENGTH] = INT_TO_JSVAL(argc << 1);
@@ -195,17 +273,17 @@ JSObject::setArgsLengthOverridden()
     JS_ASSERT(isArguments());
     jsval v = fslots[JSSLOT_ARGS_LENGTH];
     v = INT_TO_JSVAL(JSVAL_TO_INT(v) | 1);
     JS_ASSERT(JSVAL_IS_INT(v));
     fslots[JSSLOT_ARGS_LENGTH] = v;
 }
 
 inline bool
-JSObject::isArgsLengthOverridden()
+JSObject::isArgsLengthOverridden() const
 {
     JS_ASSERT(isArguments());
     jsval v = fslots[JSSLOT_ARGS_LENGTH];
     return (JSVAL_TO_INT(v) & 1) != 0;
 }
 
 inline jsval 
 JSObject::getArgsCallee() const
@@ -217,16 +295,32 @@ JSObject::getArgsCallee() const
 inline void 
 JSObject::setArgsCallee(jsval callee)
 {
     JS_ASSERT(isArguments());
     fslots[JSSLOT_ARGS_CALLEE] = callee;
 }
 
 inline jsval
+JSObject::getArgsElement(uint32 i) const
+{
+    JS_ASSERT(isArguments());
+    JS_ASSERT(i < numSlots() - JS_INITIAL_NSLOTS);
+    return dslots[i];
+}
+
+inline void
+JSObject::setArgsElement(uint32 i, jsval v)
+{
+    JS_ASSERT(isArguments());
+    JS_ASSERT(i < numSlots() - JS_INITIAL_NSLOTS);
+    dslots[i] = v;
+}
+
+inline jsval
 JSObject::getDateLocalTime() const
 {
     JS_ASSERT(isDate());
     return fslots[JSSLOT_DATE_LOCAL_TIME];
 }
 
 inline jsval *
 JSObject::addressOfDateLocalTime()
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -3266,16 +3266,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                          * the distance from the goto around the else, to the
                          * ifeq for the if inside the else that forms an "if
                          * else if" chain.  Thus cond spans the condition of
                          * the second if, so we simply decompile it and start
                          * over at label if_again.
                          */
                         cond = js_GetSrcNoteOffset(sn, 1);
                         if (cond != 0) {
+                            cond -= tail;
                             DECOMPILE_CODE(pc + oplen, cond - oplen);
                             pc += cond;
                             elseif = JS_TRUE;
                             goto if_again;
                         }
 
                         js_printf(jp, " {\n");
                         jp->indent += 4;
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -43,24 +43,24 @@
 #if JS_THREADED_INTERP
   interrupt:
 #else /* !JS_THREADED_INTERP */
   case -1:
     JS_ASSERT(switchMask == -1);
 #endif /* !JS_THREADED_INTERP */
     {
         bool moreInterrupts = false;
-        JSTrapHandler handler = cx->debugHooks->interruptHandler;
-        if (handler) {
+        JSInterruptHook hook = cx->debugHooks->interruptHook;
+        if (hook) {
 #ifdef JS_TRACER
             if (TRACE_RECORDER(cx))
-                AbortRecording(cx, "interrupt handler");
+                AbortRecording(cx, "interrupt hook");
 #endif
-            switch (handler(cx, script, regs.pc, &rval,
-                            cx->debugHooks->interruptHandlerData)) {
+            switch (hook(cx, script, regs.pc, &rval,
+                         cx->debugHooks->interruptHookData)) {
               case JSTRAP_ERROR:
                 goto error;
               case JSTRAP_CONTINUE:
                 break;
               case JSTRAP_RETURN:
                 fp->rval = rval;
                 ok = JS_TRUE;
                 goto forced_return;
@@ -71,16 +71,17 @@
               default:;
             }
             moreInterrupts = true;
         }
 
 #ifdef JS_TRACER
         if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
             AbortableRecordingStatus status = tr->monitorRecording(op);
+            JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
             switch (status) {
               case ARECORD_CONTINUE:
                 moreInterrupts = true;
                 break;
               case ARECORD_IMACRO:
                 atoms = COMMON_ATOMS_START(&rt->atomState);
                 op = JSOp(*regs.pc);
                 DO_OP();    /* keep interrupting for op. */
@@ -252,23 +253,17 @@ BEGIN_CASE(JSOP_STOP)
         /*
          * If fp has a call object, sync values and clear the back-
          * pointer. This can happen for a lightweight function if it calls eval
          * unexpectedly (in a way that is hidden from the compiler). See bug
          * 325540.
          */
         fp->putActivationObjects(cx);
 
-#ifdef INCLUDE_MOZILLA_DTRACE
-        /* DTrace function return, inlines */
-        if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
-            jsdtrace_function_rval(cx, fp, fp->fun, &fp->rval);
-        if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
-            jsdtrace_function_return(cx, fp, fp->fun);
-#endif
+        DTrace::exitJSFun(cx, fp, fp->fun, fp->rval);
 
         /* Restore context version only if callee hasn't set version. */
         if (JS_LIKELY(cx->version == currentVersion)) {
             currentVersion = ifp->callerVersion;
             if (currentVersion != cx->version)
                 js_SetVersion(cx, currentVersion);
         }
 
@@ -1811,19 +1806,23 @@ BEGIN_CASE(JSOP_SETMETHOD)
             if (sprop)
                 break;
         }
 
         if (!atom)
             LOAD_ATOM(0);
         id = ATOM_TO_JSID(atom);
         if (entry && JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {
-            uintN defineHow = (op == JSOP_SETMETHOD)
-                              ? JSDNP_CACHE_RESULT | JSDNP_SET_METHOD
-                              : JSDNP_CACHE_RESULT;
+            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))
                 goto error;
         } else {
             if (!obj->setProperty(cx, id, &rval))
                 goto error;
             ABORT_RECORDING(cx, "Non-native set");
         }
     } while (0);
@@ -1846,18 +1845,18 @@ BEGIN_CASE(JSOP_GETELEM)
     }
 
     VALUE_TO_OBJECT(cx, -2, lval, obj);
     if (JSVAL_IS_INT(rval)) {
         if (obj->isDenseArray()) {
             jsuint idx = jsuint(JSVAL_TO_INT(rval));
 
             if (idx < obj->getArrayLength() &&
-                idx < js_DenseArrayCapacity(obj)) {
-                rval = obj->dslots[idx];
+                idx < obj->getDenseArrayCapacity()) {
+                rval = obj->getDenseArrayElement(idx);
                 if (rval != JSVAL_HOLE)
                     goto end_getelem;
 
                 /* Reload rval from the stack in the rare hole case. */
                 rval = FETCH_OPND(-1);
             }
         } else if (obj->isArguments()
 #ifdef JS_TRACER
@@ -1868,17 +1867,17 @@ BEGIN_CASE(JSOP_GETELEM)
 
             if (arg < obj->getArgsLength()) {
                 JSStackFrame *afp = (JSStackFrame *) obj->getPrivate();
                 if (afp) {
                     rval = afp->argv[arg];
                     goto end_getelem;
                 }
 
-                rval = GetArgsSlot(obj, arg);
+                rval = obj->getArgsElement(arg);
                 if (rval != JSVAL_HOLE)
                     goto end_getelem;
                 rval = FETCH_OPND(-1);
             }
         }
         id = INT_JSVAL_TO_JSID(rval);
     } else {
         if (!js_InternNonIntElementId(cx, obj, rval, &id))
@@ -1911,27 +1910,27 @@ END_CASE(JSOP_CALLELEM)
 BEGIN_CASE(JSOP_SETELEM)
     rval = FETCH_OPND(-1);
     FETCH_OBJECT(cx, -3, lval, obj);
     FETCH_ELEMENT_ID(obj, -2, id);
     do {
         if (obj->isDenseArray() && JSID_IS_INT(id)) {
             jsuint length;
 
-            length = js_DenseArrayCapacity(obj);
+            length = obj->getDenseArrayCapacity();
             i = JSID_TO_INT(id);
             if ((jsuint)i < length) {
-                if (obj->dslots[i] == JSVAL_HOLE) {
+                if (obj->getDenseArrayElement(i) == JSVAL_HOLE) {
                     if (js_PrototypeHasIndexedProperties(cx, obj))
                         break;
                     if ((jsuint)i >= obj->getArrayLength())
-                        obj->setArrayLength(i + 1);
-                    obj->incArrayCountBy(1);
+                        obj->setDenseArrayLength(i + 1);
+                    obj->incDenseArrayCountBy(1);
                 }
-                obj->dslots[i] = rval;
+                obj->setDenseArrayElement(i, rval);
                 goto end_setelem;
             }
         }
     } while (0);
     if (!obj->setProperty(cx, id, &rval))
         goto error;
   end_setelem:
 END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)
@@ -2153,30 +2152,30 @@ BEGIN_CASE(JSOP_APPLY)
                 CHECK_INTERRUPT_HANDLER();
             } else {
                 newifp->hookData = NULL;
             }
 
             inlineCallCount++;
             JS_RUNTIME_METER(rt, inlineCalls);
 
-#ifdef INCLUDE_MOZILLA_DTRACE
-            /* DTrace function entry, inlines */
-            if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
-                jsdtrace_function_entry(cx, fp, fun);
-            if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
-                jsdtrace_function_info(cx, fp, fp->down, fun);
-            if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
-                jsdtrace_function_args(cx, fp, fun, fp->argc, fp->argv);
-#endif
+            DTrace::enterJSFun(cx, fp, fun, fp->down, fp->argc, fp->argv);
 
 #ifdef JS_TRACER
-            if (TRACE_RECORDER(cx)) {
-                TRACE_1(EnterFrame, inlineCallCount);
+            if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
+                AbortableRecordingStatus status = tr->record_EnterFrame(inlineCallCount);
                 RESTORE_INTERP_VARS();
+                if (StatusAbortsRecorderIfActive(status)) {
+                    if (TRACE_RECORDER(cx)) {
+                        JS_ASSERT(TRACE_RECORDER(cx) == tr);
+                        AbortRecording(cx, "record_EnterFrame failed");
+                    }
+                    if (status == ARECORD_ERROR)
+                        goto error;
+                }
             } else if (fp->script == fp->down->script &&
                        *fp->down->regs->pc == JSOP_CALL &&
                        *fp->regs->pc == JSOP_TRACE) {
                 MONITOR_BRANCH(Record_EnterFrame);
             }
 #endif
 
             /* Load first op and dispatch it (safe since JSOP_STOP). */
@@ -2187,40 +2186,23 @@ BEGIN_CASE(JSOP_APPLY)
             JS_ASSERT(fp->regs == &regs);
             script = fp->script;
             atoms = script->atomMap.vector;
             js_FreeRawStack(cx, newmark);
             goto error;
         }
 
         if (fun->flags & JSFUN_FAST_NATIVE) {
-#ifdef INCLUDE_MOZILLA_DTRACE
-            /* DTrace function entry, non-inlines */
-            if (VALUE_IS_FUNCTION(cx, lval)) {
-                if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
-                    jsdtrace_function_entry(cx, NULL, fun);
-                if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
-                    jsdtrace_function_info(cx, NULL, fp, fun);
-                if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
-                    jsdtrace_function_args(cx, fp, fun, argc, vp+2);
-            }
-#endif
+            DTrace::enterJSFun(cx, NULL, fun, fp, argc, vp + 2, &lval);
 
             JS_ASSERT(fun->u.n.extra == 0);
             JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) ||
                       PRIMITIVE_THIS_TEST(fun, vp[1]));
             ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
-#ifdef INCLUDE_MOZILLA_DTRACE
-            if (VALUE_IS_FUNCTION(cx, lval)) {
-                if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
-                    jsdtrace_function_rval(cx, NULL, fun, vp);
-                if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
-                    jsdtrace_function_return(cx, NULL, fun);
-            }
-#endif
+            DTrace::exitJSFun(cx, NULL, fun, *vp, &lval);
             regs.sp = vp + 1;
             if (!ok) {
                 /*
                  * If we are executing the JSOP_NEXTITER imacro and a
                  * Stopiteration exception is raised, transform it into a
                  * JSVAL_HOLE return value.  The tracer generates equivalent
                  * code by calling CatchStopIteration_tn.
                  */
@@ -2915,19 +2897,19 @@ BEGIN_CASE(JSOP_DEFFUN)
     getter = setter = JS_PropertyStub;
     flags = JSFUN_GSFLAG2ATTR(fun->flags);
     if (flags) {
         /* Function cannot be both getter a setter. */
         JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
         attrs |= flags | JSPROP_SHARED;
         rval = JSVAL_VOID;
         if (flags == JSPROP_GETTER)
-            getter = js_CastAsPropertyOp(obj);
+            getter = CastAsPropertyOp(obj);
         else
-            setter = js_CastAsPropertyOp(obj);
+            setter = CastAsPropertyOp(obj);
     }
 
     /*
      * We define the function as a property of the variable object and not the
      * current scope chain even for the case of function expression statements
      * and functions defined by eval inside let or with blocks.
      */
     parent = fp->varobj(cx);
@@ -3015,20 +2997,20 @@ BEGIN_CASE(JSOP_DEFFUN_DBGFC)
         if (attrs == JSPROP_ENUMERATE) {
             JS_ASSERT(fp->flags & JSFRAME_EVAL);
             ok = parent->setProperty(cx, id, &rval);
         } else {
             JS_ASSERT(attrs & JSPROP_PERMANENT);
 
             ok = parent->defineProperty(cx, id, rval,
                                         (flags & JSPROP_GETTER)
-                                        ? js_CastAsPropertyOp(obj)
+                                        ? CastAsPropertyOp(obj)
                                         : JS_PropertyStub,
                                         (flags & JSPROP_SETTER)
-                                        ? js_CastAsPropertyOp(obj)
+                                        ? CastAsPropertyOp(obj)
                                         : JS_PropertyStub,
                                         attrs);
         }
     }
 
     if (!ok)
         goto error;
 END_CASE(JSOP_DEFFUN_FC)
@@ -3241,22 +3223,22 @@ BEGIN_CASE(JSOP_SETTER)
     /*
      * Getters and setters are just like watchpoints from an access control
      * point of view.
      */
     if (!obj->checkAccess(cx, id, JSACC_WATCH, &rtmp, &attrs))
         goto error;
 
     if (op == JSOP_GETTER) {
-        getter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
+        getter = CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
         setter = JS_PropertyStub;
         attrs = JSPROP_GETTER;
     } else {
         getter = JS_PropertyStub;
-        setter = js_CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
+        setter = CastAsPropertyOp(JSVAL_TO_OBJECT(rval));
         attrs = JSPROP_SETTER;
     }
     attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
 
     /* Check for a readonly or permanent property of the same name. */
     if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL))
         goto error;
 
@@ -3660,17 +3642,17 @@ BEGIN_CASE(JSOP_INSTANCEOF)
         goto error;
     regs.sp--;
     STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
 END_CASE(JSOP_INSTANCEOF)
 
 #if JS_HAS_DEBUGGER_KEYWORD
 BEGIN_CASE(JSOP_DEBUGGER)
 {
-    JSTrapHandler handler = cx->debugHooks->debuggerHandler;
+    JSDebuggerHandler handler = cx->debugHooks->debuggerHandler;
     if (handler) {
         switch (handler(cx, script, regs.pc, &rval, cx->debugHooks->debuggerHandlerData)) {
         case JSTRAP_ERROR:
             goto error;
         case JSTRAP_CONTINUE:
             break;
         case JSTRAP_RETURN:
             fp->rval = rval;
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -717,17 +717,16 @@ SetStaticLevel(JSTreeContext *tc, uintN 
 JSScript *
 Compiler::compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
                         JSPrincipals *principals, uint32 tcflags,
                         const jschar *chars, size_t length,
                         FILE *file, const char *filename, uintN lineno,
                         JSString *source /* = NULL */,
                         unsigned staticLevel /* = 0 */)
 {
-    Compiler compiler(cx, principals, callerFrame);
     JSArenaPool codePool, notePool;
     TokenKind tt;
     JSParseNode *pn;
     uint32 scriptGlobals;
     JSScript *script;
     bool inDirectivePrologue;
 #ifdef METER_PARSENODES
     void *sbrk(ptrdiff_t), *before = sbrk(0);
@@ -737,28 +736,31 @@ Compiler::compileScript(JSContext *cx, J
 
     /*
      * The scripted callerFrame can only be given for compile-and-go scripts
      * and non-zero static level requires callerFrame.
      */
     JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
     JS_ASSERT_IF(staticLevel != 0, callerFrame);
 
+    Compiler compiler(cx, principals, callerFrame);
     if (!compiler.init(chars, length, file, filename, lineno))
         return NULL;
 
     JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
                      &cx->scriptStackQuota);
     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
                      &cx->scriptStackQuota);
 
     Parser &parser = compiler.parser;
     TokenStream &tokenStream = parser.tokenStream;
 
     JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
+    if (!cg.init())
+        return NULL;
 
     MUST_FLOW_THROUGH("out");
 
     /* Null script early in case of error, to reduce our code footprint. */
     script = NULL;
 
     cg.flags |= tcflags;
     cg.scopeChain = scopeChain;
@@ -1097,30 +1099,30 @@ HasFinalReturn(JSParseNode *pn)
 }
 
 static JSBool
 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
                 uintN anonerrnum)
 {
     const char *name;
 
-    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
+    JS_ASSERT(tc->inFunction());
     if (tc->fun->atom) {
         name = js_AtomToPrintableString(cx, tc->fun->atom);
     } else {
         errnum = anonerrnum;
         name = NULL;
     }
     return ReportCompileErrorNumber(cx, TS(tc->parser), NULL, flags, errnum, name);
 }
 
 static JSBool
 CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
 {
-    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
+    JS_ASSERT(tc->inFunction());
     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
            ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT,
                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
 }
 
 /*
  * Check that it is permitted to assign to lhs.  Strict mode code may not
  * assign to 'eval' or 'arguments'.
@@ -1224,17 +1226,17 @@ CheckStrictFormals(JSContext *cx, JSTree
 
 JSParseNode *
 Parser::functionBody()
 {
     JSStmtInfo stmtInfo;
     uintN oldflags, firstLine;
     JSParseNode *pn;
 
-    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
+    JS_ASSERT(tc->inFunction());
     js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1);
     stmtInfo.flags = SIF_BODY_BLOCK;
 
     oldflags = tc->flags;
     tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID);
 
     /*
      * Save the body's first line, and store it in pn->pn_pos.begin.lineno
@@ -1522,16 +1524,19 @@ Compiler::compileFunctionBody(JSContext 
                      &cx->scriptStackQuota);
     JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
                      &cx->scriptStackQuota);
 
     Parser &parser = compiler.parser;
     TokenStream &tokenStream = parser.tokenStream;
 
     JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
+    if (!funcg.init())
+        return NULL;
+
     funcg.flags |= TCF_IN_FUNCTION;
     funcg.fun = fun;
     if (!GenerateBlockId(&funcg, funcg.bodyid))
         return NULL;
 
     /* FIXME: make Function format the source for a function definition. */
     tokenStream.mungeCurrentToken(TOK_NAME);
     JSParseNode *fn = FunctionNode::create(&funcg);
@@ -1650,17 +1655,17 @@ BindDestructuringArg(JSContext *cx, Bind
     JSParseNode *pn;
 
     /* Flag tc so we don't have to lookup arguments on every use. */
     if (atom == tc->parser->context->runtime->atomState.argumentsAtom)
         tc->flags |= TCF_FUN_PARAM_ARGUMENTS;
     if (atom == tc->parser->context->runtime->atomState.evalAtom)
         tc->flags |= TCF_FUN_PARAM_EVAL;
 
-    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
+    JS_ASSERT(tc->inFunction());
 
     JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
     if (localKind != JSLOCAL_NONE) {
         ReportCompileErrorNumber(cx, TS(tc->parser), NULL, JSREPORT_ERROR,
                                  JSMSG_DESTRUCT_DUP_ARG);
         return JS_FALSE;
     }
     JS_ASSERT(!tc->decls.lookup(atom));
@@ -1690,22 +1695,22 @@ Parser::newFunction(JSTreeContext *tc, J
     /*
      * Find the global compilation context in order to pre-set the newborn
      * function's parent slot to tc->scopeChain. If the global context is a
      * compile-and-go one, we leave the pre-set parent intact; otherwise we
      * clear parent and proto.
      */
     while (tc->parent)
         tc = tc->parent;
-    parent = (tc->flags & TCF_IN_FUNCTION) ? NULL : tc->scopeChain;
+    parent = tc->inFunction() ? NULL : tc->scopeChain;
 
     fun = js_NewFunction(context, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
                          parent, atom);
 
-    if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
+    if (fun && !tc->compileAndGo()) {
         FUN_OBJECT(fun)->clearParent();
         FUN_OBJECT(fun)->clearProto();
     }
     return fun;
 }
 
 static JSBool
 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
@@ -1986,16 +1991,24 @@ CanFlattenUpvar(JSDefinition *dn, JSFunc
 
         /*
          * If this function is reaching up across an enclosing funarg, then we
          * cannot copy dn's value into a flat closure slot (the display stops
          * working once the funarg escapes).
          */
         if (!afunbox || afunbox->node->isFunArg())
             return false;
+
+        /*
+         * Reaching up for dn across a generator also means we can't flatten,
+         * since the generator iterator does not run until later, in general.
+         * See bug 563034.
+         */
+        if (afunbox->tcflags & TCF_FUN_IS_GENERATOR)
+            return false;
     }
 
     /*
      * If afunbox's function (which is at the same level as dn) is in a loop,
      * pessimistically assume the variable initializer may be in the same loop.
      * A flat closure would then be unsafe, as the captured variable could be
      * assigned after the closure is created. See bug 493232.
      */
@@ -2076,33 +2089,33 @@ CanFlattenUpvar(JSDefinition *dn, JSFunc
             return false;
     }
     return true;
 }
 
 static void
 FlagHeavyweights(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
 {
-    JSFunctionBox *afunbox = funbox->parent;
     uintN dnLevel = dn->frameLevel();
 
-    while (afunbox) {
+    while ((funbox = funbox->parent) != NULL) {
         /*
-         * Notice that afunbox->level is the static level of the definition or
-         * expression of the function parsed into afunbox, not the static level
+         * Notice that funbox->level is the static level of the definition or
+         * expression of the function parsed into funbox, not the static level
          * of its body. Therefore we must add 1 to match dn's level to find the
-         * afunbox whose body contains the dn definition.
+         * funbox whose body contains the dn definition.
          */
-        if (afunbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
-            afunbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
+        if (funbox->level + 1U == dnLevel || (dnLevel == 0 && dn->isLet())) {
+            funbox->tcflags |= TCF_FUN_HEAVYWEIGHT;
             break;
         }
-        afunbox = afunbox->parent;
-    }
-    if (!afunbox && (tcflags & TCF_IN_FUNCTION))
+        funbox->tcflags |= TCF_FUN_ENTRAINS_SCOPES;
+    }
+
+    if (!funbox && (tcflags & TCF_IN_FUNCTION))
         tcflags |= TCF_FUN_HEAVYWEIGHT;
 }
 
 static void
 DeoptimizeUsesWithin(JSDefinition *dn, JSFunctionBox *funbox, uint32& tcflags)
 {
     uintN ndeoptimized = 0;
     const TokenPos &pos = funbox->node->pn_body->pn_pos;
@@ -2167,20 +2180,21 @@ Parser::setFunctionKinds(JSFunctionBox *
 
                 if (funbox->shouldUnbrand(methodSets, slowMethodSets))
                     funbox->tcflags |= TCF_FUN_UNBRAND_THIS;
             }
         }
 
         JSFunction *fun = (JSFunction *) funbox->object;
 
+        JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
+
         FUN_METER(allfun);
         if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
             FUN_METER(heavy);
-            JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
         } else if (pn->pn_type != TOK_UPVARS) {
             /*
              * No lexical dependencies => null closure, for best performance.
              * A null closure needs no scope chain, but alas we've coupled
              * principals-finding to scope (for good fundamental reasons, but
              * the implementation overloads the parent slot and we should fix
              * that). See, e.g., the JSOP_LAMBDA case in jsinterp.cpp.
              *
@@ -2242,17 +2256,18 @@ Parser::setFunctionKinds(JSFunctionBox *
                     }
                 }
                 if (!ale)
                     mutation = false;
 
                 if (nupvars == 0) {
                     FUN_METER(onlyfreevar);
                     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
-                } else if (!mutation && !(funbox->tcflags & TCF_FUN_IS_GENERATOR)) {
+                } else if (!mutation &&
+                           !(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
                     /*
                      * Algol-like functions can read upvars using the dynamic
                      * link (cx->fp/fp->down), optimized using the cx->display
                      * lookup table indexed by static level. They do not need
                      * to entrain and search their environment objects.
                      */
                     FUN_METER(display);
                     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
@@ -2642,17 +2657,17 @@ Parser::functionDef(uintN lambda, bool n
          * object property (it might also need the activation property, if the
          * outer function contains with statements, e.g., but the stack slot
          * wins when jsemit.c's BindNameToSlot can optimize a JSOP_NAME into a
          * JSOP_GETLOCAL bytecode).
          */
         if (topLevel) {
             pn->pn_dflags |= PND_TOPLEVEL;
 
-            if (tc->flags & TCF_IN_FUNCTION) {
+            if (tc->inFunction()) {
                 JSLocalKind localKind;
                 uintN index;
 
                 /*
                  * Define a local in the outer function so that BindNameToSlot
                  * can properly optimize accesses. Note that we need a local
                  * variable, not an argument, for the function statement. Thus
                  * we add a variable even if a parameter with the given name
@@ -3221,30 +3236,79 @@ OuterLet(JSTreeContext *tc, JSStmtInfo *
         if (!stmt)
             return false;
         if (stmt->type == STMT_BLOCK)
             return true;
     }
     return false;
 }
 
+/*
+ * If we are generating global or eval-called-from-global code, bind a "gvar"
+ * here, as soon as possible. The JSOP_GETGVAR, etc., ops speed up interpreted
+ * global variable access by memoizing name-to-slot mappings during execution
+ * of the script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
+ * can't be done due to a pre-existing property of the same name as the var or
+ * const but incompatible attributes/getter/setter/etc, these ops devolve to
+ * JSOP_NAME, etc.
+ *
+ * For now, don't try to lookup eval frame variables at compile time. This is
+ * sub-optimal: we could handle eval-called-from-global-code gvars since eval
+ * gets its own script and frame. The eval-from-function-code case is harder,
+ * since functions do not atomize gvars and then reserve their atom indexes as
+ * stack frame slots.
+ */
+static bool
+BindGvar(JSParseNode *pn, JSTreeContext *tc, bool inWith = false)
+{
+    JS_ASSERT(pn->pn_op == JSOP_NAME);
+    JS_ASSERT(!tc->inFunction());
+
+    if (tc->compiling() && !tc->parser->callerFrame) {
+        JSCodeGenerator *cg = (JSCodeGenerator *) tc;
+
+        /* Index pn->pn_atom so we can map fast global number to name. */
+        JSAtomListElement *ale = cg->atomList.add(tc->parser, pn->pn_atom);
+        if (!ale)
+            return false;
+
+        /* Defend against cg->ngvars 16-bit overflow. */
+        uintN slot = ALE_INDEX(ale);
+        if ((slot + 1) >> 16)
+            return true;
+
+        if ((uint16)(slot + 1) > cg->ngvars)
+            cg->ngvars = (uint16)(slot + 1);
+
+        if (!inWith) {
+            pn->pn_op = JSOP_GETGVAR;
+            pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
+            pn->pn_dflags |= PND_BOUND | PND_GVAR;
+        }
+    }
+
+    return true;
+}
+
 static JSBool
 BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
 {
     JSParseNode *pn = data->pn;
 
+    /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
+    pn->pn_op = JSOP_NAME;
+
     if (!CheckStrictBinding(cx, tc, atom, pn))
         return false;
 
     JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
 
     if (stmt && stmt->type == STMT_WITH) {
-        pn->pn_op = JSOP_NAME;
         data->fresh = false;
-        return JS_TRUE;
+        return tc->inFunction() || BindGvar(pn, tc, true);
     }
 
     JSAtomListElement *ale = tc->decls.lookup(atom);
     JSOp op = data->op;
 
     if (stmt || ale) {
         JSDefinition *dn = ale ? ALE_DEFN(ale) : NULL;
         JSDefinition::Kind dn_kind = dn ? dn->kind() : JSDefinition::VAR;
@@ -3369,53 +3433,18 @@ BindVarOrConst(JSContext *cx, BindData *
         ALE_SET_DEFN(ale, pn);
         pn->pn_defn = true;
         pn->pn_dflags &= ~PND_PLACEHOLDER;
     }
 
     if (data->op == JSOP_DEFCONST)
         pn->pn_dflags |= PND_CONST;
 
-    if (!(tc->flags & TCF_IN_FUNCTION)) {
-        /*
-         * If we are generating global or eval-called-from-global code, bind a
-         * "gvar" here, as soon as possible. The JSOP_GETGVAR, etc., ops speed
-         * up global variable access by memoizing name-to-slot mappings in the
-         * script prolog (via JSOP_DEFVAR/JSOP_DEFCONST). If the memoization
-         * can't be done due to a pre-existing property of the same name as the
-         * var or const but incompatible attributes/getter/setter/etc, these
-         * ops devolve to JSOP_NAME, etc.
-         *
-         * For now, don't try to lookup eval frame variables at compile time.
-         * Seems sub-optimal: why couldn't we find eval-called-from-a-function
-         * upvars early and possibly simplify jsemit.cpp:BindNameToSlot?
-         */
-        pn->pn_op = JSOP_NAME;
-        if ((tc->flags & TCF_COMPILING) && !tc->parser->callerFrame) {
-            JSCodeGenerator *cg = (JSCodeGenerator *) tc;
-
-            /* Index atom so we can map fast global number to name. */
-            ale = cg->atomList.add(tc->parser, atom);
-            if (!ale)
-                return JS_FALSE;
-
-            /* Defend against cg->ngvars 16-bit overflow. */
-            uintN slot = ALE_INDEX(ale);
-            if ((slot + 1) >> 16)
-                return JS_TRUE;
-
-            if ((uint16)(slot + 1) > cg->ngvars)
-                cg->ngvars = (uint16)(slot + 1);
-
-            pn->pn_op = JSOP_GETGVAR;
-            pn->pn_cookie = MAKE_UPVAR_COOKIE(tc->staticLevel, slot);
-            pn->pn_dflags |= PND_BOUND | PND_GVAR;
-        }
-        return JS_TRUE;
-    }
+    if (!tc->inFunction())
+        return BindGvar(pn, tc);
 
     if (atom == cx->runtime->atomState.argumentsAtom) {
         pn->pn_op = JSOP_ARGUMENTS;
         pn->pn_dflags |= PND_BOUND;
         return JS_TRUE;
     }
 
     JSLocalKind localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
@@ -3441,17 +3470,16 @@ BindVarOrConst(JSContext *cx, BindData *
 
     if (localKind == JSLOCAL_ARG) {
         /* We checked errors and strict warnings earlier -- see above. */
         JS_ASSERT(ale && ALE_DEFN(ale)->kind() == JSDefinition::ARG);
     } else {
         /* Not an argument, must be a redeclared local var. */
         JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
     }
-    pn->pn_op = JSOP_NAME;
     return JS_TRUE;
 }
 
 static JSBool
 MakeSetCall(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN msg)
 {
     JSParseNode *pn2;
 
@@ -4170,17 +4198,17 @@ ContainsStmt(JSParseNode *pn, TokenKind 
 
 JSParseNode *
 Parser::returnOrYield(bool useAssignExpr)
 {
     TokenKind tt, tt2;
     JSParseNode *pn, *pn2;
 
     tt = tokenStream.currentToken().type;
-    if (tt == TOK_RETURN && !(tc->flags & TCF_IN_FUNCTION)) {
+    if (tt == TOK_RETURN && !tc->inFunction()) {
         ReportCompileErrorNumber(context, &tokenStream, NULL, JSREPORT_ERROR,
                                  JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
         return NULL;
     }
 
     pn = UnaryNode::create(tc);
     if (!pn)
         return NULL;
@@ -5630,17 +5658,17 @@ Parser::statement()
 
     /* Check termination of this primitive statement. */
     return MatchOrInsertSemicolon(context, &tokenStream) ? pn : NULL;
 }
 
 static void
 NoteArgumentsUse(JSTreeContext *tc)
 {
-    JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
+    JS_ASSERT(tc->inFunction());
     tc->flags |= TCF_FUN_USES_ARGUMENTS;
     if (tc->funbox)
         tc->funbox->node->pn_dflags |= PND_FUNARG;
 }
 
 JSParseNode *
 Parser::variables(bool inLetHead)
 {
@@ -5800,17 +5828,17 @@ Parser::variables(bool inLetHead)
                          ? JSOP_SETCONST
                          : JSOP_SETNAME;
 
             NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
 
             /* The declarator's position must include the initializer. */
             pn2->pn_pos.end = init->pn_pos.end;
 
-            if ((tc->flags & TCF_IN_FUNCTION) &&
+            if (tc->inFunction() &&
                 atom == context->runtime->atomState.argumentsAtom) {
                 NoteArgumentsUse(tc);
                 if (!let)
                     tc->flags |= TCF_FUN_HEAVYWEIGHT;
             }
         }
     } while (tokenStream.matchToken(TOK_COMMA));
 
@@ -8354,17 +8382,17 @@ Parser::primaryExpr(TokenKind tt, JSBool
             return NULL;
 
         obj = js_NewRegExpObject(context, &tokenStream,
                                  tokenStream.getTokenbuf().begin(),
                                  tokenStream.getTokenbuf().length(),
                                  tokenStream.currentToken().t_reflags);
         if (!obj)
             return NULL;
-        if (!(tc->flags & TCF_COMPILE_N_GO)) {
+        if (!tc->compileAndGo()) {
             obj->clearParent();
             obj->clearProto();
         }
 
         pn->pn_objbox = tc->parser->newObjectBox(obj);
         if (!pn->pn_objbox)
             return NULL;
 
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -86,20 +86,22 @@
 /* Scalar typedefs. */
 typedef uint8  jsbytecode;
 typedef uint8  jssrcnote;
 typedef uint32 jsatomid;
 
 #ifdef __cplusplus
 
 /* Class and struct forward declarations in namespace js. */
+extern "C++" {
 namespace js {
 struct Parser;
 struct Compiler;
 }
+}
 
 #endif
 
 /* Struct typedefs. */
 typedef struct JSArgumentFormatMap  JSArgumentFormatMap;
 typedef struct JSCodeGenerator      JSCodeGenerator;
 typedef struct JSGCThing            JSGCThing;
 typedef struct JSGenerator          JSGenerator;
@@ -179,42 +181,55 @@ template <class T,
           class HashPolicy = DefaultHasher<T>,
           class AllocPolicy = ContextAllocPolicy>
 class HashSet;
 
 class DeflatedStringCache;
 
 class PropertyCache;
 struct PropertyCacheEntry;
+
+static inline JSPropertyOp
+CastAsPropertyOp(JSObject *object)
+{
+    return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object);
+}
+
 } /* namespace js */
 
 /* Common instantiations. */
 typedef js::Vector<jschar, 32> JSCharBuffer;
 
-static inline JSPropertyOp
-js_CastAsPropertyOp(JSObject *object)
-{
-    return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object);
-}
-
 } /* export "C++" */
 #endif  /* __cplusplus */
 
 /* "Friend" types used by jscntxt.h and jsdbgapi.h. */
 typedef enum JSTrapStatus {
     JSTRAP_ERROR,
     JSTRAP_CONTINUE,
     JSTRAP_RETURN,
     JSTRAP_THROW,
     JSTRAP_LIMIT
 } JSTrapStatus;
 
 typedef JSTrapStatus
 (* JSTrapHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
-                  void *closure);
+                  jsval closure);
+
+typedef JSTrapStatus
+(* JSInterruptHook)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
+                    void *closure);
+
+typedef JSTrapStatus
+(* JSDebuggerHandler)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
+                      void *closure);
+
+typedef JSTrapStatus
+(* JSThrowHook)(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval,
+                void *closure);
 
 typedef JSBool
 (* JSWatchPointHandler)(JSContext *cx, JSObject *obj, jsval id, jsval old,
                         jsval *newp, void *closure);
 
 /* called just after script creation */
 typedef void
 (* JSNewScriptHook)(JSContext  *cx,
@@ -266,33 +281,33 @@ typedef void *
 typedef void
 (* JSObjectHook)(JSContext *cx, JSObject *obj, JSBool isNew, void *closure);
 
 typedef JSBool
 (* JSDebugErrorHook)(JSContext *cx, const char *message, JSErrorReport *report,
                      void *closure);
 
 typedef struct JSDebugHooks {
-    JSTrapHandler       interruptHandler;
-    void                *interruptHandlerData;
+    JSInterruptHook     interruptHook;
+    void                *interruptHookData;
     JSNewScriptHook     newScriptHook;
     void                *newScriptHookData;
     JSDestroyScriptHook destroyScriptHook;
     void                *destroyScriptHookData;
-    JSTrapHandler       debuggerHandler;
+    JSDebuggerHandler   debuggerHandler;
     void                *debuggerHandlerData;
     JSSourceHandler     sourceHandler;
     void                *sourceHandlerData;
     JSInterpreterHook   executeHook;
     void                *executeHookData;
     JSInterpreterHook   callHook;
     void                *callHookData;
     JSObjectHook        objectHook;
     void                *objectHookData;
-    JSTrapHandler       throwHook;
+    JSThrowHook         throwHook;
     void                *throwHookData;
     JSDebugErrorHook    debugErrorHook;
     void                *debugErrorHookData;
 } JSDebugHooks;
 
 /* JSObjectOps function pointer typedefs. */
 
 /*
--- a/js/src/jsrecursion.cpp
+++ b/js/src/jsrecursion.cpp
@@ -116,26 +116,26 @@ class UpRecursiveSlotMap : public Recurs
         lir->insStore(lirbuf->sp, lirbuf->state, offsetof(TracerState, sp), ACC_OTHER);
         lirbuf->rp = lir->ins2(LIR_piadd, lirbuf->rp,
                                lir->insImmWord(-int(sizeof(FrameInfo*))));
         lir->insStore(lirbuf->rp, lirbuf->state, offsetof(TracerState, rp), ACC_OTHER);
     }
 };
 
 #if defined DEBUG
-static JS_REQUIRES_STACK void
-AssertDownFrameIsConsistent(JSContext* cx, VMSideExit* anchor, FrameInfo* fi)
+JS_REQUIRES_STACK void
+TraceRecorder::assertDownFrameIsConsistent(VMSideExit* anchor, FrameInfo* fi)
 {
     JS_ASSERT(anchor->recursive_down);
     JS_ASSERT(anchor->recursive_down->callerHeight == fi->callerHeight);
 
     unsigned downPostSlots = fi->callerHeight;
     TraceType* typeMap = fi->get_typemap();
 
-    CaptureStackTypes(cx, 1, typeMap);
+    captureStackTypes(1, typeMap);
     const TraceType* m1 = anchor->recursive_down->get_typemap();
     for (unsigned i = 0; i < downPostSlots; i++) {
         if (m1[i] == typeMap[i])
             continue;
         if ((typeMap[i] == TT_INT32 && m1[i] == TT_DOUBLE) ||
             (typeMap[i] == TT_DOUBLE && m1[i] == TT_INT32)) {
             continue;
         }
@@ -253,25 +253,25 @@ TraceRecorder::upRecursion()
 
     if (anchor && anchor->exitType == RECURSIVE_MISMATCH_EXIT) {
         /*
          * Case 0: Anchoring off a RECURSIVE_MISMATCH guard. Guard on this FrameInfo.
          * This is always safe because this point is only reached on simple "call myself"
          * recursive functions.
          */
 #if defined DEBUG
-        AssertDownFrameIsConsistent(cx, anchor, fi);
+        assertDownFrameIsConsistent(anchor, fi);
 #endif
         fi = anchor->recursive_down;
     } else if (recursive_pc != fragment->root->ip) {
         /*
          * Case 1: Guess that down-recursion has to started back out, infer types
          * from the down frame.
          */
-        CaptureStackTypes(cx, 1, fi->get_typemap());
+        captureStackTypes(1, fi->get_typemap());
     } else {
         /* Case 2: Guess that up-recursion is backing out, infer types from our Tree. */
         JS_ASSERT(tree->nStackTypes == downPostSlots + 1);
         TraceType* typeMap = fi->get_typemap();
         for (unsigned i = 0; i < downPostSlots; i++)
             typeMap[i] = tree->typeMap[i];
     }
 
@@ -486,17 +486,17 @@ TraceRecorder::slurpDownFrames(jsbytecod
 
     /*
      * Build the exit typemap. This may capture extra types, but they are
      * thrown away.
      */
     TraceType* typeMap = exit->stackTypeMap();
     jsbytecode* oldpc = cx->fp->regs->pc;
     cx->fp->regs->pc = exit->pc;
-    CaptureStackTypes(cx, frameDepth, typeMap);
+    captureStackTypes(frameDepth, typeMap);
     cx->fp->regs->pc = oldpc;
     if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) {
         JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP);
         if (*cx->fp->regs->pc == JSOP_RETURN)
             typeMap[downPostSlots] = determineSlotType(&stackval(-1));
         else
             typeMap[downPostSlots] = TT_VOID;
     } else {
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -348,16 +348,48 @@ typedef struct REGlobalData {
     REBackTrackData *backTrackStack;/* stack of matched-so-far positions */
     REBackTrackData *backTrackSP;
     size_t backTrackStackSize;
     size_t cursz;                   /* size of current stack entry */
     size_t backTrackCount;          /* how many times we've backtracked */
     size_t backTrackLimit;          /* upper limit on backtrack states */
 } REGlobalData;
 
+void
+JSRegExpStatics::clearRoots()
+{
+    input = NULL;
+    cx->runtime->gcPoke = JS_TRUE;
+}
+
+bool
+JSRegExpStatics::copy(const JSRegExpStatics& other)
+{
+    clearRoots();
+    input = other.input;
+    multiline = other.multiline;
+    lastMatch = other.lastMatch;
+    lastParen = other.lastParen;
+    leftContext = other.leftContext;
+    rightContext = other.rightContext;
+    if (!parens.resize(other.parens.length()))
+        return false;
+    memcpy(parens.begin(), other.parens.begin(), sizeof(JSSubString) * parens.length());
+    return true;
+}
+
+void
+JSRegExpStatics::clear()
+{
+    clearRoots();
+    multiline = false;
+    lastMatch = lastParen = leftContext = rightContext = js_EmptySubString;
+    parens.clear();
+}
+
 /*
  * 1. If IgnoreCase is false, return ch.
  * 2. Let u be ch converted to upper case as if by calling
  *    String.prototype.toUpperCase on the one-character string ch.
  * 3. If u does not consist of a single character, return ch.
  * 4. Let cu be u's character.
  * 5. If ch's code point value is greater than or equal to decimal 128 and cu's
  *    code point value is less than decimal 128, then return ch.
@@ -4860,21 +4892,20 @@ JSBool
 js_ExecuteRegExp(JSContext *cx, JSRegExp *re, JSString *str, size_t *indexp,
                  JSBool test, jsval *rval)
 {
     REGlobalData gData;
     REMatchState *x, *result;
 
     const jschar *cp, *ep;
     size_t i, length, start;
-    JSSubString *morepar;
     JSBool ok;
     JSRegExpStatics *res;
     ptrdiff_t matchlen;
-    uintN num, morenum;
+    uintN num;
     JSString *parstr, *matchstr;
     JSObject *obj;
 
     RECapture *parsub = NULL;
     void *mark;
     int64 *timestamp;
 
     /*
@@ -4968,55 +4999,32 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp
             ok = JS_FALSE;
             goto out;
         }
         DEFVAL(STRING_TO_JSVAL(matchstr), INT_TO_JSID(0));
     }
 
     res = &cx->regExpStatics;
     res->input = str;
-    res->parenCount = uint16(re->parenCount);
+    if (!res->parens.resize(re->parenCount)) {
+        ok = JS_FALSE;
+        goto out;
+    }
     if (re->parenCount == 0) {
         res->lastParen = js_EmptySubString;
     } else {
         for (num = 0; num < re->parenCount; num++) {
+            JSSubString *sub = &res->parens[num];
             parsub = &result->parens[num];
-            if (num < 9) {
-                if (parsub->index == -1) {
-                    res->parens[num].chars = NULL;
-                    res->parens[num].length = 0;
-                } else {
-                    res->parens[num].chars = gData.cpbegin + parsub->index;
-                    res->parens[num].length = parsub->length;
-                }
+            if (parsub->index == -1) {
+                sub->chars = NULL;
+                sub->length = 0;
             } else {
-                morenum = num - 9;
-                morepar = res->moreParens;
-                if (!morepar) {
-                    res->moreLength = 10;
-                    morepar = (JSSubString*)
-                        cx->malloc(10 * sizeof(JSSubString));
-                } else if (morenum >= res->moreLength) {
-                    res->moreLength += 10;
-                    morepar = (JSSubString*)
-                        cx->realloc(morepar,
-                                    res->moreLength * sizeof(JSSubString));
-                }
-                if (!morepar) {
-                    ok = JS_FALSE;
-                    goto out;
-                }
-                res->moreParens = morepar;
-                if (parsub->index == -1) {
-                    morepar[morenum].chars = NULL;
-                    morepar[morenum].length = 0;
-                } else {
-                    morepar[morenum].chars = gData.cpbegin + parsub->index;
-                    morepar[morenum].length = parsub->length;
-                }
+                sub->chars = gData.cpbegin + parsub->index;
+                sub->length = parsub->length;
             }
             if (test)
                 continue;
             if (parsub->index == -1) {
                 ok = js_DefineProperty(cx, obj, INT_TO_JSID(num + 1), JSVAL_VOID, NULL, NULL,
                                        JSPROP_ENUMERATE);
             } else {
                 parstr = js_NewDependentString(cx, str,
@@ -5204,34 +5212,28 @@ js_InitRegExpStatics(JSContext *cx)
 
     JS_ClearRegExpStatics(cx);
 }
 
 JS_FRIEND_API(void)
 js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                              AutoValueRooter *tvr)
 {
-    *statics = cx->regExpStatics;
+    statics->copy(cx->regExpStatics);
     if (statics->input)
         tvr->setString(statics->input);
-    /*
-     * Prevent JS_ClearRegExpStatics from freeing moreParens, since we've only
-     * moved it elsewhere (into statics->moreParens).
-     */
-    cx->regExpStatics.moreParens = NULL;
     JS_ClearRegExpStatics(cx);
 }
 
 JS_FRIEND_API(void)
 js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                         AutoValueRooter *tvr)
 {
     /* Clear/free any new JSRegExpStatics data before clobbering. */
-    JS_ClearRegExpStatics(cx);
-    cx->regExpStatics = *statics;
+    cx->regExpStatics.copy(*statics);
 }
 
 void
 js_TraceRegExpStatics(JSTracer *trc, JSContext *acx)
 {
     JSRegExpStatics *res = &acx->regExpStatics;
 
     if (res->input)
@@ -5273,17 +5275,17 @@ regexp_static_getProperty(JSContext *cx,
         break;
       case REGEXP_STATIC_LEFT_CONTEXT:
         sub = &res->leftContext;
         break;
       case REGEXP_STATIC_RIGHT_CONTEXT:
         sub = &res->rightContext;
         break;
       default:
-        sub = REGEXP_PAREN_SUBSTRING(res, slot);
+        sub = (size_t(slot) < res->parens.length()) ? &res->parens[slot] : &js_EmptySubString;
         break;
     }
     str = js_NewStringCopyN(cx, sub->chars, sub->length);
     if (!str)
         return JS_FALSE;
     *vp = STRING_TO_JSVAL(str);
     return JS_TRUE;
 }
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -47,29 +47,16 @@
 #include "jsstr.h"
 
 #ifdef JS_THREADSAFE
 #include "jsdhash.h"
 #endif
 
 JS_BEGIN_EXTERN_C
 
-struct JSRegExpStatics {
-    JSString    *input;         /* input string to match (perl $_, GC root) */
-    JSBool      multiline;      /* whether input contains newlines (perl $*) */
-    uint16      parenCount;     /* number of valid elements in parens[] */
-    uint16      moreLength;     /* number of allocated elements in moreParens */
-    JSSubString parens[9];      /* last set of parens matched (perl $1, $2) */
-    JSSubString *moreParens;    /* null or realloc'd vector for $10, etc. */
-    JSSubString lastMatch;      /* last string matched (perl $&) */
-    JSSubString lastParen;      /* last paren matched (perl $+) */
-    JSSubString leftContext;    /* input to left of last match (perl $`) */
-    JSSubString rightContext;   /* input to right of last match (perl $') */
-};
-
 namespace js { class AutoValueRooter; }
 
 extern JS_FRIEND_API(void)
 js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                              js::AutoValueRooter *tvr);
 
 extern JS_FRIEND_API(void)
 js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
@@ -91,27 +78,16 @@ typedef struct RECharSet {
         uint8       *bits;
         struct {
             size_t  startIndex;
             size_t  length;
         } src;
     } u;
 } RECharSet;
 
-/*
- * This macro is safe because moreParens is guaranteed to be allocated and big
- * enough to hold parenCount, or else be null when parenCount is 0.
- */
-#define REGEXP_PAREN_SUBSTRING(res, num)                                      \
-    (((jsuint)(num) < (jsuint)(res)->parenCount)                              \
-     ? ((jsuint)(num) < 9)                                                    \
-       ? &(res)->parens[num]                                                  \
-       : &(res)->moreParens[(num) - 9]                                        \
-     : &js_EmptySubString)
-
 typedef struct RENode RENode;
 
 struct JSRegExp {
     jsrefcount   nrefs;         /* reference count */
     uint16       flags;         /* flags, see jsapi.h's JSREG_* defines */
     size_t       parenCount;    /* number of parenthesized submatches */
     size_t       classCount;    /* count [...] bitmaps */
     RECharSet    *classList;    /* list of [...] bitmaps */
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -763,26 +763,30 @@ JSScope::addProperty(JSContext *cx, jsid
  * SPROP_CALL_[GS]ETTER macros.
  */
 static inline bool
 NormalizeGetterAndSetter(JSContext *cx, JSScope *scope,
                          jsid id, uintN attrs, uintN flags,
                          JSPropertyOp &getter,
                          JSPropertyOp &setter)
 {
-    if (setter == JS_PropertyStub)
+    if (setter == JS_PropertyStub) {
+        JS_ASSERT(!(attrs & JSPROP_SETTER));
         setter = NULL;
+    }
     if (flags & JSScopeProperty::METHOD) {
         /* Here, getter is the method, a function object reference. */
         JS_ASSERT(getter);
         JS_ASSERT(!setter || setter == js_watch_set);
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     } else {
-        if (getter == JS_PropertyStub)
+        if (getter == JS_PropertyStub) {
+            JS_ASSERT(!(attrs & JSPROP_GETTER));
             getter = NULL;
+        }
     }
 
     /*
      * Check for a watchpoint on a deleted property; if one exists, change
      * setter to js_watch_set or js_watch_set_wrapper.
      * XXXbe this could get expensive with lots of watchpoints...
      */
     if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&
@@ -1153,17 +1157,17 @@ JSScope::clear(JSContext *cx)
 void
 JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop)
 {
     JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
     generateOwnShape(cx);
 }
 
 bool
-JSScope::methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval)
+JSScope::methodShapeChange(JSContext *cx, JSScopeProperty *sprop)
 {
     JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
     if (sprop->isMethod()) {
 #ifdef DEBUG
         jsval prev = object->lockedGetSlot(sprop->slot);
         JS_ASSERT(sprop->methodValue() == prev);
         JS_ASSERT(hasMethodBarrier());
         JS_ASSERT(object->getClass() == &js_ObjectClass);
@@ -1184,25 +1188,25 @@ JSScope::methodShapeChange(JSContext *cx
             return false;
     }
 
     generateOwnShape(cx);
     return true;
 }
 
 bool
-JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval)
+JSScope::methodShapeChange(JSContext *cx, uint32 slot)
 {
     if (!hasMethodBarrier()) {
         generateOwnShape(cx);
     } else {
         for (JSScopeProperty *sprop = lastProp; sprop; sprop = sprop->parent) {
             JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
             if (sprop->slot == slot)
-                return methodShapeChange(cx, sprop, toval);
+                return methodShapeChange(cx, sprop);
         }
     }
     return true;
 }
 
 void
 JSScope::protoShapeChange(JSContext *cx)
 {
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -368,18 +368,18 @@ struct JSScope : public JSObjectMap
      * below) parallels this.
      */
     bool methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, jsval v);
     bool methodWriteBarrier(JSContext *cx, uint32 slot, jsval v);
 
     void trace(JSTracer *trc);
 
     void deletingShapeChange(JSContext *cx, JSScopeProperty *sprop);
-    bool methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval);
-    bool methodShapeChange(JSContext *cx, uint32 slot, jsval toval);
+    bool methodShapeChange(JSContext *cx, JSScopeProperty *sprop);
+    bool methodShapeChange(JSContext *cx, uint32 slot);
     void protoShapeChange(JSContext *cx);
     void shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop);
     bool globalObjectOwnShapeChange(JSContext *cx);
 
 /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
 #define SCOPE_CAPACITY(scope)   JS_BIT(JS_DHASH_BITS-(scope)->hashShift)
 
     enum {
@@ -570,50 +570,56 @@ JSObject::lockedSetSlot(uintN slot, jsva
     OBJ_CHECK_SLOT(this, slot);
     this->setSlot(slot, value);
 }
 
 /*
  * Helpers for reinterpreting JSPropertyOp as JSObject* for scripted getters
  * and setters.
  */
+namespace js {
+
 inline JSObject *
-js_CastAsObject(JSPropertyOp op)
+CastAsObject(JSPropertyOp op)
 {
     return JS_FUNC_TO_DATA_PTR(JSObject *, op);
 }
 
 inline jsval
-js_CastAsObjectJSVal(JSPropertyOp op)
+CastAsObjectJSVal(JSPropertyOp op)
 {
     return OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, op));
 }
 
-namespace js {
 class PropertyTree;
-}
+
+} /* namespace js */
 
 struct JSScopeProperty {
     friend struct JSScope;
     friend class js::PropertyTree;
     friend JSDHashOperator js::RemoveNodeIfDead(JSDHashTable *table, JSDHashEntryHdr *hdr,
                                                 uint32 number, void *arg);
     friend void js::SweepScopeProperties(JSContext *cx);
 
     jsid            id;                 /* int-tagged jsval/untagged JSAtom* */
 
   private:
     union {
-        JSPropertyOp rawGetter;         /* getter and setter hooks or objects */
+        JSPropertyOp    rawGetter;      /* getter and setter hooks or objects */
+        JSObject        *getterObj;     /* user-defined callable "get" object or
+                                           null if sprop->hasGetterValue() */
         JSScopeProperty *next;          /* next node in freelist */
     };
 
     union {
-        JSPropertyOp rawSetter;         /* getter is JSObject* and setter is 0
+        JSPropertyOp    rawSetter;      /* getter is JSObject* and setter is 0
                                            if sprop->isMethod() */
+        JSObject        *setterObj;     /* user-defined callable "set" object or
+                                           null if sprop->hasSetterValue() */
         JSScopeProperty **prevp;        /* pointer to previous node's next, or
                                            pointer to head of freelist */
     };
 
     void insertFree(JSScopeProperty *&list) {
         id = JSVAL_NULL;
         next = list;
         prevp = &list;
@@ -668,20 +674,18 @@ struct JSScopeProperty {
         IN_DICTIONARY   = 0x20
     };
 
     JSScopeProperty(jsid id, JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
                     uintN attrs, uintN flags, intN shortid)
         : id(id), rawGetter(getter), rawSetter(setter), slot(slot), attrs(uint8(attrs)),
           flags(uint8(flags)), shortid(int16(shortid))
     {
-        JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER),
-                     JSVAL_TO_OBJECT(getterValue())->isCallable());
-        JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER),
-                     JSVAL_TO_OBJECT(setterValue())->isCallable());
+        JS_ASSERT_IF(getter && (attrs & JSPROP_GETTER), getterObj->isCallable());
+        JS_ASSERT_IF(setter && (attrs & JSPROP_SETTER), setterObj->isCallable());
     }
 
     bool marked() const { return (flags & MARK) != 0; }
     void mark() { flags |= MARK; }
     void clearMark() { flags &= ~MARK; }
 
     bool hasRegenFlag() const { return (flags & SHAPE_REGEN) != 0; }
     void setRegenFlag() { flags |= SHAPE_REGEN; }
@@ -693,58 +697,44 @@ struct JSScopeProperty {
     /* Public bits stored in sprop->flags. */
     enum {
         ALIAS           = 0x02,
         HAS_SHORTID     = 0x04,
         METHOD          = 0x10,
         PUBLIC_FLAGS    = ALIAS | HAS_SHORTID | METHOD
     };
 
-    uintN getFlags() const { return flags & PUBLIC_FLAGS; }
-    bool isAlias() const { return (flags & ALIAS) != 0; }
+    uintN getFlags() const  { return flags & PUBLIC_FLAGS; }
+    bool isAlias() const    { return (flags & ALIAS) != 0; }
     bool hasShortID() const { return (flags & HAS_SHORTID) != 0; }
-    bool isMethod() const { return (flags & METHOD) != 0; }
+    bool isMethod() const   { return (flags & METHOD) != 0; }
+
+    JSObject *methodObject() const { JS_ASSERT(isMethod()); return getterObj; }
+    jsval methodValue() const      { return OBJECT_TO_JSVAL(methodObject()); }
 
-    JSObject *methodObject() const {
-        JS_ASSERT(isMethod());
-        return js_CastAsObject(rawGetter);
-    }
-    jsval methodValue() const {
-        JS_ASSERT(isMethod());
-        return js_CastAsObjectJSVal(rawGetter);
+    JSPropertyOp getter() const    { return rawGetter; }
+    bool hasDefaultGetter() const  { return !rawGetter; }
+    JSPropertyOp getterOp() const  { JS_ASSERT(!hasGetterValue()); return rawGetter; }
+    JSObject *getterObject() const { JS_ASSERT(hasGetterValue()); return getterObj; }
+
+    // Per ES5, decode null getterObj as the undefined value, which encodes as null.
+    jsval getterValue() const {
+        JS_ASSERT(hasGetterValue());
+        return getterObj ? OBJECT_TO_JSVAL(getterObj) : JSVAL_VOID;
     }
 
-    JSPropertyOp getter() const { return rawGetter; }
-    bool hasDefaultGetter() const { return !rawGetter; }
-    JSPropertyOp getterOp() const {
-        JS_ASSERT(!hasGetterValue());
-        return rawGetter;
-    }
-    JSObject *getterObject() const {
-        JS_ASSERT(hasGetterValue());
-        return js_CastAsObject(rawGetter);
-    }
-    jsval getterValue() const {
-        JS_ASSERT(hasGetterValue());
-        return rawGetter ? js_CastAsObjectJSVal(rawGetter) : JSVAL_VOID;
-    }
+    JSPropertyOp setter() const    { return rawSetter; }
+    bool hasDefaultSetter() const  { return !rawSetter; }
+    JSPropertyOp setterOp() const  { JS_ASSERT(!hasSetterValue()); return rawSetter; }
+    JSObject *setterObject() const { JS_ASSERT(hasSetterValue()); return setterObj; }
 
-    JSPropertyOp setter() const { return rawSetter; }
-    bool hasDefaultSetter() const { return !rawSetter; }
-    JSPropertyOp setterOp() const {
-        JS_ASSERT(!hasSetterValue());
-        return rawSetter;
-    }
-    JSObject *setterObject() const {
-        JS_ASSERT(hasSetterValue() && rawSetter);
-        return js_CastAsObject(rawSetter);
-    }
+    // Per ES5, decode null setterObj as the undefined value, which encodes as null.
     jsval setterValue() const {
         JS_ASSERT(hasSetterValue());
-        return rawSetter ? js_CastAsObjectJSVal(rawSetter) : JSVAL_VOID;
+        return setterObj ? OBJECT_TO_JSVAL(setterObj) : JSVAL_VOID;
     }
 
     inline JSDHashNumber hash() const;
     inline bool matches(const JSScopeProperty *p) const;
     inline bool matchesParamsAfterId(JSPropertyOp agetter, JSPropertyOp asetter, uint32 aslot,
                                      uintN aattrs, uintN aflags, intN ashortid) const;
 
     bool get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp);
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -135,29 +135,29 @@ JSScope::methodReadBarrier(JSContext *cx
 
 inline bool
 JSScope::methodWriteBarrier(JSContext *cx, JSScopeProperty *sprop, jsval v)
 {
     if (flags & (BRANDED | METHOD_BARRIER)) {
         jsval prev = object->lockedGetSlot(sprop->slot);
 
         if (prev != v && VALUE_IS_FUNCTION(cx, prev))
-            return methodShapeChange(cx, sprop, v);
+            return methodShapeChange(cx, sprop);
     }
     return true;
 }
 
 inline bool
 JSScope::methodWriteBarrier(JSContext *cx, uint32 slot, jsval v)
 {
     if (flags & (BRANDED | METHOD_BARRIER)) {
         jsval prev = object->lockedGetSlot(slot);
 
         if (prev != v && VALUE_IS_FUNCTION(cx, prev))
-            return methodShapeChange(cx, slot, v);
+            return methodShapeChange(cx, slot);
     }
     return true;
 }
 
 inline void
 JSScope::trace(JSTracer *trc)
 {
     JSContext *cx = trc->context;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -1118,47 +1118,56 @@ struct ManualCmp {
         return true;
     }
 };
 
 }
 
 template <class InnerMatch>
 static jsint
-Duff(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
+UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen)
 {
     JS_ASSERT(patlen > 0 && textlen > 0);
     const jschar *textend = text + textlen - (patlen - 1);
     const jschar p0 = *pat;
     const jschar *const patNext = pat + 1;
     const typename InnerMatch::Extent extent = InnerMatch::computeExtent(pat, patlen);
     uint8 fixup;
 
     const jschar *t = text;
     switch ((textend - t) & 7) {
+      case 0: if (*t++ == p0) { fixup = 8; goto match; }
+      case 7: if (*t++ == p0) { fixup = 7; goto match; }
+      case 6: if (*t++ == p0) { fixup = 6; goto match; }
+      case 5: if (*t++ == p0) { fixup = 5; goto match; }
+      case 4: if (*t++ == p0) { fixup = 4; goto match; }
+      case 3: if (*t++ == p0) { fixup = 3; goto match; }
+      case 2: if (*t++ == p0) { fixup = 2; goto match; }
+      case 1: if (*t++ == p0) { fixup = 1; goto match; }
+    }
+    while (t != textend) {
+      if (t[0] == p0) { t += 1; fixup = 8; goto match; }
+      if (t[1] == p0) { t += 2; fixup = 7; goto match; }
+      if (t[2] == p0) { t += 3; fixup = 6; goto match; }
+      if (t[3] == p0) { t += 4; fixup = 5; goto match; }
+      if (t[4] == p0) { t += 5; fixup = 4; goto match; }
+      if (t[5] == p0) { t += 6; fixup = 3; goto match; }
+      if (t[6] == p0) { t += 7; fixup = 2; goto match; }
+      if (t[7] == p0) { t += 8; fixup = 1; goto match; }
+        t += 8;
+        continue;
         do {
-          case 0: if (*t++ == p0) { fixup = 8; goto match; }
-          case 7: if (*t++ == p0) { fixup = 7; goto match; }
-          case 6: if (*t++ == p0) { fixup = 6; goto match; }
-          case 5: if (*t++ == p0) { fixup = 5; goto match; }
-          case 4: if (*t++ == p0) { fixup = 4; goto match; }
-          case 3: if (*t++ == p0) { fixup = 3; goto match; }
-          case 2: if (*t++ == p0) { fixup = 2; goto match; }
-          case 1: if (*t++ == p0) { fixup = 1; goto match; }
-            continue;
-            do {
-                if (*t++ == p0) {
-                  match:
-                    if (!InnerMatch::match(patNext, t, extent))
-                        goto failed_match;
-                    return t - text - 1;
-                }
-              failed_match:;
-            } while (--fixup > 0);
-        } while(t != textend);
+            if (*t++ == p0) {
+              match:
+                if (!InnerMatch::match(patNext, t, extent))
+                    goto failed_match;
+                return t - text - 1;
+            }
+          failed_match:;
+        } while (--fixup > 0);
     }
     return -1;
 }
 
 static JS_ALWAYS_INLINE jsint
 StringMatch(const jschar *text, jsuint textlen,
             const jschar *pat, jsuint patlen)
 {
@@ -1204,20 +1213,20 @@ StringMatch(const jschar *text, jsuint t
     /*
      * For big patterns with large potential overlap we want the SIMD-optimized
      * speed of memcmp. For small patterns, a simple loop is faster.
      *
      * FIXME: Linux memcmp performance is sad and the manual loop is faster.
      */
     return
 #if !defined(__linux__)
-           patlen > 128 ? Duff<MemCmp>(text, textlen, pat, patlen)
+           patlen > 128 ? UnrolledMatch<MemCmp>(text, textlen, pat, patlen)
                         :
 #endif
-                          Duff<ManualCmp>(text, textlen, pat, patlen);
+                          UnrolledMatch<ManualCmp>(text, textlen, pat, patlen);
 }
 
 static JSBool
 str_indexOf(JSContext *cx, uintN argc, jsval *vp)
 {
 
     JSString *str;
     NORMALIZE_THIS(cx, vp, str);
@@ -1714,34 +1723,34 @@ InterpretDollar(JSContext *cx, jschar *d
         return NULL;
 
     /* Interpret all Perl match-induced dollar variables. */
     res = &cx->regExpStatics;
     dc = dp[1];
     if (JS7_ISDEC(dc)) {
         /* ECMA-262 Edition 3: 1-9 or 01-99 */
         num = JS7_UNDEC(dc);
-        if (num > res->parenCount)
+        if (num > res->parens.length())
             return NULL;
 
         cp = dp + 2;
         if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
             tmp = 10 * num + JS7_UNDEC(dc);
-            if (tmp <= res->parenCount) {
+            if (tmp <= res->parens.length()) {
                 cp++;
                 num = tmp;
             }
         }
         if (num == 0)
             return NULL;
 
         /* Adjust num from 1 $n-origin to 0 array-index-origin. */
         num--;
         *skip = cp - dp;
-        return REGEXP_PAREN_SUBSTRING(res, num);
+        return (num < res->parens.length()) ? &res->parens[num] : &js_EmptySubString;
     }
 
     *skip = 2;
     switch (dc) {
       case '$':
         rdata.dollarStr.chars = dp;
         rdata.dollarStr.length = 1;
         return &rdata.dollarStr;
@@ -1764,29 +1773,41 @@ PushRegExpSubstr(JSContext *cx, const JS
     size_t off = sub.chars - whole->chars();
     JSString *str = js_NewDependentString(cx, whole, off, sub.length);
     if (!str)
         return false;
     *sp++ = STRING_TO_JSVAL(str);
     return true;
 }
 
+class PreserveRegExpStatics {
+    JSContext *cx;
+    JSRegExpStatics save;
+
+  public:
+    PreserveRegExpStatics(JSContext *cx) : cx(cx), save(cx) {
+        save.copy(cx->regExpStatics);
+    }
+
+    ~PreserveRegExpStatics() {
+        cx->regExpStatics.copy(save);
+    }
+};
+
 static bool
 FindReplaceLength(JSContext *cx, ReplaceData &rdata, size_t *sizep)
 {
     JSString *repstr;
     size_t replen, skip;
     jschar *dp, *ep;
     JSSubString *sub;
     JSObject *lambda;
 
     lambda = rdata.lambda;
     if (lambda) {
-        uintN i, m, n;
-
         LeaveTrace(cx);
 
         /*
          * In the lambda case, not only do we find the replacement string's
          * length, we compute repstr and return it via rdata for use within
          * DoReplace.  The lambda is called with arguments ($&, $1, $2, ...,
          * index, input), i.e., all the properties of a regexp match array.
          * For $&, etc., we must create string jsvals from cx->regExpStatics.
@@ -1797,87 +1818,57 @@ FindReplaceLength(JSContext *cx, Replace
 
         if (!rdata.invokevp) {
             rdata.invokevp = js_AllocStack(cx, 2 + argc, &rdata.invokevpMark);
             if (!rdata.invokevp)
                 return false;
         }
         jsval* invokevp = rdata.invokevp;
 
-        MUST_FLOW_THROUGH("lambda_out");
-        bool ok = false;
-        bool freeMoreParens = false;
-
-        /*
-         * Save the regExpStatics from the current regexp, since they may be
-         * clobbered by a RegExp usage in the lambda function.  Note that all
-         * members of JSRegExpStatics are JSSubStrings, so not GC roots, save
-         * input, which is rooted otherwise via vp[1] in str_replace.
-         */
-        JSRegExpStatics save = cx->regExpStatics;
+        PreserveRegExpStatics save(cx);
 
         /* Push lambda and its 'this' parameter. */
         jsval *sp = invokevp;
         *sp++ = OBJECT_TO_JSVAL(lambda);
         *sp++ = OBJECT_TO_JSVAL(lambda->getParent());
 
         /* Push $&, $1, $2, ... */
         if (!PushRegExpSubstr(cx, cx->regExpStatics.lastMatch, sp))
-            goto lambda_out;
-
-        i = 0;
-        m = cx->regExpStatics.parenCount;
-        n = JS_MIN(m, 9);
-        for (uintN j = 0; i < n; i++, j++) {
-            if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[j], sp))
-                goto lambda_out;
+            return false;
+
+        uintN i = 0;
+        for (uintN n = cx->regExpStatics.parens.length(); i < n; i++) {
+            if (!PushRegExpSubstr(cx, cx->regExpStatics.parens[i], sp))
+                return false;
         }
-        for (uintN j = 0; i < m; i++, j++) {
-            if (!PushRegExpSubstr(cx, cx->regExpStatics.moreParens[j], sp))
-                goto lambda_out;
-        }
-
-        /*
-         * We need to clear moreParens in the top-of-stack cx->regExpStatics
-         * so it won't be possibly realloc'ed, leaving the bottom-of-stack
-         * moreParens pointing to freed memory.
-         */
-        cx->regExpStatics.moreParens = NULL;
-        freeMoreParens = true;
 
         /* Make sure to push undefined for any unmatched parens. */
         for (; i < p; i++)
             *sp++ = JSVAL_VOID;
 
         /* Push match index and input string. */
         *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
         *sp++ = STRING_TO_JSVAL(rdata.str);
 
         if (!js_Invoke(cx, argc, invokevp, 0))
-            goto lambda_out;
+            return false;
 
         /*
          * NB: we count on the newborn string root to hold any string
          * created by this js_ValueToString that would otherwise be GC-
          * able, until we use rdata.repstr in DoReplace.
          */
         repstr = js_ValueToString(cx, *invokevp);
         if (!repstr)
-            goto lambda_out;
+            return false;
 
         rdata.repstr = repstr;
         *sizep = repstr->length();
 
-        ok = true;
-
-      lambda_out:
-        if (freeMoreParens)
-            cx->free(cx->regExpStatics.moreParens);
-        cx->regExpStatics = save;
-        return ok;
+        return true;
     }
 
     repstr = rdata.repstr;
     replen = repstr->length();
     for (dp = rdata.dollar, ep = rdata.dollarEnd; dp;
          dp = js_strchr_limit(dp, '$', ep)) {
         sub = InterpretDollar(cx, dp, ep, rdata, &skip);
         if (sub) {
@@ -2207,20 +2198,21 @@ str_split(JSContext *cx, uintN argc, jsv
         len++;
 
         /*
          * Imitate perl's feature of including parenthesized substrings that
          * matched part of the delimiter in the new array, after the split
          * substring that was delimited.
          */
         if (re && sep->chars) {
-            for (uintN num = 0; num < cx->regExpStatics.parenCount; num++) {
+            JSRegExpStatics *res = &cx->regExpStatics;
+            for (uintN num = 0; num < res->parens.length(); num++) {
                 if (limited && len >= limit)
                     break;
-                JSSubString *parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
+                JSSubString *parsub = &res->parens[num];
                 sub = js_NewStringCopyN(cx, parsub->chars, parsub->length);
                 if (!sub || !splits.push(sub))
                     return false;
                 len++;
             }
             sep->chars = NULL;
         }
         i = j + sep->length;
--- a/js/src/jstask.cpp
+++ b/js/src/jstask.cpp
@@ -67,16 +67,20 @@ JSBackgroundThread::init()
     thread = PR_CreateThread(PR_USER_THREAD, start, this, PR_PRIORITY_LOW,
                              PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
     return !!thread;
 }
 
 void
 JSBackgroundThread::cancel()
 {
+    /* We allow to call the cancel method after failed init. */
+    if (!thread)
+        return;
+
     PR_Lock(lock);
     if (shutdown) {
         PR_Unlock(lock);
         return;
     }
     shutdown = true;
     PR_NotifyCondVar(wakeup);
     PR_Unlock(lock);
--- a/js/src/jstask.h
+++ b/js/src/jstask.h
@@ -38,16 +38,17 @@
 
 #ifndef jstask_h___
 #define jstask_h___
 
 class JSBackgroundTask {
     friend class JSBackgroundThread;
     JSBackgroundTask* next;
   public:
+    virtual ~JSBackgroundTask() {}
     virtual void run() = 0;
 };
 
 #ifdef JS_THREADSAFE
 
 #include "prthread.h"
 #include "prlock.h"
 #include "prcvar.h"
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -102,31 +102,31 @@ using namespace js;
 
 /* Implement embedder-specific nanojit members. */
 
 void*
 nanojit::Allocator::allocChunk(size_t nbytes)
 {
     VMAllocator *vma = (VMAllocator*)this;
     JS_ASSERT(!vma->outOfMemory());
-    void *p = calloc(1, nbytes);
+    void *p = js_calloc(nbytes);
     if (!p) {
         JS_ASSERT(nbytes < sizeof(vma->mReserve));
         vma->mOutOfMemory = true;
         p = (void*) &vma->mReserve[0];
     }
     vma->mSize += nbytes;
     return p;
 }
 
 void
 nanojit::Allocator::freeChunk(void *p) {
     VMAllocator *vma = (VMAllocator*)this;
     if (p != &vma->mReserve[0])
-        free(p);
+        js_free(p);
 }
 
 void
 nanojit::Allocator::postReset() {
     VMAllocator *vma = (VMAllocator*)this;
     vma->mOutOfMemory = false;
     vma->mSize = 0;
 }
@@ -901,22 +901,16 @@ void
 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4,
                       LIns *ins5, LIns *ins6)
 {
     LIns* insa[] = { ins1, ins2, ins3, ins4, ins5, ins6 };
     tprint(format, 6, insa);
 }
 #endif
 
-/*
- * The entire VM shares one oracle. Collisions and concurrent updates are
- * tolerated and worst case cause performance regressions.
- */
-static Oracle oracle;
-
 Tracker::Tracker()
 {
     pagelist = NULL;
 }
 
 Tracker::~Tracker()
 {
     clear();
@@ -946,30 +940,30 @@ Tracker::findTrackerPage(const void* v) 
     }
     return NULL;
 }
 
 struct Tracker::TrackerPage*
 Tracker::addTrackerPage(const void* v)
 {
     jsuword base = getTrackerPageBase(v);
-    struct TrackerPage* p = (struct TrackerPage*) calloc(1, sizeof(*p));
+    struct TrackerPage* p = (struct TrackerPage*) js_calloc(sizeof(*p));
     p->base = base;
     p->next = pagelist;
     pagelist = p;
     return p;
 }
 
 void
 Tracker::clear()
 {
     while (pagelist) {
         TrackerPage* p = pagelist;
         pagelist = pagelist->next;
-        free(p);
+        js_free(p);
     }
 }
 
 bool
 Tracker::has(const void *v) const
 {
     return get(v) != NULL;
 }
@@ -1212,54 +1206,48 @@ Oracle::isInstructionUndemotable(jsbytec
 void
 Oracle::clearDemotability()
 {
     _stackDontDemote.reset();
     _globalDontDemote.reset();
     _pcDontDemote.reset();
 }
 
-JS_REQUIRES_STACK static JS_INLINE void
-MarkSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot)
+JS_REQUIRES_STACK void
+TraceRecorder::markSlotUndemotable(LinkableFragment* f, unsigned slot)
 {
     if (slot < f->nStackTypes) {
-        oracle.markStackSlotUndemotable(cx, slot);
+        oracle->markStackSlotUndemotable(cx, slot);
         return;
     }
 
     uint16* gslots = f->globalSlots->data();
-    oracle.markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]);
-}
-
-JS_REQUIRES_STACK static JS_INLINE void
-MarkSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot, const void* pc)
+    oracle->markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]);
+}
+
+JS_REQUIRES_STACK void
+TraceRecorder::markSlotUndemotable(LinkableFragment* f, unsigned slot, const void* pc)
 {
     if (slot < f->nStackTypes) {
-        oracle.markStackSlotUndemotable(cx, slot, pc);
+        oracle->markStackSlotUndemotable(cx, slot, pc);
         return;
     }
 
     uint16* gslots = f->globalSlots->data();
-    oracle.markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]);
-}
-
-static JS_REQUIRES_STACK inline bool
-IsSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot, const void* ip)
+    oracle->markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]);
+}
+
+static JS_REQUIRES_STACK bool
+IsSlotUndemotable(Oracle* oracle, JSContext* cx, LinkableFragment* f, unsigned slot, const void* ip)
 {
     if (slot < f->nStackTypes)
-        return oracle.isStackSlotUndemotable(cx, slot, ip);
+        return oracle->isStackSlotUndemotable(cx, slot, ip);
 
     uint16* gslots = f->globalSlots->data();
-    return oracle.isGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]);
-}
-
-static JS_REQUIRES_STACK inline bool
-IsSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot)
-{
-    return IsSlotUndemotable(cx, f, slot, cx->fp->regs->pc);
+    return oracle->isGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]);
 }
 
 class FrameInfoCache
 {
     struct HashPolicy
     {
         typedef FrameInfo *Lookup;
         static HashNumber hash(const FrameInfo* fi) {
@@ -1962,31 +1950,31 @@ public:
         mTypeMap(typeMap),
         mPtr(typeMap)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
             TraceType type = getCoercedType(*vp);
             if (type == TT_INT32 &&
-                oracle.isGlobalSlotUndemotable(mCx, slot))
+                JS_TRACE_MONITOR(mCx).oracle->isGlobalSlotUndemotable(mCx, slot))
                 type = TT_DOUBLE;
             JS_ASSERT(type != TT_JSVAL);
             debug_only_printf(LC_TMTracer,
                               "capture type global%d: %d=%c\n",
                               n, type, typeChar[type]);
             *mPtr++ = type;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(jsval *vp, int count, JSStackFrame* fp) {
         for (int i = 0; i < count; ++i) {
             TraceType type = getCoercedType(vp[i]);
             if (type == TT_INT32 &&
-                oracle.isStackSlotUndemotable(mCx, length()))
+                JS_TRACE_MONITOR(mCx).oracle->isStackSlotUndemotable(mCx, length()))
                 type = TT_DOUBLE;
             JS_ASSERT(type != TT_JSVAL);
             debug_only_printf(LC_TMTracer,
                               "capture type %s%d: %d=%c\n",
                               stackSlotKind(), i, type, typeChar[type]);
             *mPtr++ = type;
         }
         return true;
@@ -2132,16 +2120,17 @@ InitConst(const T &t)
 
 JS_REQUIRES_STACK
 TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* fragment,
                              unsigned stackSlots, unsigned ngslots, TraceType* typeMap,
                              VMSideExit* innermost, jsbytecode* outer, uint32 outerArgc,
                              RecordReason recordReason)
   : cx(cx),
     traceMonitor(&JS_TRACE_MONITOR(cx)),
+    oracle(JS_TRACE_MONITOR(cx).oracle),   
     fragment(fragment),
     tree(fragment->root),
     recordReason(recordReason),
     globalObj(tree->globalObj),
     outer(outer),
     outerArgc(outerArgc),
     lexicalBlock(cx->fp->blockChain),
     anchor(anchor),
@@ -2679,16 +2668,17 @@ TraceMonitor::flush()
     )
 
     frameCache->reset();
     dataAlloc->reset();
     traceAlloc->reset();
     codeAlloc->reset();
     tempAlloc->reset();
     reTempAlloc->reset();
+    oracle->clear();
 
     Allocator& alloc = *dataAlloc;
 
     for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
         globalStates[i].globalShape = -1;
         globalStates[i].globalSlots = new (alloc) SlotList(&alloc);
     }
 
@@ -2751,17 +2741,16 @@ bool
 NativeToValue(JSContext* cx, jsval& v, TraceType type, double* slot)
 {
     JSBool ok;
     jsint i;
     jsdouble d;
     switch (type) {
       case TT_OBJECT:
         v = OBJECT_TO_JSVAL(*(JSObject**)slot);
-        JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */
         debug_only_printf(LC_TMTracer,
                           "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
                           JSVAL_IS_NULL(v)
                           ? "null"
                           : JSVAL_TO_OBJECT(v)->getClass()->name);
         break;
 
       case TT_INT32:
@@ -2784,17 +2773,16 @@ NativeToValue(JSContext* cx, jsval& v, T
         if (!ok) {
             js_ReportOutOfMemory(cx);
             return false;
         }
         return true;
 
       case TT_JSVAL:
         v = *(jsval*)slot;
-        JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */
         debug_only_printf(LC_TMTracer, "box<%p> ", (void*)v);
         break;
 
       case TT_STRING:
         v = STRING_TO_JSVAL(*(JSString**)slot);
         debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot));
         break;
 
@@ -3528,17 +3516,17 @@ TraceRecorder::importGlobalSlot(unsigned
     jsval* vp = &globalObj->getSlotRef(slot);
     JS_ASSERT(!known(vp));
 
     /* Add the slot to the list of interned global slots. */
     TraceType type;
     int index = tree->globalSlots->offsetOf(uint16(slot));
     if (index == -1) {
         type = getCoercedType(*vp);
-        if (type == TT_INT32 && oracle.isGlobalSlotUndemotable(cx, slot))
+        if (type == TT_INT32 && oracle->isGlobalSlotUndemotable(cx, slot))
             type = TT_DOUBLE;
         index = (int)tree->globalSlots->length();
         tree->globalSlots->add(uint16(slot));
         tree->typeMap.add(type);
         SpecializeTreesToMissingGlobals(cx, globalObj, tree);
         JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length());
     } else {
         type = importTypeMap[importStackSlots + index];
@@ -3759,17 +3747,17 @@ public:
         if (isPromote && *mTypeMap == TT_DOUBLE) {
             mLir->insStore(mRecorder.get(vp), mRecorder.eos_ins,
                             mRecorder.nativeGlobalOffset(vp), ACC_OTHER);
 
             /*
              * Aggressively undo speculation so the inner tree will compile
              * if this fails.
              */
-            oracle.markGlobalSlotUndemotable(mCx, slot);
+            mRecorder.oracle->markGlobalSlotUndemotable(mCx, slot);
         }
         JS_ASSERT(!(!isPromote && *mTypeMap == TT_INT32));
         ++mTypeMap;
     }
 };
 
 class AdjustCallerStackTypesVisitor : public SlotVisitorBase
 {
@@ -3803,17 +3791,17 @@ public:
             if (isPromote && *mTypeMap == TT_DOUBLE) {
                 mLir->insStore(mRecorder.get(vp), mLirbuf->sp,
                                 mRecorder.nativespOffset(vp), ACC_STACK);
 
                 /*
                  * Aggressively undo speculation so the inner tree will compile
                  * if this fails.
                  */
-                oracle.markStackSlotUndemotable(mCx, mSlotnum);
+                mRecorder.oracle->markStackSlotUndemotable(mCx, mSlotnum);
             }
             JS_ASSERT(!(!isPromote && *mTypeMap == TT_INT32));
             ++vp;
             ++mTypeMap;
             ++mSlotnum;
         }
         return true;
     }
@@ -4435,17 +4423,17 @@ class SlotMap : public SlotVisitorBase
         slots.add(SlotInfo(vp, t));
     }
 
     JS_REQUIRES_STACK void
     markUndemotes()
     {
         for (unsigned i = 0; i < length(); i++) {
             if (get(i).lastCheck == TypeCheck_Undemote)
-                MarkSlotUndemotable(mRecorder.cx, mRecorder.tree, i);
+                mRecorder.markSlotUndemotable(mRecorder.tree, i);
         }
     }
 
     JS_REQUIRES_STACK virtual void
     adjustTail(TypeConsensus consensus)
     {
     }
 
@@ -4760,40 +4748,40 @@ TypeMapLinkability(JSContext* cx, const 
 {
     const TypeMap& peerMap = peer->typeMap;
     unsigned minSlots = JS_MIN(typeMap.length(), peerMap.length());
     TypeConsensus consensus = TypeConsensus_Okay;
     for (unsigned i = 0; i < minSlots; i++) {
         if (typeMap[i] == peerMap[i])
             continue;
         if (typeMap[i] == TT_INT32 && peerMap[i] == TT_DOUBLE &&
-            IsSlotUndemotable(cx, peer, i, peer->ip)) {
+            IsSlotUndemotable(JS_TRACE_MONITOR(cx).oracle, cx, peer, i, peer->ip)) {
             consensus = TypeConsensus_Undemotes;
         } else {
             return TypeConsensus_Bad;
         }
     }
     return consensus;
 }
 
-static JS_REQUIRES_STACK unsigned
-FindUndemotesInTypemaps(JSContext* cx, const TypeMap& typeMap, LinkableFragment* f,
+JS_REQUIRES_STACK unsigned
+TraceRecorder::findUndemotesInTypemaps(const TypeMap& typeMap, LinkableFragment* f,
                         Queue<unsigned>& undemotes)
 {
     undemotes.setLength(0);
     unsigned minSlots = JS_MIN(typeMap.length(), f->typeMap.length());
     for (unsigned i = 0; i < minSlots; i++) {
         if (typeMap[i] == TT_INT32 && f->typeMap[i] == TT_DOUBLE) {
             undemotes.add(i);
         } else if (typeMap[i] != f->typeMap[i]) {
             return 0;
         }
     }
     for (unsigned i = 0; i < undemotes.length(); i++)
-        MarkSlotUndemotable(cx, f, undemotes[i]);
+        markSlotUndemotable(f, undemotes[i]);
     return undemotes.length();
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::joinEdgesToEntry(TreeFragment* peer_root)
 {
     if (fragment->root != fragment)
         return;
@@ -4832,17 +4820,17 @@ TraceRecorder::joinEdgesToEntry(TreeFrag
                 }
 
                 /* It's okay! Link together and remove the unstable exit. */
                 JS_ASSERT(tree == fragment);
                 JoinPeers(traceMonitor->assembler, uexit->exit, tree);
                 uexit = peer->removeUnstableExit(uexit->exit);
             } else {
                 /* Check for int32->double slots that suggest trashing. */
-                if (FindUndemotesInTypemaps(cx, typeMap, tree, undemotes)) {
+                if (findUndemotesInTypemaps(typeMap, tree, undemotes)) {
                     JS_ASSERT(peer == uexit->fragment->root);
                     if (fragment == peer)
                         trashSelf = true;
                     else
                         whichTreesToTrash.addUnique(uexit->fragment->root);
                     return;
                 }
                 uexit = uexit->next;
@@ -5231,55 +5219,66 @@ TraceRecorder::checkTraceEnd(jsbytecode 
             return ars;
         } else {
             return endLoop();
         }
     }
     return ARECORD_CONTINUE;
 }
 
-RecordingStatus
+AbortableRecordingStatus
 TraceRecorder::hasMethod(JSObject* obj, jsid id, bool& found)
 {
     found = false;
-    RecordingStatus status = RECORD_CONTINUE;
+    AbortableRecordingStatus status = ARECORD_CONTINUE;
     if (!obj)
         return status;
 
+    TraceMonitor &localtm = *traceMonitor;
+    JSContext *localcx = cx;
+
     JSObject* pobj;
     JSProperty* prop;
     int protoIndex = obj->lookupProperty(cx, id, &pobj, &prop);
     if (protoIndex < 0)
-        return RECORD_ERROR;
+        RETURN_ERROR_A("lookupProperty failed");
+
+    /* lookupProperty can reenter the interpreter and kill |this|. */
+    if (!localtm.recorder) {
+        if (prop)
+            pobj->dropProperty(localcx, prop);
+        return ARECORD_ABORTED;
+    }
+
     if (!prop)
         return status;
 
     if (!pobj->isNative()) {
         // We can't rely on __iterator__ being present on trace just because
         // it's there now, if found in a non-native object.
-        status = RECORD_STOP;
+        status = ARECORD_STOP;
     } else {
         JSScope* scope = pobj->scope();
         JSScopeProperty* sprop = (JSScopeProperty*) prop;
 
         if (sprop->hasDefaultGetterOrIsMethod() && SPROP_HAS_VALID_SLOT(sprop, scope)) {
             jsval v = pobj->lockedGetSlot(sprop->slot);
             if (VALUE_IS_FUNCTION(cx, v)) {
                 found = true;
                 if (!scope->generic() && !scope->branded() && !scope->brand(cx, sprop->slot, v))
-                    status = RECORD_STOP;
+                    status = ARECORD_STOP;
             }
         }
     }
 
     pobj->dropProperty(cx, prop);
     return status;
 }
 
-JS_REQUIRES_STACK RecordingStatus
+JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::hasIteratorMethod(JSObject* obj, bool& found)
 {
     JS_ASSERT(cx->fp->regs->sp + 2 <= cx->fp->slots + cx->fp->script->nslots);
 
     return hasMethod(obj, ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom), found);
 }
 
 /*
@@ -5679,35 +5678,36 @@ RecordTree(JSContext* cx, TreeFragment* 
 }
 
 static JS_REQUIRES_STACK TypeConsensus
 FindLoopEdgeTarget(JSContext* cx, VMSideExit* exit, TreeFragment** peerp)
 {
     TreeFragment* from = exit->root();
 
     JS_ASSERT(from->code());
+    Oracle* oracle = JS_TRACE_MONITOR(cx).oracle;
 
     TypeMap typeMap(NULL);
     FullMapFromExit(typeMap, exit);
     JS_ASSERT(typeMap.length() - exit->numStackSlots == from->nGlobalTypes());
 
     /* Mark all double slots as undemotable */
     uint16* gslots = from->globalSlots->data();
     for (unsigned i = 0; i < typeMap.length(); i++) {
         if (typeMap[i] == TT_DOUBLE) {
             if (exit->exitType == RECURSIVE_UNLINKED_EXIT) {
                 if (i < exit->numStackSlots)
-                    oracle.markStackSlotUndemotable(cx, i, exit->recursive_pc);
+                    oracle->markStackSlotUndemotable(cx, i, exit->recursive_pc);
                 else
-                    oracle.markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]);
+                    oracle->markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]);
             }
             if (i < from->nStackTypes)
-                oracle.markStackSlotUndemotable(cx, i, from->ip);
+                oracle->markStackSlotUndemotable(cx, i, from->ip);
             else if (i >= exit->numStackSlots)
-                oracle.markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]);
+                oracle->markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]);
         }
     }
 
     JS_ASSERT(exit->exitType == UNSTABLE_LOOP_EXIT ||
               (exit->exitType == RECURSIVE_UNLINKED_EXIT && exit->recursive_pc));
 
     TreeFragment* firstPeer = NULL;
     if (exit->exitType == UNSTABLE_LOOP_EXIT || exit->recursive_pc == from->ip) {
@@ -5918,53 +5918,63 @@ AttemptToExtendTree(JSContext* cx, VMSid
         return rv;
     }
 #ifdef MOZ_TRACEVIS
     if (tvso) tvso->r = R_FAIL_EXTEND_COLD;
 #endif
     return false;
 }
 
-static JS_REQUIRES_STACK VMSideExit*
+static JS_REQUIRES_STACK bool
 ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount,
-            VMSideExit** innermostNestedGuardp);
-
-JS_REQUIRES_STACK bool
+            VMSideExit** innermostNestedGuardp, VMSideExit** lrp);
+
+static inline MonitorResult
+RecordingIfTrue(bool b)
+{
+    return b ? MONITOR_RECORDING : MONITOR_NOT_RECORDING;
+}
+
+/*
+ * A postcondition of recordLoopEdge is that if recordLoopEdge does not return
+ * MONITOR_RECORDING, the recording has been aborted.
+ */
+JS_REQUIRES_STACK MonitorResult
 TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
 {
 #ifdef JS_THREADSAFE
     if (cx->fp->scopeChain->getGlobal()->scope()->title.ownercx != cx) {
         AbortRecording(cx, "Global object not owned by this context");
-        return false; /* we stay away from shared global objects */
+        return MONITOR_NOT_RECORDING; /* we stay away from shared global objects */
     }
 #endif
 
     TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
     /* Process needFlush and deep abort requests. */
     if (tm->needFlush) {
         ResetJIT(cx, FR_DEEP_BAIL);