Bug 1232966 - SharedArrayBuffer.prototype.slice. r=arai
authorLars T Hansen <lhansen@mozilla.com>
Tue, 01 Nov 2016 09:50:25 +0100
changeset 348070 334620bedacab85bcfed7934a714d95d9945455d
parent 348069 3e3ae9ca5f809c355799fe04e4f81315a8104f3d
child 348071 55398c6f895777f83bdec5cc4bbef3af9f958930
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarai
bugs1232966
milestone52.0a1
Bug 1232966 - SharedArrayBuffer.prototype.slice. r=arai
js/src/builtin/TypedArray.js
js/src/js.msg
js/src/vm/SelfHosting.cpp
js/src/vm/SharedArrayObject.cpp
js/src/vm/SharedArrayObject.h
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1664,14 +1664,78 @@ function ArrayBufferStaticSlice(buf, sta
 
 // ES 2016 draft Mar 25, 2016 24.1.3.3.
 function ArrayBufferSpecies() {
     // Step 1.
     return this;
 }
 _SetCanonicalName(ArrayBufferSpecies, "get [Symbol.species]");
 
-// ES 2017 proposal
+// Shared memory and atomics proposal (30 Oct 2016)
 function SharedArrayBufferSpecies() {
     // Step 1.
     return this;
 }
 _SetCanonicalName(SharedArrayBufferSpecies, "get [Symbol.species]");
+
+// Shared memory and atomics proposal 6.2.1.5.3 (30 Oct 2016)
+// http://tc39.github.io/ecmascript_sharedmem/shmem.html
+function SharedArrayBufferSlice(start, end) {
+    // Step 1.
+    var O = this;
+
+    // Steps 2-4,
+    // This function is not generic.
+    if (!IsObject(O) || !IsSharedArrayBuffer(O)) {
+        return callFunction(CallSharedArrayBufferMethodIfWrapped, O, start, end,
+                            "SharedArrayBufferSlice");
+    }
+
+    // Step 5.
+    var len = SharedArrayBufferByteLength(O);
+
+    // Step 6.
+    var relativeStart = ToInteger(start);
+
+    // Step 7.
+    var first = relativeStart < 0 ? std_Math_max(len + relativeStart, 0)
+                                  : std_Math_min(relativeStart, len);
+
+    // Step 8.
+    var relativeEnd = end === undefined ? len
+                                        : ToInteger(end);
+
+    // Step 9.
+    var final = relativeEnd < 0 ? std_Math_max(len + relativeEnd, 0)
+                                : std_Math_min(relativeEnd, len);
+
+    // Step 10.
+    var newLen = std_Math_max(final - first, 0);
+
+    // Step 11
+    var ctor = SpeciesConstructor(O, GetBuiltinConstructor("SharedArrayBuffer"));
+
+    // Step 12.
+    var new_ = new ctor(newLen);
+
+    // Step 13.
+    var isWrapped = false;
+    if (!IsSharedArrayBuffer(new_)) {
+        if (!IsWrappedSharedArrayBuffer(new_))
+            ThrowTypeError(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED);
+        isWrapped = true;
+    }
+
+    // Step 14.
+    if (new_ === O)
+        ThrowTypeError(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED);
+
+    // Step 15.
+    var actualLen = PossiblyWrappedSharedArrayBufferByteLength(new_);
+    if (actualLen < newLen)
+        ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen);
+
+    // Steps 16-18.
+    SharedArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped);
+
+    // Step 19.
+    return new_;
+}
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -523,16 +523,19 @@ MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1
 MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED,    0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
 MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer")
 MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%")
 MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object")
 MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}")
 
 // Shared array buffer
 MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH,  0, JSEXN_RANGEERR, "length argument out of range")
