Bug 1021379 - Renamed typed arrays' move method to copyWithin, fixed up to ES6 semantics. r=till, r=jwalden
authorRishab Arora <ra.rishab@gmail.com>
Thu, 10 Jul 2014 11:49:22 +0530
changeset 201038 712980bf7962
parent 201037 af3358a8005d
child 201039 9f2e00997d30
push id48077
push userjwalden@mit.edu
push dateFri, 22 Aug 2014 17:08:31 +0000
treeherdermozilla-inbound@9f2e00997d30 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, jwalden
bugs1021379
milestone34.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 1021379 - Renamed typed arrays' move method to copyWithin, fixed up to ES6 semantics. r=till, r=jwalden
js/src/jit-test/tests/basic/typed-array-copyWithin.js
js/src/tests/js1_8_5/extensions/typedarray.js
js/src/vm/TypedArrayObject.cpp
js/xpconnect/tests/chrome/test_xrayToJS.xul
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/typed-array-copyWithin.js
@@ -0,0 +1,189 @@
+// Bug 1021379 - Rename typed arrays' move method to copyWithin,
+// fix up to ES6 semantics
+// Tests for TypedArray#copyWithin
+
+load(libdir + "asserts.js");
+
+const constructors = [
+  Int8Array,
+  Uint8Array,
+  Uint8ClampedArray,
+  Int16Array,
+  Uint16Array,
+  Int32Array,
+  Uint32Array,
+  Float32Array,
+  Float64Array
+];
+
+for (var constructor of constructors) {
+
+    assertEq(constructor.prototype.copyWithin.length, 2);
+
+    // works with two arguments
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3),
+                 new constructor([4, 5, 3, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(1, 3),
+                 new constructor([1, 4, 5, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(1, 2),
+                 new constructor([1, 3, 4, 5, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(2, 2),
+                 new constructor([1, 2, 3, 4, 5]));
+
+    // works with three arguments
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, 4),
+                 new constructor([4, 2, 3, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(1, 3, 4),
+                 new constructor([1, 4, 3, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(1, 2, 4),
+                 new constructor([1, 3, 4, 4, 5]));
+
+    // works with negative arguments
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, -2),
+                 new constructor([4, 5, 3, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, -2, -1),
+                 new constructor([4, 2, 3, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(-4, -3, -2),
+                 new constructor([1, 3, 3, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(-4, -3, -1),
+                 new constructor([1, 3, 4, 4, 5]));
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(-4, -3),
+                 new constructor([1, 3, 4, 5, 5]));
+
+    // throws on null/undefined values
+    assertThrowsInstanceOf(function() {
+      constructor.prototype.copyWithin.call(null, 0, 3);
+    }, TypeError, "Assert that copyWithin fails if this value is null");
+
+    assertThrowsInstanceOf(function() {
+      var throw42 = { valueOf: function() { throw 42; } };
+      constructor.prototype.copyWithin.call(null, throw42, throw42, throw42);
+    }, TypeError, "Assert that copyWithin fails if this value is null");
+
+    assertThrowsInstanceOf(function() {
+      var throw42 = { valueOf: function() { throw 42; } };
+      constructor.prototype.copyWithin.call(undefined, throw42, throw42, throw42);
+    }, TypeError, "Assert that copyWithin fails if this value is undefined");
+
+    assertThrowsInstanceOf(function() {
+      constructor.prototype.copyWithin.call(undefined, 0, 3);
+    }, TypeError, "Assert that copyWithin fails if this value is undefined");
+
+    // test with this value as string
+    assertThrowsInstanceOf(function() {
+      constructor.prototype.copyWithin.call("hello world", 0, 3);
+    }, TypeError, "Assert that copyWithin fails if this value is string");
+
+    assertThrowsInstanceOf(function() {
+      var throw42 = { valueOf: function() { throw 42; } };
+      constructor.prototype.copyWithin.call("hello world", throw42, throw42, throw42);
+    }, TypeError, "Assert that copyWithin fails if this value is string");
+
+    // test with target > start on 2 arguments
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(3, 0),
+                 new constructor([1, 2, 3, 1, 2]));
+
+    // test with target > start on 3 arguments
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(3, 0, 4),
+                 new constructor([1, 2, 3, 1, 2]));
+
+    // test on fractional arguments
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0.2, 3.9),
+                 new constructor([4, 5, 3, 4, 5]));
+
+    // test with -0
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(-0, 3),
+                 new constructor([4, 5, 3, 4, 5]));
+
+    // test with arguments more than this.length
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, 7),
+                 new constructor([1, 2, 3, 4, 5]));
+
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(7, 0),
+                 new constructor([1, 2, 3, 4, 5]));
+
+    // test with arguments less than -this.length
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(-7, 0),
+                 new constructor([1, 2, 3, 4, 5]));
+
+    // test with arguments equal to -this.length
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(-5, 0),
+                 new constructor([1, 2, 3, 4, 5]));
+
+    // test on empty constructor
+    assertDeepEq(new constructor([]).copyWithin(0, 3),
+                 new constructor([]));
+
+    // test with target range being shorter than end - start
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(2, 1, 4),
+                 new constructor([1, 2, 2, 3, 4]));
+
+    // test overlapping ranges
+    arr = new constructor([1, 2, 3, 4, 5]);
+    arr.copyWithin(2, 1, 4);
+    assertDeepEq(arr.copyWithin(2, 1, 4),
+                 new constructor([1, 2, 2, 2, 3]));
+
+    // undefined as third argument
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, undefined),
+                 new constructor([4, 5, 3, 4, 5]));
+
+    // test that this.length is never called
+    arr = new constructor([0, 1, 2, 3, 5]);
+    Object.defineProperty(arr, "length", {
+      get: function () { throw new Error("length accessor called"); }
+    });
+    arr.copyWithin(1, 3);
+
+    var large = 10000;
+
+    // test on a large constructor
+    arr = new constructor(large);
+    assertDeepEq(arr.copyWithin(45, 900), arr);
+
+    // test on floating point numbers
+    for (var i = 0; i < large; i++) {
+      arr[i] = Math.random();
+    }
+    arr.copyWithin(45, 900);
+
+    // test on constructor of objects
+    for (var i = 0; i < large; i++) {
+      arr[i] = { num: Math.random() };
+    }
+    arr.copyWithin(45, 900);
+
+    // test constructor length remains same
+    assertEq(arr.length, large);
+
+    // test null on third argument is handled correctly
+    assertDeepEq(new constructor([1, 2, 3, 4, 5]).copyWithin(0, 3, null),
+                 new constructor([1, 2, 3, 4, 5]));
+
+    //Any argument referenced, but not provided, has the value |undefined|
+    var tarray = new constructor(8);
+    try
+    {
+      tarray.copyWithin({ valueOf: function() { throw 42; } });
+      throw new Error("expected to throw");
+    }
+    catch (e)
+    {
+      assertEq(e, 42, "should have failed converting target to index");
+    }
+
+    /* // fails, unclear whether it should, disabling for now
+    // test with a proxy object
+    var handler = {
+      get: function(recipient, name) {
+          return recipient[name] + 2;
+      }
+    };
+
+    var p = new Proxy(new constructor([1, 2, 3, 4, 5]), handler);
+
+    assertThrowsInstanceOf(function() {
+      constructor.prototype.copyWithin.call(p, 0, 3);
+    }, TypeError, "Assert that copyWithin fails if used with a proxy object");
+    */
+}
--- a/js/src/tests/js1_8_5/extensions/typedarray.js
+++ b/js/src/tests/js1_8_5/extensions/typedarray.js
@@ -576,61 +576,61 @@ function test()
     // pulled into the ES spec, so for now this is disallowed.
     if (false) {
         check(function () simple.byteLength == 12);
         getter = Object.getOwnPropertyDescriptor(Int8Array.prototype, 'byteLength').get;
         Object.defineProperty(Int8Array.prototype, 'byteLength', { get: function () { return 1 + getter.apply(this) } });
         check(function () simple.byteLength == 13);
     }
 
