Merge tracemonkey to mozilla-central. a=blockers
authorRobert Sayre <sayrer@gmail.com>
Mon, 06 Dec 2010 22:46:53 -0500
changeset 58752 37b29506a7d4c9915a39e881812b011159f96e30
parent 58744 73ba6e1c2b05786f249c943131861b2594b39f98 (current diff)
parent 58751 8921e3faccd2558eab22efb3212aadda978edb99 (diff)
child 58754 885c41905de1115800518fd5fae1bad374fd8aeb
push id17414
push userrsayre@mozilla.com
push dateTue, 07 Dec 2010 03:47:09 +0000
treeherdermozilla-central@37b29506a7d4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblockers
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge tracemonkey to mozilla-central. a=blockers
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4696,24 +4696,33 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
   // document.body.foopy() needs to ensure that looking up foopy on
   // document.body's prototype will find the right function.
   JSObject *global = ::JS_GetGlobalForObject(cx, proto);
 
   // Only do this if the global object is a window.
   // XXX Is there a better way to check this?
   nsISupports *globalNative = XPConnect()->GetNativeOfWrapper(cx, global);
   nsCOMPtr<nsPIDOMWindow> piwin = do_QueryInterface(globalNative);
-  if(!piwin) {
+  if (!piwin) {
     return NS_OK;
   }
 
   nsGlobalWindow *win = nsGlobalWindow::FromSupports(globalNative);
   if (win->IsClosedOrClosing()) {
     return NS_OK;
   }
+
+  // If the window is in a different compartment than the global object, then
+  // it's likely that global is a sandbox object whose prototype is a window.
+  // Don't do anything in this case.
+  if (win->FastGetGlobalJSObject() &&
+      global->compartment() != win->FastGetGlobalJSObject()->compartment()) {
+    return NS_OK;
+  }
+
   if (win->IsOuterWindow()) {
     // XXXjst: Do security checks here when we remove the security
     // checks on the inner window.
 
     win = win->GetCurrentInnerWindowInternal();
 
     if (!win || !(global = win->GetGlobalJSObject()) ||
         win->IsClosedOrClosing()) {
@@ -4941,17 +4950,17 @@ nsWindowSH::PreCreate(nsISupports *nativ
 
   nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj);
   if (win->IsOuterWindow()) {
     if (!win->EnsureInnerWindow()) {
       return NS_ERROR_FAILURE;
     }
 
     *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject();
-    return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW;
+    return NS_OK;
   }
 
   JSObject *winObj = win->FastGetGlobalJSObject();
   if (!winObj) {
     NS_ASSERTION(win->GetOuterWindowInternal()->IsCreatingInnerWindow(),
                  "should have a JS object by this point");
     return NS_OK;
   }
--- a/js/src/assembler/jit/ExecutableAllocator.h
+++ b/js/src/assembler/jit/ExecutableAllocator.h
@@ -50,26 +50,24 @@
 
 #if WTF_PLATFORM_WINCE
 // From pkfuncs.h (private header file from the Platform Builder)
 #define CACHE_SYNC_ALL 0x07F
 extern "C" __declspec(dllimport) void CacheRangeFlush(LPVOID pAddr, DWORD dwLength, DWORD dwFlags);
 #endif
 
 #define JIT_ALLOCATOR_PAGE_SIZE (ExecutableAllocator::pageSize)
-#if WTF_PLATFORM_WIN_OS || WTF_PLATFORM_WINCE
 /*
- * In practice, VirtualAlloc allocates in 64K chunks. (Technically, it
- * allocates in page chunks, but the starting address is always a multiple
- * of 64K, so each allocation uses up 64K of address space.
+ * On Windows, VirtualAlloc effectively allocates in 64K chunks. (Technically,
+ * it allocates in page chunks, but the starting address is always a multiple
+ * of 64K, so each allocation uses up 64K of address space.)  So a size less
+ * than that would be pointless.  But it turns out that 64KB is a reasonable
+ * size for all platforms.
  */
-# define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 16)
-#else
-# define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 4)
-#endif
+#define JIT_ALLOCATOR_LARGE_ALLOC_SIZE (ExecutableAllocator::pageSize * 16)
 
 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
 #define PROTECTION_FLAGS_RW (PROT_READ | PROT_WRITE)
 #define PROTECTION_FLAGS_RX (PROT_READ | PROT_EXEC)
 #define INITIAL_PROTECTION_FLAGS PROTECTION_FLAGS_RX
 #else
 #define INITIAL_PROTECTION_FLAGS (PROT_READ | PROT_WRITE | PROT_EXEC)
 #endif
@@ -198,57 +196,89 @@ public:
     static ExecutableAllocator *create()
     {
         ExecutableAllocator *allocator = new ExecutableAllocator();
         if (!allocator)
             return allocator;
 
         if (!pageSize)
             intializePageSize();
-        allocator->m_smallAllocationPool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
-        if (!allocator->m_smallAllocationPool) {
+        ExecutablePool *pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
+        if (!pool) {
             delete allocator;
             return NULL;
         }
+        JS_ASSERT(allocator->m_smallAllocationPools.empty());
+        allocator->m_smallAllocationPools.append(pool);
         return allocator;
     }
 
-    ~ExecutableAllocator() { delete m_smallAllocationPool; }
+    ~ExecutableAllocator()
+    {
+        for (size_t i = 0; i < m_smallAllocationPools.length(); i++)
+            delete m_smallAllocationPools[i];
+    }
 
     // poolForSize returns reference-counted objects. The caller owns a reference
     // to the object; i.e., poolForSize increments the count before returning the
     // object.
 
     ExecutablePool* poolForSize(size_t n)
     {
 #ifndef DEBUG_STRESS_JSC_ALLOCATOR
-        // Try to fit in the existing small allocator
-        if (n < m_smallAllocationPool->available()) {
-	    m_smallAllocationPool->addRef();
-            return m_smallAllocationPool;
-	}
+        // Try to fit in an existing small allocator.  Use the pool with the
+        // least available space that is big enough (best-fit).  This is the
+        // best strategy because (a) it maximizes the chance of the next
+        // allocation fitting in a small pool, and (b) it minimizes the
+        // potential waste when a small pool is next abandoned.
+        ExecutablePool *minPool = NULL;
+        for (size_t i = 0; i < m_smallAllocationPools.length(); i++) {
+            ExecutablePool *pool = m_smallAllocationPools[i];
+            if (n <= pool->available() && (!minPool || pool->available() < minPool->available()))
+                minPool = pool;
+        }
+        if (minPool) {
+            minPool->addRef();
+            return minPool;
+        }
 #endif
 
         // If the request is large, we just provide a unshared allocator
         if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
             return ExecutablePool::create(n);
 
         // Create a new allocator
         ExecutablePool* pool = ExecutablePool::create(JIT_ALLOCATOR_LARGE_ALLOC_SIZE);
         if (!pool)
             return NULL;
   	    // At this point, local |pool| is the owner.
 
-        // If the new allocator will result in more free space than in
-        // the current small allocator, then we will use it instead
-        if ((pool->available() - n) > m_smallAllocationPool->available()) {
-	        m_smallAllocationPool->release();
-            m_smallAllocationPool = pool;
-	        pool->addRef();
-	    }
+        if (m_smallAllocationPools.length() < maxSmallPools) {
+            // We haven't hit the maximum number of live pools;  add the new pool.
+            m_smallAllocationPools.append(pool);
+            pool->addRef();
+        } else {
+            // Find the pool with the least space.
+            int iMin = 0;
+            for (size_t i = 1; i < m_smallAllocationPools.length(); i++)
+                if (m_smallAllocationPools[i]->available() <
+                    m_smallAllocationPools[iMin]->available())
+                {
+                    iMin = i;
+                }
+
+            // If the new allocator will result in more free space than the small
+            // pool with the least space, then we will use it instead
+            ExecutablePool *minPool = m_smallAllocationPools[iMin];
+            if ((pool->available() - n) > minPool->available()) {
+                minPool->release();
+                m_smallAllocationPools[iMin] = pool;
+                pool->addRef();
+            }
+        }
 
    	    // Pass ownership to the caller.
         return pool;
     }
 
 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
     static void makeWritable(void* start, size_t size)
     {
@@ -350,17 +380,19 @@ public:
 #endif
 
 private:
 
 #if ENABLE_ASSEMBLER_WX_EXCLUSIVE
     static void reprotectRegion(void*, size_t, ProtectionSeting);
 #endif
 
-    ExecutablePool* m_smallAllocationPool;
+    static const size_t maxSmallPools = 4;
+    typedef js::Vector<ExecutablePool *, maxSmallPools, js::SystemAllocPolicy > SmallExecPoolVector;
+    SmallExecPoolVector m_smallAllocationPools;
     static void intializePageSize();
 };
 
 // This constructor can fail due to OOM. If it does, m_freePtr will be
 // set to NULL. 
 inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1)
 {
     size_t allocSize = roundUpAllocationSize(n, JIT_ALLOCATOR_PAGE_SIZE);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug601046.js
@@ -0,0 +1,8 @@
+// don't assert
+var f = function(){};
+for (var p in f);
+Object.defineProperty(f, "j", ({configurable: true, value: "a"}));
+Object.defineProperty(f, "k", ({configurable: true, value: "b"}));
+Object.defineProperty(f, "j", ({configurable: true, get: function() {}}));
+delete f.k;
+Object.defineProperty(f, "j", ({configurable: false}));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testIncDecReadOnly.js
@@ -0,0 +1,22 @@
+var r;
+Object.defineProperty(this, "x", {value: 0, writable: false});
+
+for (var a = 0; a < 10; ++a)
+    r = ++x;
+assertEq(x, 0);
+assertEq(r, 1);
+
+for (var a = 0; a < 10; ++a)
+    r = --x;
+assertEq(x, 0);
+assertEq(r, -1);
+
+for (var a = 0; a < 10; ++a)
+    r = x++;
+assertEq(x, 0);
+assertEq(r, 0);
+
+for (var a = 0; a < 10; ++a)
+    r = x--;
+assertEq(x, 0);
+assertEq(r, 0);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4279,17 +4279,17 @@ JSObject::allocSlot(JSContext *cx, uint3
         return false;
 
     /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
     JS_ASSERT(getSlot(slot).isUndefined());
     *slotp = slot;
     return true;
 }
 
-void
+bool
 JSObject::freeSlot(JSContext *cx, uint32 slot)
 {
     uint32 limit = slotSpan();
     JS_ASSERT(slot < limit);
 
     Value &vref = getSlotRef(slot);
     if (inDictionaryMode() && lastProp->table) {
         uint32 &last = lastProp->table->freelist;
@@ -4302,20 +4302,21 @@ JSObject::freeSlot(JSContext *cx, uint32
          * shape (and not a reserved slot; see bug 595230): push the slot onto
          * the dictionary property table's freelist. We want to let the last
          * slot be freed by shrinking the dslots vector; see js_TraceObject.
          */
         if (JSSLOT_FREE(clasp) <= slot && slot + 1 < limit) {
             JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
             vref.setPrivateUint32(last);
             last = slot;
-            return;
+            return true;
         }
     }
     vref.setUndefined();
+    return false;
 }
 
 /* JSBOXEDWORD_INT_MAX as a string */
 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
 
 /*
  * Convert string indexes that convert to int jsvals as ints to save memory.
  * Care must be taken to use this macro every time a property name is used, or
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1026,17 +1026,21 @@ struct JSObject : js::gc::Cell {
     inline void freeSlotsArray(JSContext *cx);
 
     /* Free the slots array and copy slots that fit into the fixed array. */
     inline void revertToFixedSlots(JSContext *cx);
 
     inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags = 0);
 
     bool allocSlot(JSContext *cx, uint32 *slotp);
-    void freeSlot(JSContext *cx, uint32 slot);
+
+    /*
+     * Return true iff this is a dictionary-mode object and the freed slot was
+     * added to the freelist. */
+    bool freeSlot(JSContext *cx, uint32 slot);
 
     bool reportReadOnly(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR);
     bool reportNotConfigurable(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR);
     bool reportNotExtensible(JSContext *cx, uintN report = JSREPORT_ERROR);
 
   private:
     js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::Shape &child);
 
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -197,17 +197,17 @@ PropertyCache::fill(JSContext *cx, JSObj
             }
         }
 
         /*
          * If getting a value via a stub getter, or doing an INCDEC op
          * with stub getters and setters, we can cache the slot.
          */
         if (!(cs->format & (JOF_SET | JOF_FOR)) &&
-            (!(cs->format & JOF_INCDEC) || shape->hasDefaultSetter()) &&
+            (!(cs->format & JOF_INCDEC) || (shape->hasDefaultSetter() && shape->writable())) &&
             shape->hasDefaultGetter() &&
             pobj->containsSlot(shape->slot)) {
             /* Great, let's cache shape's slot and use it on cache hit. */
             vword.setSlot(shape->slot);
         } else {
             /* Best we can do is to cache shape (still a nice speedup). */
             vword.setShape(shape);
             if (adding &&
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1072,51 +1072,60 @@ JSObject::changeProperty(JSContext *cx, 
 
     if (getter == PropertyStub)
         getter = NULL;
     if (setter == PropertyStub)
         setter = NULL;
     if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
         return shape;
 
-    Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);
-
     const Shape *newShape;
 
+    /*
+     * Dictionary-mode objects exclusively own their mutable shape structs, so
+     * we simply modify in place.
+     */
     if (inDictionaryMode()) {
-        shape->removeFromDictionary(this);
-        newShape = Shape::newDictionaryShape(cx, child, &lastProp);
-        if (newShape) {
-            JS_ASSERT(newShape == lastProp);
-
-            /*
-             * Let tableShape be the shape with non-null table, either the one
-             * we removed or the parent of lastProp.
-             */
-            const Shape *tableShape = shape->table ? shape : lastProp->parent;
+        /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
+        uint32 slot = shape->slot;
+        if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) {
+            if (!allocSlot(cx, &slot))
+                return NULL;
+        }
 
-            if (PropertyTable *table = tableShape->table) {
-                /* Overwrite shape with newShape in the property table. */
-                Shape **spp = table->search(shape->id, true);
-                SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
+        Shape *mutableShape = const_cast<Shape *>(shape);
+        mutableShape->slot = slot;
+        if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
+            mutableShape->slotSpan = slot + 1;
 
-                /* Hand the table off from tableShape to newShape. */
-                tableShape->setTable(NULL);
-                newShape->setTable(table);
-            }
-
-            updateFlags(newShape);
-            updateShape(cx);
-
-            if (!js_UpdateWatchpointsForShape(cx, this, newShape)) {
-                METER(wrapWatchFails);
-                return NULL;
+            for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
+                if (temp->slotSpan <= slot)
+                    temp->slotSpan = slot + 1;
             }
         }
+
+        mutableShape->rawGetter = getter;
+        mutableShape->rawSetter = setter;
+        mutableShape->attrs = uint8(attrs);
+
+        updateFlags(shape);
+
+        /* See the corresponding code in putProperty. */
+        lastProp->shape = js_GenerateShape(cx, false);
+        clearOwnShape();
+
+        if (!js_UpdateWatchpointsForShape(cx, this, shape)) {
+            METER(wrapWatchFails);
+            return NULL;
+        }
+
+        newShape = mutableShape;
     } else if (shape == lastProp) {
+        Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);
+
         newShape = getChildProperty(cx, shape->parent, child);
 #ifdef DEBUG
         if (newShape) {
             JS_ASSERT(newShape == lastProp);
             if (newShape->table) {
                 Shape **spp = nativeSearch(shape->id);
                 JS_ASSERT(SHAPE_FETCH(spp) == newShape);
             }
@@ -1124,16 +1133,17 @@ JSObject::changeProperty(JSContext *cx, 
 #endif
     } else {
         /*
          * Let JSObject::putProperty handle this |overwriting| case, including
          * the conservation of shape->slot (if it's valid). We must not call
          * removeProperty because it will free an allocated shape->slot, and
          * putProperty won't re-allocate it.
          */
+        Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);
         newShape = putProperty(cx, child.id, child.rawGetter, child.rawSetter, child.slot,
                                child.attrs, child.flags, child.shortid);
 #ifdef DEBUG
         if (newShape)
             METER(changePuts);
 #endif
     }
 
@@ -1153,19 +1163,20 @@ JSObject::removeProperty(JSContext *cx, 
     Shape **spp = nativeSearch(id);
     Shape *shape = SHAPE_FETCH(spp);
     if (!shape) {
         METER(uselessRemoves);
         return true;
     }
 
     /* First, if shape is unshared and not has a slot, free its slot number. */
+    bool addedToFreelist = false;
     bool hadSlot = !shape->isAlias() && shape->hasSlot();
     if (hadSlot) {
-        freeSlot(cx, shape->slot);
+        addedToFreelist = freeSlot(cx, shape->slot);
         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     }
 
 
     /* If shape is not the last property added, switch to dictionary mode. */
     if (shape != lastProp && !inDictionaryMode()) {
         if (!toDictionaryMode(cx))
             return false;
@@ -1220,22 +1231,30 @@ JSObject::removeProperty(JSContext *cx, 
         if (table) {
             if (shape == oldLastProp) {
                 JS_ASSERT(shape->table == table);
                 JS_ASSERT(shape->parent == lastProp);
                 JS_ASSERT(shape->slotSpan >= lastProp->slotSpan);
                 JS_ASSERT_IF(hadSlot, shape->slot + 1 <= shape->slotSpan);
 
                 /*
-                 * If the dictionary table's freelist is non-empty, we must
-                 * preserve lastProp->slotSpan. We can't reduce slotSpan even
-                 * by one or we might lose non-decreasing slotSpan order.
-                 */
-                if (table->freelist != SHAPE_INVALID_SLOT)
+                 * Maintain slot freelist consistency. The only constraint we
+                 * have is that slot numbers on the freelist are less than 
+                 * lastProp->slotSpan. Thus, if the freelist is non-empty,
+                 * then lastProp->slotSpan may not decrease.
+                 */ 
+                if (table->freelist != SHAPE_INVALID_SLOT) {
                     lastProp->slotSpan = shape->slotSpan;
+                    
+                    /* Add the slot to the freelist if it wasn't added in freeSlot. */
+                    if (hadSlot && !addedToFreelist) {
+                        getSlotRef(shape->slot).setPrivateUint32(table->freelist);
+                        table->freelist = shape->slot;
+                    }
+                }
             }
 
             /* Hand off table from old to new lastProp. */
             oldLastProp->setTable(NULL);
             lastProp->setTable(table);
         }
     } else {
         /*
--- a/js/src/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/src/xpconnect/idl/nsIXPCScriptable.idl
@@ -59,23 +59,16 @@
  * Classes that want slim wrappers should return NS_SUCCESS_ALLOW_SLIM_WRAPPERS
  * from their scriptable helper's PreCreate hook. They must also force a parent
  * for their wrapper (from the PreCreate hook), they must implement
  * nsWrapperCache and their scriptable helper must implement nsXPCClassInfo and
  * must return DONT_ASK_INSTANCE_FOR_SCRIPTABLE in the flags.
  */
 #define NS_SUCCESS_ALLOW_SLIM_WRAPPERS \
    (NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_XPCONNECT,3))
-
-/**
- * Classes that can change principals and require same-origin XOWs must return
- * this from their scriptable helper's PreCreate hook.
- */
-#define NS_SUCCESS_NEEDS_XOW \
-   (NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_XPCONNECT,4))
 %}
 
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
  * boolean to PR_TRUE before making the call.  Implementations may skip writing
  * to *_retval unless they want to return PR_FALSE.
  */
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -2693,31 +2693,28 @@ public:
                         XPCNativeScriptableCreateInfo& sciProto);
 
     JSBool HasExternalReference() const {return mRefCnt > 1;}
 
     JSBool NeedsSOW() { return !!(mWrapperWord & NEEDS_SOW); }
     void SetNeedsSOW() { mWrapperWord |= NEEDS_SOW; }
     JSBool NeedsCOW() { return !!(mWrapperWord & NEEDS_COW); }
     void SetNeedsCOW() { mWrapperWord |= NEEDS_COW; }
-    JSBool NeedsXOW() { return !!(mWrapperWord & NEEDS_XOW); }
 
     JSObject* GetWrapper()
     {
         return (JSObject *) (mWrapperWord & (size_t)~(size_t)FLAG_MASK);
     }
     void SetWrapper(JSObject *obj)
     {
         PRWord needsSOW = NeedsSOW() ? NEEDS_SOW : 0;
         PRWord needsCOW = NeedsCOW() ? NEEDS_COW : 0;
-        PRWord needsXOW = NeedsXOW() ? NEEDS_XOW : 0;
         mWrapperWord = PRWord(obj) |
                          needsSOW |
-                         needsCOW |
-                         needsXOW;
+                         needsCOW;
     }
 
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
 
     QITableEntry* GetOffsets()
     {
         if(!HasProto() || !GetProto()->ClassIsDOMObject())
             return nsnull;
@@ -2746,29 +2743,22 @@ protected:
                      XPCNativeSet* aSet);
 
     virtual ~XPCWrappedNative();
 
 private:
     enum {
         NEEDS_SOW = JS_BIT(0),
         NEEDS_COW = JS_BIT(1),
-        NEEDS_XOW = JS_BIT(2),
-
-        LAST_FLAG = NEEDS_XOW,
+
+        LAST_FLAG = NEEDS_COW,
 
         FLAG_MASK = 0x7
     };
 
-protected:
-    void SetNeedsXOW() {
-        NS_ASSERTION(mWrapperWord == 0, "It's too late to call this");
-        mWrapperWord = NEEDS_XOW;
-    }
-
 private:
 
     JSBool Init(XPCCallContext& ccx, JSObject* parent, JSBool isGlobal,
                 const XPCNativeScriptableCreateInfo* sci);
     JSBool Init(XPCCallContext &ccx, JSObject *existingJSObject);
     JSBool FinishInit(XPCCallContext &ccx);
 
     JSBool ExtendSet(XPCCallContext& ccx, XPCNativeInterface* aInterface);
@@ -2801,49 +2791,16 @@ private:
     PRWord                       mWrapperWord;
 
 #ifdef XPC_CHECK_WRAPPER_THREADSAFETY
 public:
     nsCOMPtr<nsIThread>          mThread; // Don't want to overload _mOwningThread
 #endif
 };
 
-class XPCWrappedNativeWithXOW : public XPCWrappedNative
-{
-public:
-    XPCWrappedNativeWithXOW(already_AddRefed<nsISupports> aIdentity,
-                            XPCWrappedNativeProto* aProto)
-        : XPCWrappedNative(aIdentity, aProto),
-          mXOW(nsnull)
-    {
-        SetNeedsXOW();
-    }
-    XPCWrappedNativeWithXOW(already_AddRefed<nsISupports> aIdentity,
-                            XPCWrappedNativeScope* aScope,
-                            XPCNativeSet* aSet)
-        : XPCWrappedNative(aIdentity, aScope, aSet),
-          mXOW(nsnull)
-    {
-        SetNeedsXOW();
-    }
-
-    JSObject *GetXOW()
-    {
-        return mXOW;
-    }
-
-    void SetXOW(JSObject *xow)
-    {
-        mXOW = xow;
-    }
-
-private:
-    JSObject *mXOW;
-};
-
 /***************************************************************************
 ****************************************************************************
 *
 * Core classes for wrapped JSObject for use from native code...
 *
 ****************************************************************************
 ***************************************************************************/
 
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -422,32 +422,29 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
 
     JSObject* parent = Scope->GetGlobalJSObject();
 
     jsval newParentVal = JSVAL_NULL;
     XPCMarkableJSVal newParentVal_markable(&newParentVal);
     AutoMarkingJSVal newParentVal_automarker(ccx, &newParentVal_markable);
     JSBool needsSOW = JS_FALSE;
     JSBool needsCOW = JS_FALSE;
-    JSBool needsXOW = JS_FALSE;
 
     JSAutoEnterCompartment ac;
 
     if(sciWrapper.GetFlags().WantPreCreate())
     {
         JSObject* plannedParent = parent;
         nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, ccx,
                                                           parent, &parent);
         if(NS_FAILED(rv))
             return rv;
 
         if(rv == NS_SUCCESS_CHROME_ACCESS_ONLY)
             needsSOW = JS_TRUE;
-        else if(rv == NS_SUCCESS_NEEDS_XOW)
-            needsXOW = JS_TRUE;
         rv = NS_OK;
 
         NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(parent),
                      "Xray wrapper being used to parent XPCWrappedNative?");
 
         if(!ac.enter(ccx, parent))
             return NS_ERROR_FAILURE;
 