+MSG_DEF(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected SharedArrayBuffer, but species constructor returned non-SharedArrayBuffer")
+MSG_DEF(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different SharedArrayBuffer, but species constructor returned same SharedArrayBuffer")
+MSG_DEF(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected SharedArrayBuffer with at least {0} bytes, but species constructor returns SharedArrayBuffer with {1} bytes")
 
 // Reflect
 MSG_DEF(JSMSG_BAD_PARSE_NODE,          0, JSEXN_INTERNALERR, "bad parse node")
 
 // Symbol
 MSG_DEF(JSMSG_SYMBOL_TO_STRING,        0, JSEXN_TYPEERR, "can't convert symbol to string")
 MSG_DEF(JSMSG_SYMBOL_TO_NUMBER,        0, JSEXN_TYPEERR, "can't convert symbol to number")
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -972,16 +972,17 @@ intrinsic_GeneratorSetClosed(JSContext* 
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isObject());
 
     GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>();
     genObj->setClosed();
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_IsWrappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
     if (!args[0].isObject()) {
         args.rval().setBoolean(false);
@@ -995,75 +996,78 @@ intrinsic_IsWrappedArrayBuffer(JSContext
     }
 
     JSObject* unwrapped = CheckedUnwrap(obj);
     if (!unwrapped) {
         JS_ReportErrorASCII(cx, "Permission denied to access object");
         return false;
     }
 
-    args.rval().setBoolean(unwrapped->is<ArrayBufferObject>());
+    args.rval().setBoolean(unwrapped->is<T>());
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_ArrayBufferByteLength(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isObject());
-    MOZ_ASSERT(args[0].toObject().is<ArrayBufferObject>());
-
-    size_t byteLength = args[0].toObject().as<ArrayBufferObject>().byteLength();
+    MOZ_ASSERT(args[0].toObject().is<T>());
+
+    size_t byteLength = args[0].toObject().as<T>().byteLength();
     args.rval().setInt32(mozilla::AssertedCast<int32_t>(byteLength));
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_PossiblyWrappedArrayBufferByteLength(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
     JSObject* obj = CheckedUnwrap(&args[0].toObject());
     if (!obj) {
         JS_ReportErrorASCII(cx, "Permission denied to access object");
         return false;
     }
 
-    uint32_t length = obj->as<ArrayBufferObject>().byteLength();
+    uint32_t length = obj->as<T>().byteLength();
     args.rval().setInt32(mozilla::AssertedCast<int32_t>(length));
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 5);
 
     bool isWrapped = args[4].toBoolean();
-    Rooted<ArrayBufferObject*> toBuffer(cx);
+    Rooted<T*> toBuffer(cx);
     if (!isWrapped) {
-        toBuffer = &args[0].toObject().as<ArrayBufferObject>();
+        toBuffer = &args[0].toObject().as<T>();
     } else {
         JSObject* wrapped = &args[0].toObject();
         MOZ_ASSERT(wrapped->is<WrapperObject>());
         RootedObject toBufferObj(cx, CheckedUnwrap(wrapped));
         if (!toBufferObj) {
             JS_ReportErrorASCII(cx, "Permission denied to access object");
             return false;
         }
-        toBuffer = toBufferObj.as<ArrayBufferObject>();
+        toBuffer = toBufferObj.as<T>();
     }
-    Rooted<ArrayBufferObject*> fromBuffer(cx, &args[1].toObject().as<ArrayBufferObject>());
+    Rooted<T*> fromBuffer(cx, &args[1].toObject().as<T>());
     uint32_t fromIndex = uint32_t(args[2].toInt32());
     uint32_t count = uint32_t(args[3].toInt32());
 
-    ArrayBufferObject::copyData(toBuffer, fromBuffer, fromIndex, count);
+    T::copyData(toBuffer, fromBuffer, fromIndex, count);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 intrinsic_IsSpecificTypedArray(JSContext* cx, unsigned argc, Value* vp, Scalar::Type type)
 {
@@ -2363,23 +2367,36 @@ static const JSFunctionSpec intrinsic_fu
 
     JS_FN("GeneratorIsRunning",      intrinsic_GeneratorIsRunning,      1,0),
     JS_FN("GeneratorSetClosed",      intrinsic_GeneratorSetClosed,      1,0),
 
     JS_FN("IsArrayBuffer",
           intrinsic_IsInstanceOfBuiltin<ArrayBufferObject>,             1,0),
     JS_FN("IsSharedArrayBuffer",
           intrinsic_IsInstanceOfBuiltin<SharedArrayBufferObject>,       1,0),
-    JS_FN("IsWrappedArrayBuffer",    intrinsic_IsWrappedArrayBuffer,    1,0),
-
-    JS_INLINABLE_FN("ArrayBufferByteLength",   intrinsic_ArrayBufferByteLength, 1,0,
+    JS_FN("IsWrappedArrayBuffer",
+          intrinsic_IsWrappedArrayBuffer<ArrayBufferObject>,            1,0),
+    JS_FN("IsWrappedSharedArrayBuffer",
+          intrinsic_IsWrappedArrayBuffer<SharedArrayBufferObject>,      1,0),
+
+    JS_INLINABLE_FN("ArrayBufferByteLength",
+                    intrinsic_ArrayBufferByteLength<ArrayBufferObject>, 1,0,
                     IntrinsicArrayBufferByteLength),
-    JS_INLINABLE_FN("PossiblyWrappedArrayBufferByteLength", intrinsic_PossiblyWrappedArrayBufferByteLength, 1,0,
+    JS_INLINABLE_FN("PossiblyWrappedArrayBufferByteLength",
+                    intrinsic_PossiblyWrappedArrayBufferByteLength<ArrayBufferObject>, 1,0,
                     IntrinsicPossiblyWrappedArrayBufferByteLength),
-    JS_FN("ArrayBufferCopyData",     intrinsic_ArrayBufferCopyData,     5,0),
+    JS_FN("ArrayBufferCopyData",
+          intrinsic_ArrayBufferCopyData<ArrayBufferObject>,             5,0),
+
+    JS_FN("SharedArrayBufferByteLength",
+          intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>,     1,0),
+    JS_FN("PossiblyWrappedSharedArrayBufferByteLength",
+          intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>, 1,0),
+    JS_FN("SharedArrayBufferCopyData",
+          intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>,       5,0),
 
     JS_FN("IsUint8TypedArray",        intrinsic_IsUint8TypedArray,      1,0),
     JS_FN("IsInt8TypedArray",         intrinsic_IsInt8TypedArray,       1,0),
     JS_FN("IsUint16TypedArray",       intrinsic_IsUint16TypedArray,     1,0),
     JS_FN("IsInt16TypedArray",        intrinsic_IsInt16TypedArray,      1,0),
     JS_FN("IsUint32TypedArray",       intrinsic_IsUint32TypedArray,     1,0),
     JS_FN("IsInt32TypedArray",        intrinsic_IsInt32TypedArray,      1,0),
     JS_FN("IsFloat32TypedArray",      intrinsic_IsFloat32TypedArray,    1,0),
@@ -2404,16 +2421,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0),
     JS_FN("SetOverlappingTypedElements",intrinsic_SetOverlappingTypedElements,3,0),
 
     JS_INLINABLE_FN("SetDisjointTypedElements",intrinsic_SetDisjointTypedElements,3,0,
                     IntrinsicSetDisjointTypedElements),
 
     JS_FN("CallArrayBufferMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<ArrayBufferObject>>, 2, 0),
+    JS_FN("CallSharedArrayBufferMethodIfWrapped",
+          CallNonGenericSelfhostedMethod<Is<SharedArrayBufferObject>>, 2, 0),
     JS_FN("CallTypedArrayMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<TypedArrayObject>>, 2, 0),
 
     JS_FN("CallLegacyGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<LegacyGeneratorObject>>, 2, 0),
     JS_FN("CallStarGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
 
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -322,16 +322,30 @@ SharedArrayBufferObject::addSizeOfExclud
     // some threads might be to high (if the refcount goes up) or too low (if
     // the refcount goes down). But that's unlikely and hard to avoid, so we
     // just live with the risk.
     const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
     info->objectsNonHeapElementsShared +=
         buf.byteLength() / buf.rawBufferObject()->refcount();
 }
 
+/* static */ void
+SharedArrayBufferObject::copyData(Handle<SharedArrayBufferObject*> toBuffer,
+                                  Handle<SharedArrayBufferObject*> fromBuffer,
+                                  uint32_t fromIndex, uint32_t count)
+{
+    MOZ_ASSERT(toBuffer->byteLength() >= count);
+    MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
+    MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
+
+    jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared(),
+                                              fromBuffer->dataPointerShared() + fromIndex,
+                                              count);
+}
+
 static const ClassSpec SharedArrayBufferObjectProtoClassSpec = {
     DELEGATED_CLASSSPEC(SharedArrayBufferObject::class_.spec),
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
@@ -371,16 +385,17 @@ static const JSFunctionSpec static_funct
 };
 
 static const JSPropertySpec static_properties[] = {
     JS_SELF_HOSTED_SYM_GET(species, "SharedArrayBufferSpecies", 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec prototype_functions[] = {
+    JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
     JS_FS_END
 };
 
 static const JSPropertySpec prototype_properties[] = {
     JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0),
     JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY),
     JS_PS_END
 };
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -142,16 +142,20 @@ class SharedArrayBufferObject : public A
                                         SharedArrayRawBuffer* buffer,
                                         HandleObject proto = nullptr);
 
     static void Finalize(FreeOp* fop, JSObject* obj);
 
     static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
                                        JS::ClassInfo* info);
 
+    static void copyData(Handle<SharedArrayBufferObject*> toBuffer,
+                         Handle<SharedArrayBufferObject*> fromBuffer,
+                         uint32_t fromIndex, uint32_t count);
+
     SharedArrayRawBuffer* rawBufferObject() const;
 
     // Invariant: This method does not cause GC and can be called
     // without anchoring the object it is called on.
     uintptr_t globalID() const {
         // The buffer address is good enough as an ID provided the memory is not shared
         // between processes or, if it is, it is mapped to the same address in every
         // process.  (At the moment, shared memory cannot be shared between processes.)