Bug 1437471 - Use singleton type only for big typed arrays created with an existing buffer. r=bhackett
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 13 Feb 2018 12:39:59 +0100
changeset 403580 e7519d21db96d2e0e48b84668ed8ac47b1e59b9a
parent 403579 246493ebf8627765eaa25e1ad4ce91c7acff8354
child 403581 c32ead4e35257a9067fa2fa73a96fafca4acdacd
push id99832
push userjandemooij@gmail.com
push dateTue, 13 Feb 2018 11:40:34 +0000
treeherdermozilla-inbound@e7519d21db96 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs1437471
milestone60.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 1437471 - Use singleton type only for big typed arrays created with an existing buffer. r=bhackett
js/src/builtin/DataViewObject.cpp
js/src/vm/TypedArrayObject.cpp
js/src/vm/TypedArrayObject.h
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -38,20 +38,18 @@ using namespace js;
 using namespace js::gc;
 
 using mozilla::AssertedCast;
 using JS::CanonicalizeNaN;
 using JS::ToInt32;
 using JS::ToUint32;
 
 static NewObjectKind
-DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto)
+DataViewNewObjectKind(JSContext* cx)
 {
-    if (!proto && byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH)
-        return SingletonObject;
     jsbytecode* pc;
     JSScript* script = cx->currentScript(&pc);
     if (script && ObjectGroup::useSingletonForAllocationSite(script, pc, &DataViewObject::class_))
         return SingletonObject;
     return GenericObject;
 }
 
 DataViewObject*
@@ -65,17 +63,17 @@ DataViewObject::create(JSContext* cx, ui
 
     MOZ_ASSERT(byteOffset <= INT32_MAX);
     MOZ_ASSERT(byteLength <= INT32_MAX);
     MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
 
     RootedObject proto(cx, protoArg);
     RootedObject obj(cx);
 
-    NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto);
+    NewObjectKind newKind = DataViewNewObjectKind(cx);
     obj = NewObjectWithClassProto(cx, &class_, proto, newKind);
     if (!obj)
         return nullptr;
 
     if (!proto) {
         if (byteLength >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
             MOZ_ASSERT(obj->isSingleton());
         } else {
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -312,16 +312,21 @@ NewArray(JSContext* cx, uint32_t nelemen
 
 namespace {
 
 enum class SpeciesConstructorOverride {
     None,
     ArrayBuffer
 };
 
+enum class CreateSingleton {
+    Yes,
+    No
+};
+
 template<typename NativeType>
 class TypedArrayObjectTemplate : public TypedArrayObject
 {
     friend class TypedArrayObject;
 
   public:
     static constexpr Scalar::Type ArrayTypeID() { return TypeIDOfType<NativeType>::id; }
     static bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); }
@@ -400,20 +405,21 @@ class TypedArrayObjectTemplate : public 
     {
         MOZ_ASSERT(proto);
 
         JSObject* obj = NewObjectWithClassProto(cx, instanceClass(), proto, allocKind);
         return obj ? &obj->as<TypedArrayObject>() : nullptr;
     }
 
     static TypedArrayObject*
-    makeTypedInstance(JSContext* cx, uint32_t len, gc::AllocKind allocKind)
+    makeTypedInstance(JSContext* cx, uint32_t len, CreateSingleton createSingleton,
+                      gc::AllocKind allocKind)
     {
         const Class* clasp = instanceClass();
-        if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_BYTE_LENGTH) {
+        if (createSingleton == CreateSingleton::Yes) {
             JSObject* obj = NewBuiltinClassInstance(cx, clasp, allocKind, SingletonObject);
             if (!obj)
                 return nullptr;
             return &obj->as<TypedArrayObject>();
         }
 
         jsbytecode* pc;
         RootedScript script(cx, cx->currentScript(&pc));
@@ -429,18 +435,19 @@ class TypedArrayObjectTemplate : public 
         {
             return nullptr;
         }
 
         return &obj->as<TypedArrayObject>();
     }
 
     static TypedArrayObject*
-    makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset,
-                 uint32_t len, HandleObject proto)
+    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
                                   ? GetGCObjectKind(instanceClass())
                                   : AllocKindForLazyBuffer(len * sizeof(NativeType));
@@ -456,17 +463,17 @@ class TypedArrayObjectTemplate : public 
                 return nullptr;
         }
 
         AutoSetNewObjectMetadata metadata(cx);
         Rooted<TypedArrayObject*> obj(cx);
         if (proto && proto != checkProto)
             obj = makeProtoInstance(cx, proto, allocKind);
         else
-            obj = makeTypedInstance(cx, len, allocKind);
+            obj = makeTypedInstance(cx, len, createSingleton, allocKind);
         if (!obj)
             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.
@@ -829,18 +836,22 @@ class TypedArrayObjectTemplate : public 
     fromBufferSameCompartment(JSContext* cx, HandleArrayBufferObjectMaybeShared buffer,
                               uint64_t byteOffset, uint64_t lengthIndex, HandleObject proto)
     {
         // Steps 9-12.
         uint32_t length;
         if (!computeAndCheckLength(cx, buffer, byteOffset, lengthIndex, &length))
             return nullptr;
 
+        CreateSingleton createSingleton = CreateSingleton::No;
+        if (length * sizeof(NativeType) >= TypedArrayObject::SINGLETON_BYTE_LENGTH)
+            createSingleton = CreateSingleton::Yes;
+
         // Steps 13-17.
-        return makeInstance(cx, buffer, uint32_t(byteOffset), length, proto);
+        return makeInstance(cx, buffer, createSingleton, uint32_t(byteOffset), length, proto);
     }
 
     // Create a TypedArray object in another compartment.
     //
     // ES6 supports creating a TypedArray in global A (using global A's
     // TypedArray constructor) backed by an ArrayBuffer created in global B.
     //
     // Our TypedArrayObject implementation doesn't support a TypedArray in
@@ -888,17 +899,18 @@ class TypedArrayObjectTemplate : public 
         {
             JSAutoCompartment ac(cx, unwrappedBuffer);
 
             RootedObject wrappedProto(cx, protoRoot);
             if (!cx->compartment()->wrap(cx, &wrappedProto))
                 return nullptr;
 
             typedArray =
-                makeInstance(cx, unwrappedBuffer, uint32_t(byteOffset), length, wrappedProto);
+                makeInstance(cx, unwrappedBuffer, CreateSingleton::No, uint32_t(byteOffset),
+                             length, wrappedProto);
             if (!typedArray)
                 return nullptr;
         }
 
         if (!cx->compartment()->wrap(cx, &typedArray))
             return nullptr;
 
         return typedArray;
@@ -963,17 +975,17 @@ class TypedArrayObjectTemplate : public 
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
             return nullptr;
         }
 
         Rooted<ArrayBufferObject*> buffer(cx);
         if (!maybeCreateArrayBuffer(cx, uint32_t(nelements), BYTES_PER_ELEMENT, nullptr, &buffer))
             return nullptr;
 