@@ -539,37 +536,33 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
     {
         proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, Scope, info, &sciProto,
                                                     JS_FALSE, isGlobal);
         if(!proto)
             return NS_ERROR_FAILURE;
 
         proto->CacheOffsets(identity);
 
-        wrapper = needsXOW
-                  ? new XPCWrappedNativeWithXOW(identity, proto)
-                  : new XPCWrappedNative(identity, proto);
+        wrapper = new XPCWrappedNative(identity, proto);
         if(!wrapper)
             return NS_ERROR_FAILURE;
     }
     else
     {
         AutoMarkingNativeInterfacePtr iface(ccx, Interface);
         if(!iface)
             iface = XPCNativeInterface::GetISupports(ccx);
 
         AutoMarkingNativeSetPtr set(ccx);
         set = XPCNativeSet::GetNewOrUsed(ccx, nsnull, iface, 0);
 
         if(!set)
             return NS_ERROR_FAILURE;
 
-        wrapper = needsXOW
-                  ? new XPCWrappedNativeWithXOW(identity, Scope, set)
-                  : new XPCWrappedNative(identity, Scope, set);
+        wrapper = new XPCWrappedNative(identity, Scope, set);
         if(!wrapper)
             return NS_ERROR_FAILURE;
 
         DEBUG_ReportShadowedMembers(set, wrapper, nsnull);
     }
 
     // The strong reference was taken over by the wrapper, so make the nsCOMPtr
     // forget about it.
