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
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);
-        return false;
+        return MONITOR_NOT_RECORDING;
     }
 
     JS_ASSERT(r->fragment && !r->fragment->lastIns);
     TreeFragment* root = r->fragment->root;
     TreeFragment* first = LookupOrAddLoop(tm, cx->fp->regs->pc, root->globalObj,
                                         root->globalShape, cx->fp->argc);
 
     /*
      * Make sure the shape of the global object still matches (this might flush
      * the JIT cache).
      */
     JSObject* globalObj = cx->fp->scopeChain->getGlobal();
     uint32 globalShape = -1;
     SlotList* globalSlots = NULL;
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
         JS_ASSERT(!tm->recorder);
-        return false;
+        return MONITOR_NOT_RECORDING;
     }
 
     debug_only_printf(LC_TMTracer,
                       "Looking for type-compatible peer (%s:%d@%d)\n",
                       cx->fp->script->filename,
                       js_FramePCToLineNumber(cx, cx->fp),
                       FramePCOffset(cx->fp));
 
@@ -5974,20 +5984,29 @@ TraceRecorder::recordLoopEdge(JSContext*
         AUDIT(noCompatInnerTrees);
 
         TreeFragment* outerFragment = root;
         jsbytecode* outer = (jsbytecode*) outerFragment->ip;
         uint32 outerArgc = outerFragment->argc;
         JS_ASSERT(cx->fp->argc == first->argc);
         AbortRecording(cx, "No compatible inner tree");
 
-        return RecordTree(cx, first, outer, outerArgc, globalSlots, Record_Branch);
-    }
-
-    return r->attemptTreeCall(f, inlineCallC