Bug 1499045 part 2 - Add ArrayBufferViewObject::init to deduplicate more TypedArrayObject/DataViewObject code. r=jwalden
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 22 Oct 2018 14:46:35 +0000
changeset 490760 0e5ef9469604871660a21dc37e3a553125b5f533
parent 490759 33dd2766eab8fd99cc3345b9984ee597e2306caa
child 490761 1c9f10e217d127c8476fe624b579b8a8cb3cc03b
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
reviewersjwalden
bugs1499045
milestone64.0a1
Bug 1499045 part 2 - Add ArrayBufferViewObject::init to deduplicate more TypedArrayObject/DataViewObject code. r=jwalden Depends on D8726 Differential Revision: https://phabricator.services.mozilla.com/D8727
js/src/builtin/DataViewObject.cpp
js/src/vm/ArrayBufferViewObject.cpp
js/src/vm/ArrayBufferViewObject.h
js/src/vm/TypedArrayObject.cpp
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -43,71 +43,21 @@ DataViewObject*
 DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
                        Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, HandleObject proto)
 {
     if (arrayBuffer->isDetached()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return nullptr;
     }
 
-    MOZ_ASSERT(byteOffset <= INT32_MAX);
-    MOZ_ASSERT(byteLength <= INT32_MAX);
-    MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
-
     DataViewObject* obj = NewObjectWithClassProto<DataViewObject>(cx, proto);
-    if (!obj) {
+    if (!obj || !obj->init(cx, arrayBuffer, byteOffset, byteLength, /* bytesPerElement = */ 1)) {
         return nullptr;
     }
 
-    // Caller should have established these preconditions, and no
-    // (non-self-hosted) JS code has had an opportunity to run so nothing can
-    // have invalidated them.
-    MOZ_ASSERT(byteOffset <= arrayBuffer->byteLength());
-    MOZ_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength());
-
-    // The isSharedMemory property is invariant.  Self-hosting code that sets
-    // BUFFER_SLOT or the private slot (if it does) must maintain it by always
-    // setting those to reference shared memory.
-    bool isSharedMemory = IsSharedArrayBuffer(arrayBuffer.get());
-    if (isSharedMemory) {
-        obj->setIsSharedMemory();
-    }
-
-    obj->setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
-    obj->setFixedSlot(LENGTH_SLOT, Int32Value(byteLength));
-    obj->setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer));
-
-    SharedMem<uint8_t*> ptr = arrayBuffer->dataPointerEither();
-    obj->initDataPointer(ptr + byteOffset);
-
-    // Include a barrier if the data view's data pointer is in the nursery, as
-    // is done for typed arrays.
-    if (!IsInsideNursery(obj) && cx->nursery().isInside(ptr)) {
-        // Shared buffer data should never be nursery-allocated, so we
-        // need to fail here if isSharedMemory.  However, mmap() can
-        // place a SharedArrayRawBuffer up against the bottom end of a
-        // nursery chunk, and a zero-length buffer will erroneously be
-        // perceived as being inside the nursery; sidestep that.
-        if (isSharedMemory) {
-            MOZ_ASSERT(arrayBuffer->byteLength() == 0 &&
-                       (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0);
-        } else {
-            cx->runtime()->gc.storeBuffer().putWholeCell(obj);
-        }
-    }
-
-    // Verify that the private slot is at the expected place
-    MOZ_ASSERT(obj->numFixedSlots() == DATA_SLOT);
-
-    if (arrayBuffer->is<ArrayBufferObject>()) {
-        if (!arrayBuffer->as<ArrayBufferObject>().addView(cx, obj)) {
-            return nullptr;
-        }
-    }
-
     return obj;
 }
 
 // ES2017 draft rev 931261ecef9b047b14daacf82884134da48dfe0f
 // 24.3.2.1 DataView (extracted part of the main algorithm)
 bool
 DataViewObject::getAndCheckConstructorArgs(JSContext* cx, HandleObject bufobj, const CallArgs& args,
                                            uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr)
--- a/js/src/vm/ArrayBufferViewObject.cpp
+++ b/js/src/vm/ArrayBufferViewObject.cpp
@@ -7,16 +7,17 @@
 #include "vm/ArrayBufferViewObject.h"
 
 #include "builtin/DataViewObject.h"
 #include "gc/Nursery.h"
 #include "vm/JSContext.h"
 #include "vm/TypedArrayObject.h"
 
 #include "gc/Nursery-inl.h"