@@ -1352,31 +1345,16 @@ XPCWrappedNative::FlatJSObjectFinalized(
     nsWrapperCache *cache = nsnull;
     CallQueryInterface(mIdentity, &cache);
     if(cache)
         cache->ClearWrapper();
 
     // This makes IsValid return false from now on...
     mFlatJSObject = nsnull;
 
-    // Because order of finalization is random, we need to be careful here: if
-    // we're getting finalized, then it means that any XOWs in our cache are
-    // also getting finalized (or else we would be marked). But it's possible
-    // for us to outlive our cached XOW. So, in order to make it safe for the
-    // cached XOW to clear the cache, we need to finalize it first.
-    if(NeedsXOW())
-    {
-        XPCWrappedNativeWithXOW* wnxow =
-            static_cast<XPCWrappedNativeWithXOW *>(this);
-        if(JSObject* wrapper = wnxow->GetXOW())
-        {
-            wrapper->getClass()->finalize(cx, wrapper);
-        }
-    }
-
     NS_ASSERTION(mIdentity, "bad pointer!");
 #ifdef XP_WIN
     // Try to detect free'd pointer
     NS_ASSERTION(*(int*)mIdentity != 0xdddddddd, "bad pointer!");
     NS_ASSERTION(*(int*)mIdentity != 0,          "bad pointer!");
 #endif
 
     // Note that it's not safe to touch mNativeWrapper here since it's
--- a/js/src/xpconnect/tests/chrome/Makefile.in
+++ b/js/src/xpconnect/tests/chrome/Makefile.in
@@ -54,16 +54,17 @@ include $(topsrcdir)/config/rules.mk
 		test_sandboxImport.xul \
 		test_wrappers.xul \
 		test_bug484459.xul \
 		test_cows.xul \
 		test_bug517163.xul \
 		test_bug571849.xul \
 		test_bug601803.xul \
 		test_bug610390.xul \
+		test_bug614757.xul \
 		$(NULL)
 
 # Disabled until this test gets updated to test the new proxy based
 # wrappers.
 #		test_wrappers-2.xul \
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/js/src/xpconnect/tests/chrome/test_bug614757.xul
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=614757
+-->
+<window title="Mozilla Bug 601803"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=614757"
+     target="_blank">Mozilla Bug 614757</a>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+  /** Test for Bug 614757 **/
+
+  function go() {
+    is($('ifr').contentDocument.wrappedJSObject.getElementsByTagName('body')[0].toString().indexOf('Xray'),
+       -1, "Properly deep wrap");
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+
+  ]]></script>
+  <iframe type="content" src="http://mochi.test:8888/tests/js/src/xpconnect/tests/mochitest/file_evalInSandbox.html" onload="go()" id="ifr" />
+  </body>
+</window>
--- a/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp
+++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.cpp
@@ -80,16 +80,31 @@ bool
 CrossOriginWrapper::get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
                         js::Value *vp)
 {
     return JSCrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) &&
            WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(vp));
 }
 
 bool