-        return makeInstance(cx, buffer, 0, uint32_t(nelements), proto);
+        return makeInstance(cx, buffer, CreateSingleton::No, 0, uint32_t(nelements), proto);
     }
 
     static bool
     AllocateArrayBuffer(JSContext* cx, HandleObject ctor,
                         uint32_t count, uint32_t unit,
                         MutableHandle<ArrayBufferObject*> buffer);
 
     static JSObject*
@@ -1219,17 +1231,18 @@ TypedArrayObjectTemplate<T>::fromTypedAr
 
     // Step 19.b or 24.1.1.4 step 4.
     if (srcArray->hasDetachedBuffer()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return nullptr;
     }
 
     // Steps 3-4 (remaining part), 20-23.
-    Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, elementLength, proto));
+    Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, CreateSingleton::No, 0,
+                                                   elementLength, proto));
     if (!obj)
         return nullptr;
 
     // Steps 19.c-f or 24.1.1.4 steps 5-7.
     MOZ_ASSERT(!obj->isSharedMemory());
     if (isShared) {
         if (!ElementSpecific<T, SharedOps>::setFromTypedArray(obj, srcArray, 0))
             return nullptr;
@@ -1279,17 +1292,18 @@ TypedArrayObjectTemplate<T>::fromObject(
         // Step 6.b.
         uint32_t len = array->getDenseInitializedLength();
 
         // Step 6.c.
         Rooted<ArrayBufferObject*> buffer(cx);
         if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer))
             return nullptr;
 
-        Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
+        Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, CreateSingleton::No, 0,
+                                                       len, proto));
         if (!obj)
             return nullptr;
 
         // Steps 6.d-e.
         MOZ_ASSERT(!obj->isSharedMemory());
         if (!ElementSpecific<T, UnsharedOps>::initFromIterablePackedArray(cx, obj, array))
             return nullptr;
 
@@ -1346,17 +1360,18 @@ TypedArrayObjectTemplate<T>::fromObject(
     if (!GetLengthProperty(cx, arrayLike, &len))
         return nullptr;
 
     // Step 10.
     Rooted<ArrayBufferObject*> buffer(cx);
     if (!maybeCreateArrayBuffer(cx, len, BYTES_PER_ELEMENT, nullptr, &buffer))
         return nullptr;
 
-    Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, 0, len, proto));
+    Rooted<TypedArrayObject*> obj(cx, makeInstance(cx, buffer, CreateSingleton::No, 0, len,
+                                                   proto));
     if (!obj)
         return nullptr;
 
     // Steps 11-12.
     MOZ_ASSERT(!obj->isSharedMemory());
     if (!ElementSpecific<T, UnsharedOps>::setFromNonTypedArray(cx, obj, arrayLike, len))
         return nullptr;
 
--- a/js/src/vm/TypedArrayObject.h
+++ b/js/src/vm/TypedArrayObject.h
@@ -181,18 +181,19 @@ class TypedArrayObject : public NativeOb
 
     void notifyBufferDetached(JSContext* cx, void* newData);
 
     static bool
     GetTemplateObjectForNative(JSContext* cx, Native native, uint32_t len,
                                MutableHandleObject res);
 
     /*
-     * Byte length above which created typed arrays and data views will have
-     * singleton types regardless of the context in which they are created.
+     * Byte length above which created typed arrays will have singleton types
+     * regardless of the context in which they are created. This only applies to
+     * typed arrays created with an existing ArrayBuffer.
      */
     static const uint32_t SINGLETON_BYTE_LENGTH = 1024 * 1024 * 10;
 
     static bool isOriginalLengthGetter(Native native);
 
     ArrayBufferObject* bufferUnshared() const {
         MOZ_ASSERT(!isSharedMemory());
         JSObject* obj = bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();