+#include "vm/ArrayBufferObject-inl.h"
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 /*
  * This method is used to trace TypedArrayObjects and DataViewObjects. We need
  * a custom tracer to move the object's data pointer if its owner was moved and
  * stores its data inline.
@@ -105,16 +106,88 @@ ArrayBufferViewObject::bufferObject(JSCo
         Rooted<TypedArrayObject*> typedArray(cx, &thisObject->as<TypedArrayObject>());
         if (!TypedArrayObject::ensureHasBuffer(cx, typedArray)) {
             return nullptr;
         }
     }
     return thisObject->bufferEither();
 }
 
+bool
+ArrayBufferViewObject::init(JSContext* cx, ArrayBufferObjectMaybeShared* buffer,
+                            uint32_t byteOffset, uint32_t length, uint32_t bytesPerElement)
+{
+    MOZ_ASSERT_IF(!buffer, byteOffset == 0);
+    MOZ_ASSERT_IF(buffer, !buffer->isDetached());
+
+    MOZ_ASSERT(byteOffset <= INT32_MAX);
+    MOZ_ASSERT(length <= INT32_MAX);
+    MOZ_ASSERT(byteOffset + length < UINT32_MAX);
+
+    MOZ_ASSERT_IF(is<TypedArrayObject>(),
+                  length < INT32_MAX / bytesPerElement);
+
+    // The isSharedMemory property is invariant.  Self-hosting code that
+    // sets BUFFER_SLOT or the private slot (if it does) must maintain it by
+    // always setting those to reference shared memory.
+    if (buffer && buffer->is<SharedArrayBufferObject>()) {
+        setIsSharedMemory();
+    }
+
+    initFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset));
+    initFixedSlot(LENGTH_SLOT, Int32Value(length));
+    initFixedSlot(BUFFER_SLOT, ObjectOrNullValue(buffer));
+
+    if (buffer) {
+        SharedMem<uint8_t*> ptr = buffer->dataPointerEither();
+        initDataPointer(ptr + byteOffset);
+
+        // Only ArrayBuffers used for inline typed objects can have
+        // nursery-allocated data and we shouldn't see such buffers here.
+        MOZ_ASSERT_IF(buffer->byteLength() > 0, !cx->nursery().isInside(ptr));
+    } else {
+        MOZ_ASSERT(is<TypedArrayObject>());
+        MOZ_ASSERT(length * bytesPerElement <= TypedArrayObject::INLINE_BUFFER_LIMIT);
+        void* data = fixedData(TypedArrayObject::FIXED_DATA_START);
+        initPrivate(data);
+        memset(data, 0, length * bytesPerElement);
+#ifdef DEBUG
+        if (length == 0) {
+            uint8_t* elements = static_cast<uint8_t*>(data);
+            elements[0] = ZeroLengthArrayData;
+        }
+#endif
+    }
+
+#ifdef DEBUG
+    if (buffer) {
+        uint32_t viewByteLength = length * bytesPerElement;
+        uint32_t viewByteOffset = byteOffset;
+        uint32_t bufferByteLength = buffer->byteLength();
+        // Unwraps are safe: both are for the pointer value.
+        MOZ_ASSERT_IF(IsArrayBuffer(buffer),
+                      buffer->dataPointerEither().unwrap(/*safe*/) <= dataPointerEither().unwrap(/*safe*/));
+        MOZ_ASSERT(bufferByteLength - viewByteOffset >= viewByteLength);
+        MOZ_ASSERT(viewByteOffset <= bufferByteLength);
+    }
+
+    // Verify that the private slot is at the expected place.
+    MOZ_ASSERT(numFixedSlots() == DATA_SLOT);
+#endif
+
+    // ArrayBufferObjects track their views to support detaching.
+    if (buffer && buffer->is<ArrayBufferObject>()) {
+        if (!buffer->as<ArrayBufferObject>().addView(cx, this)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
 /* JS Friend API */
 
 JS_FRIEND_API(bool)
 JS_IsArrayBufferViewObject(JSObject* obj)
 {
     obj = CheckedUnwrap(obj);
     return obj && obj->is<ArrayBufferViewObject>();
 }
--- a/js/src/vm/ArrayBufferViewObject.h
+++ b/js/src/vm/ArrayBufferViewObject.h
@@ -58,16 +58,19 @@ class ArrayBufferViewObject : public Nat
   private:
     void* dataPointerEither_() const {
         // Note, do not check whether shared or not
         // Keep synced with js::Get<Type>ArrayLengthAndData in jsfriendapi.h!
         return static_cast<void*>(getPrivate(DATA_SLOT));
     }
 
   public:
+    MOZ_MUST_USE bool init(JSContext* cx, ArrayBufferObjectMaybeShared* buffer,
+                           uint32_t byteOffset, uint32_t length, uint32_t bytesPerElement);
+
     static ArrayBufferObjectMaybeShared* bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*> obj);
 
     void notifyBufferDetached(JSContext* cx, void* newData);
 
     // By construction we only need unshared variants here.  See
     // comments in ArrayBufferObject.cpp.
     uint8_t* dataPointerUnshared(const JS::AutoRequireNoGC&);
     void setDataPointerUnshared(uint8_t* data);
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -429,18 +429,16 @@ class TypedArrayObjectTemplate : public 
         return &obj->as<TypedArrayObject>();
     }
 
     static TypedArrayObject*
     makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
                  CreateSingleton createSingleton, uint32_t byteOffset, uint32_t len,
                  HandleObject proto)
     {
-        MOZ_ASSERT_IF(!buffer, byteOffset == 0);
-        MOZ_ASSERT_IF(buffer, !buffer->isDetached());
         MOZ_ASSERT(len < INT32_MAX / sizeof(NativeType));
 
         gc::AllocKind allocKind = buffer
                                   ? gc::GetGCObjectKind(instanceClass())
                                   : AllocKindForLazyBuffer(len * sizeof(NativeType));
 
         // Subclassing mandates that we hand in the proto every time. Most of
         // the time, though, that [[Prototype]] will not be interesting. If
@@ -456,89 +454,20 @@ class TypedArrayObjectTemplate : public 
 
         AutoSetNewObjectMetadata metadata(cx);
         Rooted<TypedArrayObject*> obj(cx);
         if (proto && proto != checkProto) {
             obj = makeProtoInstance(cx, proto, allocKind);
         } else {
             obj = makeTypedInstance(cx, createSingleton, allocKind);
         }
-        if (!obj) {
+        if (!obj || !obj->init(cx, buffer, byteOffset, len, BYTES_PER_ELEMENT)) {
             return nullptr;
         }
 
-        bool isSharedMemory = buffer && IsSharedArrayBuffer(buffer.get());
-
-        obj->setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectOrNullValue(buffer));
-        // This is invariant.  Self-hosting code that sets BUFFER_SLOT
-        // (if it does) must maintain it, should it need to.
-        if (isSharedMemory) {
-            obj->setIsSharedMemory();
-        }
-
-        if (buffer) {
-            obj->initDataPointer(buffer->dataPointerEither() + byteOffset);
-
-            // If the buffer is for an inline typed object, the data pointer
-            // may be in the nursery, so include a barrier to make sure this
-            // object is updated if that typed object moves.
-            auto ptr = buffer->dataPointerEither();
-            if (!IsInsideNursery(obj) && cx->nursery().isInside(ptr)) {
-                // Shared buffer data should never be nursery-allocated, so we
-                // need to fail here if isSharedMemory.  However, mmap() can
-                // place a SharedArrayRawBuffer up against the bottom end of a
-                // nursery chunk, and a zero-length buffer will erroneously be
-                // perceived as being inside the nursery; sidestep that.
-                if (isSharedMemory) {
-                    MOZ_ASSERT(buffer->byteLength() == 0 &&
-                               (uintptr_t(ptr.unwrapValue()) & gc::ChunkMask) == 0);
-                } else {
-                    cx->runtime()->gc.storeBuffer().putWholeCell(obj);
-                }
-            }
-        } else {
-            void* data = obj->fixedData(FIXED_DATA_START);
-            obj->initPrivate(data);
-            memset(data, 0, len * sizeof(NativeType));
-#ifdef DEBUG
-            if (len == 0) {
-                uint8_t* elements = static_cast<uint8_t*>(data);
-                elements[0] = ZeroLengthArrayData;
-            }
-#endif
-        }
-
-        obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(len));
-        obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
-
-#ifdef DEBUG
-        if (buffer) {
-            uint32_t arrayByteLength = obj->byteLength();
-            uint32_t arrayByteOffset = obj->byteOffset();
-            uint32_t bufferByteLength = buffer->byteLength();
-            // Unwraps are safe: both are for the pointer value.
-            if (IsArrayBuffer(buffer.get())) {
-                MOZ_ASSERT_IF(!AsArrayBuffer(buffer.get()).isDetached(),
-                              buffer->dataPointerEither().unwrap(/*safe*/) <= obj->dataPointerEither().unwrap(/*safe*/));
-            }
-            MOZ_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength);
-            MOZ_ASSERT(arrayByteOffset <= bufferByteLength);
-        }
-
-        // Verify that the private slot is at the expected place
-        MOZ_ASSERT(obj->numFixedSlots() == TypedArrayObject::DATA_SLOT);
-#endif
-
-        // ArrayBufferObjects track their views to support detaching.
-        if (buffer && buffer->is<ArrayBufferObject>()) {
-            if (!buffer->as<ArrayBufferObject>().addView(cx, obj)) {
-                return nullptr;
-            }
-        }
-
         return obj;
     }
 
     static TypedArrayObject*
     makeTemplateObject(JSContext* cx, int32_t len)
     {
         MOZ_ASSERT(len >= 0);
         size_t nbytes;