+CrossOriginWrapper::call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp)
+{
+    return JSCrossCompartmentWrapper::call(cx, wrapper, argc, vp) &&
+           WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(vp));
+}
+
+bool
+CrossOriginWrapper::construct(JSContext *cx, JSObject *wrapper,
+                              uintN argc, js::Value *argv, js::Value *rval)
+{
+    return JSCrossCompartmentWrapper::construct(cx, wrapper, argc, argv, rval) &&
+           WrapperFactory::WaiveXrayAndWrap(cx, js::Jsvalify(rval));
+}
+
+bool
 CrossOriginWrapper::enter(JSContext *cx, JSObject *wrapper, jsid id, Action act)
 {
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
     if (!ssm) {
         return true;
     }
     JSStackFrame *fp = NULL;
     nsIPrincipal *principal = GetCompartmentPrincipal(wrappedObject(wrapper)->getCompartment());
--- a/js/src/xpconnect/wrappers/CrossOriginWrapper.h
+++ b/js/src/xpconnect/wrappers/CrossOriginWrapper.h
@@ -52,16 +52,20 @@ class CrossOriginWrapper : public JSCros
 
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                        bool set, js::PropertyDescriptor *desc);
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *wrapper, jsid id,
                                           bool set, js::PropertyDescriptor *desc);
     virtual bool get(JSContext *cx, JSObject *wrapper, JSObject *receiver, jsid id,
                      js::Value *vp);
 
+    virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp);
+    virtual bool construct(JSContext *cx, JSObject *wrapper,
+                           uintN argc, js::Value *argv, js::Value *rval);
+
     virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act);
     virtual void leave(JSContext *cx, JSObject *wrapper);
 
     static CrossOriginWrapper singleton;
 };
 
 }