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 442348 0e5ef9469604871660a21dc37e3a553125b5f533
parent 442347 33dd2766eab8fd99cc3345b9984ee597e2306caa
child 442349 1c9f10e217d127c8476fe624b579b8a8cb3cc03b
push id71463
push userjandemooij@gmail.com
push dateMon, 22 Oct 2018 15:41:47 +0000
treeherderautoland@0e5ef9469604 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1499045
milestone64.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 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;