Bug 1140152 - Copy elements' underlying bit patterns in TypedArray.prototype.slice. r=till
authorAndré Bargull <andre.bargull@gmail.com>
Wed, 02 May 2018 09:52:05 -0700
changeset 472859 ecd12b33bb11877c2248df44b4872706f7221dda
parent 472858 b6836abf3cb00990b47d49fd36fce289dee65264
child 472860 afbec9a6802cb45f5032301b997c15f7d1c25884
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1140152
milestone61.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 1140152 - Copy elements' underlying bit patterns in TypedArray.prototype.slice. r=till
js/src/builtin/TypedArray.js
js/src/tests/jstests.list
js/src/tests/non262/TypedArray/slice-bitwise.js
js/src/tests/non262/TypedArray/slice-conversion.js
js/src/tests/non262/TypedArray/slice-memcpy.js
js/src/vm/SelfHosting.cpp
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1020,40 +1020,42 @@ function TypedArraySlice(start, end) {
                 : std_Math_min(relativeEnd, len);
 
     // Step 8.
     var count = std_Math_max(final - k, 0);
 
     // Step 9.
     var A = TypedArraySpeciesCreateWithLength(O, count);
 
-    // Steps 10-13 (Not implemented, bug 1140152).
-
     // Steps 14-15.
     if (count > 0) {
-        // Step 14.a.
-        var n = 0;
-
         // Steps 14.b.ii, 15.b.
         if (buffer === null) {
             // A typed array previously using inline storage may acquire a
             // buffer, so we must check with the source.
             buffer = ViewedArrayBufferIfReified(O);
         }
 
         if (IsDetachedBuffer(buffer))
             ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
 
-        // Step 14.b.
-        while (k < final) {
-            // Steps 14.b.i-v.
-            A[n++] = O[k++];
+        // Steps 10-13, 15.
+        var sliced = TypedArrayBitwiseSlice(O, A, k | 0, count | 0);
+
+        // Step 14.
+        if (!sliced) {
+            // Step 14.a.
+            var n = 0;
+
+            // Step 14.b.
+            while (k < final) {
+                // Steps 14.b.i-v.
+                A[n++] = O[k++];
+            }
         }
-
-        // FIXME: Implement step 15 (bug 1140152).
     }
 
     // Step 16.
     return A;
 }
 
 // ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
 function TypedArraySome(callbackfn/*, thisArg*/) {
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -140,19 +140,16 @@ skip script test262/built-ins/TypedArray
 skip script test262/built-ins/TypedArrayConstructors/internals/HasProperty/key-is-not-canonical-index.js
 skip script test262/built-ins/TypedArrayConstructors/internals/HasProperty/key-is-not-integer.js
 skip script test262/built-ins/TypedArrayConstructors/internals/Set/key-is-minus-zero.js
 skip script test262/built-ins/TypedArrayConstructors/internals/Set/key-is-not-canonical-index.js
 skip script test262/built-ins/TypedArrayConstructors/internals/Set/key-is-not-integer.js
 skip script test262/built-ins/TypedArrayConstructors/internals/Set/key-is-out-of-bounds.js
 skip script test262/built-ins/TypedArrayConstructors/internals/Set/tonumber-value-throws.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1140152
-skip script test262/built-ins/TypedArray/prototype/slice/bit-precision.js
-
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1317405
 skip script test262/language/computed-property-names/class/static/method-number.js
 skip script test262/language/computed-property-names/class/static/method-string.js
 skip script test262/language/computed-property-names/class/static/method-symbol.js
 
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1286997
 # Bug 1286997 probably doesn't cover all spec violations.
 skip script test262/language/expressions/assignment/S11.13.1_A5_T5.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/TypedArray/slice-bitwise.js
@@ -0,0 +1,153 @@
+// Copies bytes bit-wise if source and target type are the same.
+// Only detectable when using floating point typed arrays.
+const float32Constructors = anyTypedArrayConstructors.filter(isFloatConstructor)
+                                                     .filter(c => c.BYTES_PER_ELEMENT === 4);
+const float64Constructors = anyTypedArrayConstructors.filter(isFloatConstructor)
+                                                     .filter(c => c.BYTES_PER_ELEMENT === 8);
+
+// Also test with cross-compartment typed arrays.
+const otherGlobal = newGlobal();
+float32Constructors.push(otherGlobal.Float32Array);
+float64Constructors.push(otherGlobal.Float64Array);
+
+function* p(xs, ys) {
+    for (let x of xs) {
+        for (let y of ys) {
+            yield [x, y];
+        }
+    }
+}
+
+const isLittleEndian = new Uint8Array(new Uint16Array([1]).buffer)[0] !== 0;
+
+function geti64(i32, i) {
+    return [i32[2 * i + isLittleEndian], i32[2 * i + !isLittleEndian]];
+}
+
+function seti64(i32, i, [hi, lo]) {
+    i32[i * 2 + isLittleEndian] = hi;
+    i32[i * 2 + !isLittleEndian] = lo;
+}
+
+const NaNs = {
+    Float32: [
+        0x7F800001|0, // smallest SNaN
+        0x7FBFFFFF|0, // largest SNaN
+        0x7FC00000|0, // smallest QNaN
+        0x7FFFFFFF|0, // largest QNaN
+        0xFF800001|0, // smallest SNaN, sign-bit set
+        0xFFBFFFFF|0, // largest SNaN, sign-bit set
+        0xFFC00000|0, // smallest QNaN, sign-bit set
+        0xFFFFFFFF|0, // largest QNaN, sign-bit set
+    ],
+    Float64: [
+        [0x7FF00000|0, 0x00000001|0], // smallest SNaN
+        [0x7FF7FFFF|0, 0xFFFFFFFF|0], // largest SNaN
+        [0x7FF80000|0, 0x00000000|0], // smallest QNaN
+        [0x7FFFFFFF|0, 0xFFFFFFFF|0], // largest QNaN
+        [0xFFF00000|0, 0x00000001|0], // smallest SNaN, sign-bit set
+        [0xFFF7FFFF|0, 0xFFFFFFFF|0], // largest SNaN, sign-bit set
+        [0xFFF80000|0, 0x00000000|0], // smallest QNaN, sign-bit set
+        [0xFFFFFFFF|0, 0xFFFFFFFF|0], // largest QNaN, sign-bit set
+    ],
+};
+
+const cNaN = {
+    Float32: new Int32Array(new Float32Array([NaN]).buffer)[0],
+    Float64: geti64(new Int32Array(new Float64Array([NaN]).buffer), 0),
+};
+
+// Float32 -> Float32
+for (let [sourceConstructor, targetConstructor] of p(float32Constructors, float32Constructors)) {
+    let len = NaNs.Float32.length;
+    let f32 = new sourceConstructor(len);
+    let i32 = new Int32Array(f32.buffer);
+    f32.constructor = targetConstructor;
+
+    for (let i = 0; i < len; ++i) {
+        i32[i] = NaNs.Float32[i];
+    }
+
+    let rf32 = f32.slice(0);
+    let ri32 = new Int32Array(rf32.buffer);
+
+    assertEq(rf32.length, len);
+    assertEq(ri32.length, len);
+
+    // Same bits.
+    for (let i = 0; i < len; ++i) {
+        assertEq(ri32[i], NaNs.Float32[i]);
+    }
+}
+
+// Float32 -> Float64
+for (let [sourceConstructor, targetConstructor] of p(float32Constructors, float64Constructors)) {
+    let len = NaNs.Float32.length;
+    let f32 = new sourceConstructor(len);
+    let i32 = new Int32Array(f32.buffer);
+    f32.constructor = targetConstructor;
+
+    for (let i = 0; i < len; ++i) {
+        i32[i] = NaNs.Float32[i];
+    }
+
+    let rf64 = f32.slice(0);
+    let ri32 = new Int32Array(rf64.buffer);
+
+    assertEq(rf64.length, len);
+    assertEq(ri32.length, 2 * len);
+
+    // NaN bits canonicalized.
+    for (let i = 0; i < len; ++i) {
+        assertEqArray(geti64(ri32, i), cNaN.Float64);
+    }
+}
+
+// Float64 -> Float64
+for (let [sourceConstructor, targetConstructor] of p(float64Constructors, float64Constructors)) {
+    let len = NaNs.Float64.length;
+    let f64 = new sourceConstructor(len);
+    let i32 = new Int32Array(f64.buffer);
+    f64.constructor = targetConstructor;
+
+    for (let i = 0; i < len; ++i) {
+        seti64(i32, i, NaNs.Float64[i]);
+    }
+
+    let rf64 = f64.slice(0);
+    let ri32 = new Int32Array(rf64.buffer);
+
+    assertEq(rf64.length, len);
+    assertEq(ri32.length, 2 * len);
+
+    // Same bits.
+    for (let i = 0; i < len; ++i) {
+        assertEqArray(geti64(ri32, i), NaNs.Float64[i]);
+    }
+}
+
+// Float64 -> Float32
+for (let [sourceConstructor, targetConstructor] of p(float64Constructors, float32Constructors)) {
+    let len = NaNs.Float64.length;
+    let f64 = new sourceConstructor(len);
+    let i32 = new Int32Array(f64.buffer);
+    f64.constructor = targetConstructor;
+
+    for (let i = 0; i < len; ++i) {
+        seti64(i32, i, NaNs.Float64[i]);
+    }
+
+    let rf32 = f64.slice(0);
+    let ri32 = new Int32Array(rf32.buffer);
+
+    assertEq(rf32.length, len);
+    assertEq(ri32.length, len);
+
+    // NaN bits canonicalized.
+    for (let i = 0; i < len; ++i) {
+        assertEqArray(ri32[i], cNaN.Float32);
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/TypedArray/slice-conversion.js
@@ -0,0 +1,515 @@
+// Test conversions to different element types.
+let tests = [
+    /* Int8Array */
+    {
+        from: Int8Array,
+        to: Int8Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, -128, -127, -1, 0, 1, 127, -128, -127],
+    },
+    {
+        from: Int8Array,
+        to: Uint8Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, 128, 129, 255, 0, 1, 127, 128, 129],
+    },
+    {
+        from: Int8Array,
+        to: Uint8ClampedArray,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, 0, 0, 0, 0, 1, 127, 0, 0],
+    },
+    {
+        from: Int8Array,
+        to: Int16Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, -128, -127, -1, 0, 1, 127, -128, -127],
+    },
+    {
+        from: Int8Array,
+        to: Uint16Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, 65408, 65409, 65535, 0, 1, 127, 65408, 65409],
+    },
+    {
+        from: Int8Array,
+        to: Int32Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, -128, -127, -1, 0, 1, 127, -128, -127],
+    },
+    {
+        from: Int8Array,
+        to: Uint32Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, 4294967168, 4294967169, 4294967295, 0, 1, 127, 4294967168, 4294967169],
+    },
+    {
+        from: Int8Array,
+        to: Float32Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, -128, -127, -1, 0, 1, 127, -128, -127],
+    },
+    {
+        from: Int8Array,
+        to: Float64Array,
+        values: [-129, -128, -127, -1, 0, 1, 127, 128, 129],
+        expected: [127, -128, -127, -1, 0, 1, 127, -128, -127],
+    },
+
+    /* Uint8Array */
+    {
+        from: Uint8Array,
+        to: Int8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, -128, -127, -2, -1, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Uint8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Uint8ClampedArray,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Int16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Uint16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Int32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Uint32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Float32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+    {
+        from: Uint8Array,
+        to: Float64Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0],
+    },
+
+    /* Uint8ClampedArray */
+    {
+        from: Uint8ClampedArray,
+        to: Int8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, -128, -127, -2, -1, -1],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Uint8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Uint8ClampedArray,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Int16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Uint16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Int32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Uint32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Float32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+    {
+        from: Uint8ClampedArray,
+        to: Float64Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255],
+    },
+
+    /* Int16Array */
+    {
+        from: Int16Array,
+        to: Int8Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [-1, 0, 1, 127, -128, -127, -1, 0, 1, 127, -128, -127, -1, 0, 1],
+    },
+    {
+        from: Int16Array,
+        to: Uint8Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [255, 0, 1, 127, 128, 129, 255, 0, 1, 127, 128, 129, 255, 0, 1],
+    },
+    {
+        from: Int16Array,
+        to: Uint8ClampedArray,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [255, 0, 0, 0, 0, 0, 0, 0, 1, 127, 128, 129, 255, 0, 0],
+    },
+    {
+        from: Int16Array,
+        to: Int16Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767],
+    },
+    {
+        from: Int16Array,
+        to: Uint16Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [32767, 32768, 32769, 65407, 65408, 65409, 65535, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+    },
+    {
+        from: Int16Array,
+        to: Int32Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767],
+    },
+    {
+        from: Int16Array,
+        to: Uint32Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [32767, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 0, 1, 127, 128, 129, 32767, 4294934528, 4294934529],
+    },
+    {
+        from: Int16Array,
+        to: Float32Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767],
+    },
+    {
+        from: Int16Array,
+        to: Float64Array,
+        values: [-32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769],
+        expected: [32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767],
+    },
+
+    /* Uint16Array */
+    {
+        from: Uint16Array,
+        to: Int8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, -128, -127, -2, -1, 0, -1, 0, 1, -2, -1, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Uint8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0, 255, 0, 1, 254, 255, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Uint8ClampedArray,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255, 255, 255, 255, 255, 255, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Int16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, -32768, -32767, -2, -1, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Uint16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Int32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Uint32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Float32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0],
+    },
+    {
+        from: Uint16Array,
+        to: Float64Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0],
+    },
+
+    /* Int32Array */
+    {
+        from: Int32Array,
+        to: Int8Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [-1, 0, 1, -1, 0, 1, 127, -128, -127, -1, 0, 1, 127, -128, -127, -1, 0, 1, -1, 0, 1],
+    },
+    {
+        from: Int32Array,
+        to: Uint8Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [255, 0, 1, 255, 0, 1, 127, 128, 129, 255, 0, 1, 127, 128, 129, 255, 0, 1, 255, 0, 1],
+    },
+    {
+        from: Int32Array,
+        to: Uint8ClampedArray,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 127, 128, 129, 255, 255, 255, 255, 0, 0],
+    },
+    {
+        from: Int32Array,
+        to: Int16Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [-1, 0, 1, 32767, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, -32768, -32767, -1, 0, 1],
+    },
+    {
+        from: Int32Array,
+        to: Uint16Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [65535, 0, 1, 32767, 32768, 32769, 65407, 65408, 65409, 65535, 0, 1, 127, 128, 129, 32767, 32768, 32769, 65535, 0, 1],
+    },
+    {
+        from: Int32Array,
+        to: Int32Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [2147483647, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, -2147483648, -2147483647],
+    },
+    {
+        from: Int32Array,
+        to: Uint32Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [2147483647, 2147483648, 2147483649, 4294934527, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+    },
+    {
+        from: Int32Array,
+        to: Float32Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483648, -2147483648, -2147483648],
+    },
+    {
+        from: Int32Array,
+        to: Float64Array,
+        values: [-2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649],
+        expected: [2147483647, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, 0, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, -2147483648, -2147483647],
+    },
+
+    /* Uint32Array */
+    {
+        from: Uint32Array,
+        to: Int8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, -128, -127, -2, -1, 0, -1, 0, 1, -2, -1, 0, -1, 0, 1, -2, -1, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Uint8Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 0, 255, 0, 1, 254, 255, 0, 255, 0, 1, 254, 255, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Uint8ClampedArray,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Int16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, -32768, -32767, -2, -1, 0, -1, 0, 1, -2, -1, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Uint16Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 0, 65535, 0, 1, 65534, 65535, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Int32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, -2147483648, -2147483647, -2, -1, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Uint32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Float32Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483648, 2147483648, 2147483648, 4294967296, 4294967296, 0],
+    },
+    {
+        from: Uint32Array,
+        to: Float64Array,
+        values: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 4294967296],
+        expected: [0, 1, 127, 128, 129, 254, 255, 256, 32767, 32768, 32769, 65534, 65535, 65536, 2147483647, 2147483648, 2147483649, 4294967294, 4294967295, 0],
+    },
+
+    /* Float32Array */
+    {
+        from: Float32Array,
+        to: Int8Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 0, 0, 0, -1, 0, 1, 127, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, -128, -127, -1, 0, 1, 0, 0, 0, 0, 0],
+    },
+    {
+        from: Float32Array,
+        to: Uint8Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 0, 0, 0, 255, 0, 1, 127, 128, 129, 255, 255, 255, 255, 0, 0, 1, 1, 1, 1, 127, 128, 129, 255, 0, 1, 0, 0, 0, 0, 0],
+    },
+    {
+        from: Float32Array,
+        to: Uint8ClampedArray,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 127, 128, 129, 255, 255, 255, 255, 255, 255, 255, 0],
+    },
+    {
+        from: Float32Array,
+        to: Int16Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 0, 0, 0, 32767, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, -32768, -32767, 0, 0, 0, 0, 0],
+    },
+    {
+        from: Float32Array,
+        to: Uint16Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 0, 0, 0, 32767, 32768, 32769, 65407, 65408, 65409, 65535, 65535, 65535, 65535, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 0, 0, 0, 0, 0],
+    },
+    {
+        from: Float32Array,
+        to: Int32Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, -2147483648, -2147483648, -2147483648, 0, 0],
+    },
+    {
+        from: Float32Array,
+        to: Uint32Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 2147483648, 2147483648, 2147483648, 4294934527, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 4294967295, 4294967295, 4294967295, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, 0, 0],
+    },
+    {
+        from: Float32Array,
+        to: Float32Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [-Infinity, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1.600000023841858, -1.5, -1.399999976158142, -1, -0, 0, 1, 1.399999976158142, 1.5, 1.600000023841858, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, Infinity, NaN],
+    },
+    {
+        from: Float32Array,
+        to: Float64Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [-Infinity, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1.600000023841858, -1.5, -1.399999976158142, -1, -0, 0, 1, 1.399999976158142, 1.5, 1.600000023841858, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, Infinity, NaN],
+    },
+
+    /* Float64Array */
+    {
+        from: Float64Array,
+        to: Int8Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, -1, 0, 1, -1, 0, 1, 127, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, -128, -127, -1, 0, 1, -1, 0, 1, 0, 0],
+    },
+    {
+        from: Float64Array,
+        to: Uint8Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 255, 0, 1, 255, 0, 1, 127, 128, 129, 255, 255, 255, 255, 0, 0, 1, 1, 1, 1, 127, 128, 129, 255, 0, 1, 255, 0, 1, 0, 0],
+    },
+    {
+        from: Float64Array,
+        to: Uint8ClampedArray,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 127, 128, 129, 255, 255, 255, 255, 255, 255, 255, 0],
+    },
+    {
+        from: Float64Array,
+        to: Int16Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, -1, 0, 1, 32767, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, -32768, -32767, -1, 0, 1, 0, 0],
+    },
+    {
+        from: Float64Array,
+        to: Uint16Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 65535, 0, 1, 32767, 32768, 32769, 65407, 65408, 65409, 65535, 65535, 65535, 65535, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 65535, 0, 1, 0, 0],
+    },
+    {
+        from: Float64Array,
+        to: Int32Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 2147483647, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1, -1, -1, -1, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, -2147483648, -2147483647, 0, 0],
+    },
+    {
+        from: Float64Array,
+        to: Uint32Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [0, 2147483647, 2147483648, 2147483649, 4294934527, 4294934528, 4294934529, 4294967167, 4294967168, 4294967169, 4294967295, 4294967295, 4294967295, 4294967295, 0, 0, 1, 1, 1, 1, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, 0, 0],
+    },
+    {
+        from: Float64Array,
+        to: Float32Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [-Infinity, -2147483648, -2147483648, -2147483648, -32769, -32768, -32767, -129, -128, -127, -1.600000023841858, -1.5, -1.399999976158142, -1, -0, 0, 1, 1.399999976158142, 1.5, 1.600000023841858, 127, 128, 129, 32767, 32768, 32769, 2147483648, 2147483648, 2147483648, Infinity, NaN],
+    },
+    {
+        from: Float64Array,
+        to: Float64Array,
+        values: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+        expected: [-Infinity, -2147483649, -2147483648, -2147483647, -32769, -32768, -32767, -129, -128, -127, -1.6, -1.5, -1.4, -1, -0, 0, 1, 1.4, 1.5, 1.6, 127, 128, 129, 32767, 32768, 32769, 2147483647, 2147483648, 2147483649, Infinity, NaN],
+    },
+];
+
+for (let {from, to, values, expected} of tests) {
+    let ta = new from(values);
+    ta.constructor = to;
+    assertEqArray(ta.slice(0), expected, `${from.name} -> ${to.name}`);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/non262/TypedArray/slice-memcpy.js
@@ -0,0 +1,84 @@
+const otherGlobal = newGlobal();
+
+// Create with new ArrayBuffer and offset.
+for (var constructor of typedArrayConstructors) {
+    const elementSize = constructor.BYTES_PER_ELEMENT;
+    let ab = new constructor(10).map((v, i) => i).buffer;
+    let ta = new constructor(ab, 2 * elementSize, 6);
+    ta.constructor = {
+        [Symbol.species]: function(len) {
+            return new constructor(new ArrayBuffer((len + 4) * elementSize), 4 * elementSize);
+        }
+    };
+    let tb = ta.slice(0);
+
+    assertEqArray(ta, [2, 3, 4, 5, 6, 7]);
+    assertEqArray(tb, [2, 3, 4, 5, 6, 7]);
+}
+
+// Source and target arrays use the same ArrayBuffer, target range before source range.
+for (var constructor of typedArrayConstructors) {
+    const elementSize = constructor.BYTES_PER_ELEMENT;
+    let ab = new constructor(10).map((v, i) => i).buffer;
+    let ta = new constructor(ab, 2 * elementSize, 6);
+    ta.constructor = {
+        [Symbol.species]: function(len) {
+            return new constructor(ab, 0, len);
+        }
+    };
+    let tb = ta.slice(0);
+
+    assertEqArray(ta, [4, 5, 6, 7, 6, 7]);
+    assertEqArray(tb, [2, 3, 4, 5, 6, 7]);
+    assertEqArray(new constructor(ab), [2, 3, 4, 5, 6, 7, 6, 7, 8, 9]);
+}
+
+// Source and target arrays use the same ArrayBuffer, target range after source range.
+for (var constructor of typedArrayConstructors) {
+    const elementSize = constructor.BYTES_PER_ELEMENT;
+    let ab = new constructor(10).map((v, i) => i + 1).buffer;
+    let ta = new constructor(ab, 0, 6);
+    ta.constructor = {
+        [Symbol.species]: function(len) {
+            return new constructor(ab, 2 * elementSize, len);
+        }
+    };
+    let tb = ta.slice(0);
+
+    assertEqArray(ta, [1, 2, 1, 2, 1, 2]);
+    assertEqArray(tb, [1, 2, 1, 2, 1, 2]);
+    assertEqArray(new constructor(ab), [1, 2, 1, 2, 1, 2, 1, 2, 9, 10]);
+}
+
+// Tricky: Same as above, but with SharedArrayBuffer and different compartments.
+if (typeof setSharedArrayBuffer === "function") {
+    for (var constructor of typedArrayConstructors) {
+        const elementSize = constructor.BYTES_PER_ELEMENT;
+        let ts = new constructor(new SharedArrayBuffer(10 * elementSize));
+        for (let i = 0; i < ts.length; i++) {
+            ts[i] = i + 1;
+        }
+        let ab = ts.buffer;
+        let ta = new constructor(ab, 0, 6);
+        ta.constructor = {
+            [Symbol.species]: function(len) {
+                setSharedArrayBuffer(ab);
+                try {
+                    return otherGlobal.eval(`
+                        new ${constructor.name}(getSharedArrayBuffer(), ${2 * elementSize}, ${len});
+                    `);
+                } finally {
+                    setSharedArrayBuffer(null);
+                }
+            }
+        };
+        let tb = ta.slice(0);
+
+        assertEqArray(ta, [1, 2, 1, 2, 1, 2]);
+        assertEqArray(tb, [1, 2, 1, 2, 1, 2]);
+        assertEqArray(new constructor(ab), [1, 2, 1, 2, 1, 2, 1, 2, 9, 10]);
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1247,18 +1247,23 @@ intrinsic_MoveTypedArrayElements(JSConte
 // If calling code discipline ever fails to be maintained, it's gonna have a
 // bad time.
 static TypedArrayObject*
 DangerouslyUnwrapTypedArray(JSContext* cx, JSObject* obj)
 {
     // An unwrapped pointer to an object potentially on the other side of a
     // compartment boundary!  Isn't this such fun?
     JSObject* unwrapped = CheckedUnwrap(obj);
+    if (!unwrapped) {
+        ReportAccessDenied(cx);
+        return nullptr;
+    }
+
     if (!unwrapped->is<TypedArrayObject>()) {
-        // By *appearances* this can't happen, as self-hosted TypedArraySet
+        // By *appearances* this can't happen, as self-hosted code
         // checked this.  But.  Who's to say a GC couldn't happen between
         // the check that this value was a typed array, and this extraction
         // occurring?  A GC might turn a cross-compartment wrapper |obj| into
         // |unwrapped == obj|, a dead object no longer connected its typed
         // array.
         //
         // Yeah, yeah, it's pretty unlikely.  Are you willing to stake a
         // sec-critical bug on that assessment, now and forever, against
@@ -1583,16 +1588,123 @@ intrinsic_SetOverlappingTypedElements(JS
 
     CopyToDisjointArray(target, targetOffset, SharedMem<void*>::unshared(copyOfSrcData.get()),
                         unsafeSrcTypeCrossCompartment, count);
 
     args.rval().setUndefined();
     return true;
 }
 
+// The specification requires us to perform bitwise copying when |sourceType|
+// and |targetType| are the same (ES2017, ยง22.2.3.24, step 15). Additionally,
+// as an optimization, we can also perform bitwise copying when |sourceType|
+// and |targetType| have compatible bit-level representations.
+static bool
+IsTypedArrayBitwiseSlice(Scalar::Type sourceType, Scalar::Type targetType)
+{
+    switch (sourceType) {
+      case Scalar::Int8:
+        return targetType == Scalar::Int8 || targetType == Scalar::Uint8;
+
+      case Scalar::Uint8:
+      case Scalar::Uint8Clamped:
+        return targetType == Scalar::Int8 || targetType == Scalar::Uint8 ||
+               targetType == Scalar::Uint8Clamped;
+
+      case Scalar::Int16:
+      case Scalar::Uint16:
+        return targetType == Scalar::Int16 || targetType == Scalar::Uint16;
+
+      case Scalar::Int32:
+      case Scalar::Uint32:
+        return targetType == Scalar::Int32 || targetType == Scalar::Uint32;
+
+      case Scalar::Float32:
+        return targetType == Scalar::Float32;
+
+      case Scalar::Float64:
+        return targetType == Scalar::Float64;
+
+      default:
+        MOZ_CRASH("IsTypedArrayBitwiseSlice with a bogus typed array type");
+    }
+}
+
+static bool
+intrinsic_TypedArrayBitwiseSlice(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 4);
+    MOZ_ASSERT(args[0].isObject());
+    MOZ_ASSERT(args[1].isObject());
+    MOZ_ASSERT(args[2].isInt32());
+    MOZ_ASSERT(args[3].isInt32());
+
+    Rooted<TypedArrayObject*> source(cx, &args[0].toObject().as<TypedArrayObject>());
+    MOZ_ASSERT(!source->hasDetachedBuffer());
+
+    // As directed by |DangerouslyUnwrapTypedArray|, sigil this pointer and all
+    // variables derived from it to counsel extreme caution here.
+    Rooted<TypedArrayObject*> unsafeTypedArrayCrossCompartment(cx);
+    unsafeTypedArrayCrossCompartment = DangerouslyUnwrapTypedArray(cx, &args[1].toObject());
+    if (!unsafeTypedArrayCrossCompartment)
+        return false;
+    MOZ_ASSERT(!unsafeTypedArrayCrossCompartment->hasDetachedBuffer());
+
+    Scalar::Type sourceType = source->type();
+    if (!IsTypedArrayBitwiseSlice(sourceType, unsafeTypedArrayCrossCompartment->type())) {
+        args.rval().setBoolean(false);
+        return true;
+    }
+
+    MOZ_ASSERT(args[2].toInt32() >= 0);
+    uint32_t sourceOffset = uint32_t(args[2].toInt32());
+
+    MOZ_ASSERT(args[3].toInt32() >= 0);
+    uint32_t count = uint32_t(args[3].toInt32());
+
+    MOZ_ASSERT(count > 0 && count <= source->length());
+    MOZ_ASSERT(sourceOffset <= source->length() - count);
+    MOZ_ASSERT(count <= unsafeTypedArrayCrossCompartment->length());
+
+    size_t elementSize = TypedArrayElemSize(sourceType);
+    MOZ_ASSERT(elementSize == TypedArrayElemSize(unsafeTypedArrayCrossCompartment->type()));
+
+    SharedMem<uint8_t*> sourceData =
+        source->viewDataEither().cast<uint8_t*>() + sourceOffset * elementSize;
+
+    SharedMem<uint8_t*> unsafeTargetDataCrossCompartment =
+        unsafeTypedArrayCrossCompartment->viewDataEither().cast<uint8_t*>();
+
+    uint32_t byteLength = count * elementSize;
+
+    // The same-type case requires exact copying preserving the bit-level
+    // encoding of the source data, so use memcpy if possible. If source and
+    // target are the same buffer, we can't use memcpy (or memmove), because
+    // the specification requires sequential copying of the values. This case
+    // is only possible if a @@species constructor created a specifically
+    // crafted typed array. It won't happen in normal code and hence doesn't
+    // need to be optimized.
+    if (!TypedArrayObject::sameBuffer(source, unsafeTypedArrayCrossCompartment)) {
+        jit::AtomicOperations::memcpySafeWhenRacy(unsafeTargetDataCrossCompartment,
+                                                  sourceData,
+                                                  byteLength);
+    } else {
+        using namespace jit;
+
+        for (; byteLength > 0; byteLength--) {
+            AtomicOperations::storeSafeWhenRacy(unsafeTargetDataCrossCompartment++,
+                                                AtomicOperations::loadSafeWhenRacy(sourceData++));
+        }
+    }
+
+    args.rval().setBoolean(true);
+    return true;
+}
+
 static bool
 intrinsic_RegExpCreate(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     MOZ_ASSERT(args.length() == 1 || args.length() == 2);
     MOZ_ASSERT_IF(args.length() == 2, args[1].isString() || args[1].isUndefined());
     MOZ_ASSERT(!args.isConstructing());
@@ -2468,16 +2580,18 @@ static const JSFunctionSpec intrinsic_fu
 
     JS_FN("MoveTypedArrayElements",  intrinsic_MoveTypedArrayElements,  4,0),
     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("TypedArrayBitwiseSlice", intrinsic_TypedArrayBitwiseSlice, 4, 0),
+
     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("CallGeneratorMethodIfWrapped",