Bug 975456 -- Preserve invariant that views on a neutered buffer have a NULL data pointer r=shu
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Fri, 21 Feb 2014 12:32:24 -0500
changeset 187463 03355461606cf026a4803438e1d8cced6d797369
parent 187462 41162ee807fe6a8bdb6cf46fe52591af0489f0e2
child 187464 a7c61b56251219bcbcfb4b3a685d8bdc0b10f87b
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs975456
milestone30.0a1
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
Bug 975456 -- Preserve invariant that views on a neutered buffer have a NULL data pointer r=shu
js/src/jit-test/tests/TypedObject/neutertypedobj.js
js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
js/src/vm/ArrayBufferObject.cpp
js/src/vm/ObjectImpl.h
--- a/js/src/jit-test/tests/TypedObject/neutertypedobj.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobj.js
@@ -1,11 +1,8 @@
-// FIXME bug 975456
-quit();
-
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
 var {StructType, uint32, storage} = TypedObject;
 var S = new StructType({f: uint32, g: uint32});
 
 function readFromS(s) {
   return s.f + s.g;
--- a/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
+++ b/js/src/jit-test/tests/TypedObject/neutertypedobjsizedarray.js
@@ -1,11 +1,8 @@
-// FIXME bug 975456
-quit();
-
 // Test the case where we neuter an instance of a fixed-sized array.
 // This is a bit of a tricky case because we cannot (necessarily) fold
 // the neuter check into the bounds check, as we obtain the bounds
 // directly from the type.
 
 if (!this.hasOwnProperty("TypedObject"))
   quit();
 
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -391,18 +391,27 @@ ArrayBufferObject::changeContents(JSCont
     // Grab out data before invalidating it.
     uint32_t byteLengthCopy = byteLength();
     uintptr_t oldDataPointer = uintptr_t(dataPointer());
     ArrayBufferViewObject *viewListHead = GetViewList(this);
 
     // Update all views.
     uintptr_t newDataPointer = uintptr_t(newHeader->elements());
     for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
-        uintptr_t newDataPtr = uintptr_t(view->getPrivate()) - oldDataPointer + newDataPointer;
-        view->setPrivate(reinterpret_cast<uint8_t*>(newDataPtr));
+        // Watch out for NULL data pointers in views. This either
+        // means that the view is not fully initialized (in which case
+        // it'll be initialized later with the correct pointer) or
+        // that the view has been neutered. In that case, the buffer
+        // is "en route" to being neutered but the isNeuteredBuffer()
+        // flag may not yet be set.
+        uint8_t *viewDataPointer = static_cast<uint8_t*>(view->getPrivate());
+        if (viewDataPointer) {
+            viewDataPointer += newDataPointer - oldDataPointer;
+            view->setPrivate(viewDataPointer);
+        }
 
         // Notify compiled jit code that the base pointer has moved.
         MarkObjectStateChange(cx, view);
     }
 
     // The list of views in the old header is reachable if the contents are
     // being transferred, so null it out
     SetViewList(this, nullptr);
@@ -1195,18 +1204,23 @@ ArrayBufferViewObject::trace(JSTracer *t
 {
     HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT);
     MarkSlot(trc, &bufSlot, "typedarray.buffer");
 
     /* Update obj's data slot if the array buffer moved. Note that during
      * initialization, bufSlot may still be JSVAL_VOID. */
     if (bufSlot.isObject()) {
         ArrayBufferObject &buf = bufSlot.toObject().as<ArrayBufferObject>();
-        int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
-        obj->initPrivate(buf.dataPointer() + offset);
+        if (buf.getElementsHeader()->isNeuteredBuffer()) {
+            // When a view is neutered, it is set to NULL
+            JS_ASSERT(obj->getPrivate() == nullptr);
+        } else {
+            int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
+            obj->initPrivate(buf.dataPointer() + offset);
+        }
     }
 
     /* Update NEXT_VIEW_SLOT, if the view moved. */
     IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT));
 }
 
 void
 ArrayBufferViewObject::prependToViews(ArrayBufferViewObject *viewsHead)
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -765,16 +765,17 @@ class ObjectElements
         NONWRITABLE_ARRAY_LENGTH    = 0x8
     };
 
   private:
     friend class ::JSObject;
     friend class ObjectImpl;
     friend class ArrayObject;
     friend class ArrayBufferObject;
+    friend class ArrayBufferViewObject;
     friend class TypedArrayObject;
     friend class Nursery;
 
     template <ExecutionMode mode>
     friend bool
     ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cx,
                    Handle<ArrayObject*> obj, HandleId id,
                    unsigned attrs, HandleValue value, bool setterIsStrict);