-    // test move()
+    // test copyWithin()
     var numbers = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ];
 
     function tastring(tarray) {
         return [ x for (x of tarray) ].toString();
     }
 
-    function checkMove(offset, start, end, dest, want) {
+    function checkCopyWithin(offset, start, end, dest, want) {
         var numbers_buffer = new Uint8Array(numbers).buffer;
         var view = new Int8Array(numbers_buffer, offset);
-        view.move(start, end, dest);
+        view.copyWithin(dest, start, end);
         check(function () tastring(view) == want.toString());
         if (tastring(view) != want.toString()) {
             print("Wanted: " + want.toString());
             print("Got   : " + tastring(view));
         }
     }
 
-    // basic move [2,5) -> 4
-    checkMove(0, 2, 5, 4, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
+    // basic copyWithin [2,5) -> 4
+    checkCopyWithin(0, 2, 5, 4, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
 
     // negative values should count from end
-    checkMove(0, -7,  5,  4, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
-    checkMove(0,  2, -4,  4, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
-    checkMove(0,  2,  5, -5, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
-    checkMove(0, -7, -4, -5, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
+    checkCopyWithin(0, -7,  5,  4, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
+    checkCopyWithin(0,  2, -4,  4, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
+    checkCopyWithin(0,  2,  5, -5, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
+    checkCopyWithin(0, -7, -4, -5, [ 0, 1, 2, 3, 2, 3, 4, 7, 8 ]);
 
     // offset
-    checkMove(2, 0, 3, 4, [ 2, 3, 4, 5, 2, 3, 4 ]);
+    checkCopyWithin(2, 0, 3, 4, [ 2, 3, 4, 5, 2, 3, 4 ]);
 
     // clipping
-    checkMove(0,  5000,  6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(0, -5000, -6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(0, -5000,  6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(0,  5000,  6000, 1, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(0, -5000, -6000, 1, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(0,  5000,  6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(2, -5000, -6000, 0, [ 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(2, -5000,  6000, 0, [ 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(2,  5000,  6000, 1, [ 2, 3, 4, 5, 6, 7, 8 ]);
-    checkMove(2, -5000, -6000, 1, [ 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(0,  5000,  6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(0, -5000, -6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(0, -5000,  6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(0,  5000,  6000, 1, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(0, -5000, -6000, 1, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(0,  5000,  6000, 0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(2, -5000, -6000, 0, [ 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(2, -5000,  6000, 0, [ 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(2,  5000,  6000, 1, [ 2, 3, 4, 5, 6, 7, 8 ]);
+    checkCopyWithin(2, -5000, -6000, 1, [ 2, 3, 4, 5, 6, 7, 8 ]);
 
-    checkMove(2, -5000,    3, 1,     [ 2, 2, 3, 4, 6, 7, 8 ]);
-    checkMove(2,     1, 6000, 0,     [ 3, 4, 5, 6, 7, 8, 8 ]);
-    checkMove(2,     1, 6000, -4000, [ 3, 4, 5, 6, 7, 8, 8 ]);
+    checkCopyWithin(2, -5000,    3, 1,     [ 2, 2, 3, 4, 6, 7, 8 ]);
+    checkCopyWithin(2,     1, 6000, 0,     [ 3, 4, 5, 6, 7, 8, 8 ]);
+    checkCopyWithin(2,     1, 6000, -4000, [ 3, 4, 5, 6, 7, 8, 8 ]);
 
     testBufferManagement();
 
     print ("done");
 
     reportCompare(0, TestFailCount, "typed array tests");
 
     exitFunc ('test');
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -413,18 +413,17 @@ class TypedArrayObjectTemplate : public 
                 if (length < 0) {
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
                                          JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2");
                     return nullptr;
                 }
             }
         }
 
-        Rooted<JSObject*> proto(cx, nullptr);
-        return fromBuffer(cx, dataObj, byteOffset, length, proto);
+        return fromBuffer(cx, dataObj, byteOffset, length);
     }
 
     static bool IsThisClass(HandleValue v) {
         return v.isObject() && v.toObject().hasClass(instanceClass());
     }
 
     template<Value ValueGetter(TypedArrayObject *tarr)>
     static bool
@@ -520,83 +519,106 @@ class TypedArrayObjectTemplate : public 
     static bool
     fun_subarray(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
                                     ThisTypedArrayObject::fun_subarray_impl>(cx, args);
     }
 
-    /* move(begin, end, dest) */
+    /* copyWithin(target, start[, end]) */
+    // ES6 draft rev 26, 22.2.3.5
     static bool
-    fun_move_impl(JSContext *cx, CallArgs args)
+    fun_copyWithin_impl(JSContext *cx, CallArgs args)
     {
-        JS_ASSERT(IsThisClass(args.thisv()));
-        Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
-
-        if (args.length() < 3) {
+        MOZ_ASSERT(IsThisClass(args.thisv()));
+
+        // Steps 1-2.
+        Rooted<TypedArrayObject*> obj(cx, &args.thisv().toObject().as<TypedArrayObject>());
+
+        // Steps 3-4.
+        uint32_t len = obj->length();
+
+        // Steps 6-8.
+        uint32_t to;
+        if (!ToClampedIndex(cx, args.get(0), len, &to))
+            return false;
+
+        // Steps 9-11.
+        uint32_t from;
+        if (!ToClampedIndex(cx, args.get(1), len, &from))
+            return false;
+
+        // Steps 12-14.
+        uint32_t final;
+        if (args.get(2).isUndefined()) {
+            final = len;
+        } else {
+            if (!ToClampedIndex(cx, args.get(2), len, &final))
+                return false;
+        }
+
+        // Steps 15-18.
+
+        // If |final - from < 0|, then |count| will be less than 0, so step 18
+        // never loops.  Exit early so |count| can use a non-negative type.
+        // Also exit early if elements are being moved to their pre-existing
+        // location.
+        if (final < from || to == from) {
+            args.rval().setObject(*obj);
+            return true;
+        }
+
+        uint32_t count = Min(final - from, len - to);
+        uint32_t lengthDuringMove = obj->length(); // beware ToClampedIndex
+
+        MOZ_ASSERT(to <= INT32_MAX, "size limited to 2**31");
+        MOZ_ASSERT(from <= INT32_MAX, "size limited to 2**31");
+        MOZ_ASSERT(count <= INT32_MAX, "size limited to 2**31");
+
+        if (from + count > lengthDuringMove || to + count > lengthDuringMove) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return false;
         }
 
-        uint32_t srcBegin;
-        uint32_t srcEnd;
-        uint32_t dest;
-
-        uint32_t originalLength = tarray->length();
-        if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) ||
-            !ToClampedIndex(cx, args[1], originalLength, &srcEnd) ||
-            !ToClampedIndex(cx, args[2], originalLength, &dest))
-        {
-            return false;
-        }
-
-        if (srcBegin > srcEnd) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
-            return false;
-        }
-
-        uint32_t lengthDuringMove = tarray->length(); // beware ToClampedIndex
-        uint32_t nelts = srcEnd - srcBegin;
-
-        MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31");
-        MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31");
-        if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) {
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return false;
-        }
-
-        uint32_t byteDest = dest * sizeof(NativeType);
-        uint32_t byteSrc = srcBegin * sizeof(NativeType);
-        uint32_t byteSize = nelts * sizeof(NativeType);
+        const size_t ElementSize = sizeof(NativeType);
+        MOZ_ASSERT(to <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
+        MOZ_ASSERT(from <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
+        MOZ_ASSERT(count <= INT32_MAX / ElementSize, "overall byteLength capped at INT32_MAX");
+
+        uint32_t byteDest = to * sizeof(NativeType);
+        uint32_t byteSrc = from * sizeof(NativeType);
+        uint32_t byteSize = count * sizeof(NativeType);
 
 #ifdef DEBUG
-        uint32_t viewByteLength = tarray->byteLength();
+        uint32_t viewByteLength = obj->byteLength();
         JS_ASSERT(byteDest <= viewByteLength);
         JS_ASSERT(byteSrc <= viewByteLength);
         JS_ASSERT(byteDest + byteSize <= viewByteLength);
         JS_ASSERT(byteSrc + byteSize <= viewByteLength);
 
         // Should not overflow because size is limited to 2^31
         JS_ASSERT(byteDest + byteSize >= byteDest);
         JS_ASSERT(byteSrc + byteSize >= byteSrc);
 #endif
 
-        uint8_t *data = static_cast<uint8_t*>(tarray->viewData());
-        memmove(&data[byteDest], &data[byteSrc], byteSize);
-        args.rval().setUndefined();
+        uint8_t *data = static_cast<uint8_t*>(obj->viewData());
+        mozilla::PodMove(&data[byteDest], &data[byteSrc], byteSize);
+
+        // Step 19.
+        args.rval().set(args.thisv());
         return true;
     }
 
     static bool
-    fun_move(JSContext *cx, unsigned argc, Value *vp)
+    fun_copyWithin(JSContext *cx, unsigned argc, Value *vp)
     {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
-                                    ThisTypedArrayObject::fun_move_impl>(cx, args);
+                                    ThisTypedArrayObject::fun_copyWithin_impl>(cx, args);
     }
 
     /* set(array[, offset]) */
     static bool
     fun_set_impl(JSContext *cx, CallArgs args)
     {
         JS_ASSERT(IsThisClass(args.thisv()));
         Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>());
@@ -657,18 +679,24 @@ class TypedArrayObjectTemplate : public 
     {
         CallArgs args = CallArgsFromVp(argc, vp);
         return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass,
                                     ThisTypedArrayObject::fun_set_impl>(cx, args);
     }
 
   public:
     static JSObject *
-    fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
-               HandleObject proto)
+    fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
+        RootedObject proto(cx, nullptr);
+        return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, proto);
+    }
+
+    static JSObject *
+    fromBufferWithProto(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
+                        HandleObject proto)
     {
         if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
             return nullptr; // must be arrayBuffer
         }
 
         JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
         if (bufobj->is<ProxyObject>()) {
@@ -1206,17 +1234,18 @@ ArrayBufferObject::createTypedArrayFromB
     Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
     Rooted<JSObject*> proto(cx, &args[2].toObject());
 
     Rooted<JSObject*> obj(cx);
     double byteOffset = args[0].toNumber();
     MOZ_ASSERT(0 <= byteOffset);
     MOZ_ASSERT(byteOffset <= UINT32_MAX);
     MOZ_ASSERT(byteOffset == uint32_t(byteOffset));
-    obj = ArrayType::fromBuffer(cx, buffer, uint32_t(byteOffset), args[1].toInt32(), proto);
+    obj = ArrayType::fromBufferWithProto(cx, buffer, uint32_t(byteOffset), args[1].toInt32(),
+                                         proto);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 template<typename T>
 bool
@@ -2028,28 +2057,22 @@ TypedArrayObject::setElement(TypedArrayO
 /***
  *** JS impl
  ***/
 
 /*
  * TypedArrayObject boilerplate
  */
 
-#ifndef RELEASE_BUILD
-# define EXPERIMENTAL_FUNCTIONS(_t) JS_FN("move", _t##Object::fun_move, 3, JSFUN_GENERIC_NATIVE),
-#else
-# define EXPERIMENTAL_FUNCTIONS(_t)
-#endif
-
 #define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                      \
 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
     JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
-    EXPERIMENTAL_FUNCTIONS(_typedArray)                                            \
+    JS_FN("copyWithin", _typedArray##Object::fun_copyWithin, 2, JSFUN_GENERIC_NATIVE), \
     JS_FS_END                                                                      \
 };                                                                                 \
 /* These next 3 functions are brought to you by the buggy GCC we use to build      \
    B2G ICS. Older GCC versions have a bug in which they fail to compile            \
    reinterpret_casts of templated functions with the message: "insufficient        \
    contextual information to determine type". JS_PSG needs to                      \
    reinterpret_cast<JSPropertyOp>, so this causes problems for us here.            \
                                                                                    \
@@ -2083,17 +2106,17 @@ const JSPropertySpec _typedArray##Object
   JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject other) \
   {                                                                                             \
       return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other);                        \
   }                                                                                             \
   JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx,                    \
                                HandleObject arrayBuffer, uint32_t byteOffset, int32_t length)   \
   {                                                                                             \
       return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset,      \
-                                                              length, js::NullPtr());           \
+                                                              length);                          \
   }                                                                                             \
   JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj)                                     \
   {                                                                                             \
       if (!(obj = CheckedUnwrap(obj)))                                                          \
           return false;                                                                         \
       const Class *clasp = obj->getClass();                                                     \
       return clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]; \
   } \
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -176,19 +176,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
       "findIndex", "copyWithin", "fill", "@@iterator", "entries", "keys", "constructor"];
   if (isNightlyBuild) {
     let pjsMethods = ['mapPar', 'reducePar', 'scanPar', 'scatterPar', 'filterPar'];
     gPrototypeProperties['Array'] = gPrototypeProperties['Array'].concat(pjsMethods);
   }
   for (var c of typedArrayClasses) {
     gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT", "length", "buffer",
-                               "byteLength", "byteOffset", "@@iterator", "subarray", "set"];
-    if (!isReleaseBuild)
-      gPrototypeProperties[c].push("move");
+                               "byteLength", "byteOffset", "@@iterator", "subarray", "set",
+                               "copyWithin"];
   }
   for (var c of errorObjectClasses) {
       gPrototypeProperties[c] = ["constructor", "name",
                                  // We don't actually resolve these empty data properties
                                  // onto the Xray prototypes, but we list them here to make
                                  // the test happy.
                                  "lineNumber", "columnNumber", "fileName", "message", "stack"];
   }