Bug 1263558 - Part 1: Self-host Array generics. r=till,bholley
authorTooru Fujisawa <arai_a@mac.com>
Fri, 15 Apr 2016 12:32:36 +0900
changeset 332206 5af002b8ef582c6d18ae5e7565d6c2c55dad0759
parent 332205 344a4bcc9015457aa8deadbaac79eead70acbb60
child 332207 f1876796b8665a096aba8083a195ed8e85751b5f
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill, bholley
bugs1263558
milestone48.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 1263558 - Part 1: Self-host Array generics. r=till,bholley
js/src/builtin/Array.js
js/src/jsarray.cpp
js/src/jsarray.h
js/src/tests/js1_6/Array/generics.js
js/src/vm/SelfHosting.cpp
js/xpconnect/tests/chrome/test_xrayToJS.xul
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -1038,8 +1038,65 @@ function ArrayConcat(arg1) {
 }
 
 function ArrayStaticConcat(arr, arg1) {
     if (arguments.length < 1)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat');
     var args = callFunction(std_Array_slice, arguments, 1);
     return callFunction(std_Function_apply, ArrayConcat, arr, args);
 }
+
+function ArrayStaticJoin(arr, separator) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.join');
+    return callFunction(std_Array_join, arr, separator);
+}
+
+function ArrayStaticReverse(arr) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reverse');
+    return callFunction(std_Array_reverse, arr);
+}
+
+function ArrayStaticSort(arr, comparefn) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.sort');
+    return callFunction(std_Array_sort, arr, comparefn);
+}
+
+function ArrayStaticPush(arr, arg1) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.push');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_Array_push, arr, args);
+}
+
+function ArrayStaticPop(arr) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.pop');
+    return callFunction(std_Array_pop, arr);
+}
+
+function ArrayStaticShift(arr) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.shift');
+    return callFunction(std_Array_shift, arr);
+}
+
+function ArrayStaticUnshift(arr, arg1) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.unshift');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_Array_unshift, arr, args);
+}
+
+function ArrayStaticSplice(arr, start, deleteCount) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.splice');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_Array_splice, arr, args);
+}
+
+function ArrayStaticSlice(arr, start, end) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.slice');
+    return callFunction(std_Array_slice, arr, start, end);
+}
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1385,18 +1385,18 @@ ArrayReverseDenseKernel(JSContext* cx, H
     }
 
     return DenseElementResult::Success;
 }
 
 DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
                              JSContext*, HandleObject, uint32_t);
 
-static bool
-array_reverse(JSContext* cx, unsigned argc, Value* vp)
+bool
+js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
 {
     AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.reverse");
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     uint32_t len;
@@ -2376,18 +2376,18 @@ CanOptimizeForDenseStorage(HandleObject 
      * other indexed properties on the object.  (Note that non-writable length
      * is subsumed by the initializedLength comparison.)
      */
     return !ObjectMayHaveExtraIndexedProperties(arr) &&
            startingIndex + count <= GetAnyBoxedOrUnboxedInitializedLength(arr);
 }
 
 /* ES 2016 draft Mar 25, 2016 22.1.3.26. */
-static bool
-array_splice(JSContext* cx, unsigned argc, Value* vp)
+bool
+js::array_splice(JSContext* cx, unsigned argc, Value* vp)
 {
     return array_splice_impl(cx, argc, vp, true);
 }
 
 static inline bool
 ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj,
                 uint32_t actualStart, uint32_t actualDeleteCount)
 {
@@ -3077,38 +3077,36 @@ array_of(JSContext* cx, unsigned argc, V
     if (!SetLengthProperty(cx, obj, args.length()))
         return false;
 
     // Step 11.
     args.rval().setObject(*obj);
     return true;
 }
 
-#define GENERIC JSFUN_GENERIC_NATIVE
-
 static const JSFunctionSpec array_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,      array_toSource,     0,0),
 #endif
     JS_SELF_HOSTED_FN(js_toString_str, "ArrayToString",      0,0),
     JS_FN(js_toLocaleString_str,       array_toLocaleString, 0,0),
 
     /* Perl-ish methods. */
-    JS_INLINABLE_FN("join",     array_join,         1,JSFUN_GENERIC_NATIVE, ArrayJoin),
-    JS_FN("reverse",            array_reverse,      0,JSFUN_GENERIC_NATIVE),
-    JS_FN("sort",               array_sort,         1,JSFUN_GENERIC_NATIVE),
-    JS_INLINABLE_FN("push",     array_push,         1,JSFUN_GENERIC_NATIVE, ArrayPush),
-    JS_INLINABLE_FN("pop",      array_pop,          0,JSFUN_GENERIC_NATIVE, ArrayPop),
-    JS_INLINABLE_FN("shift",    array_shift,        0,JSFUN_GENERIC_NATIVE, ArrayShift),
-    JS_FN("unshift",            array_unshift,      1,JSFUN_GENERIC_NATIVE),
-    JS_INLINABLE_FN("splice",   array_splice,       2,JSFUN_GENERIC_NATIVE, ArraySplice),
+    JS_INLINABLE_FN("join",     array_join,         1,0, ArrayJoin),
+    JS_FN("reverse",            array_reverse,      0,0),
+    JS_FN("sort",               array_sort,         1,0),
+    JS_INLINABLE_FN("push",     array_push,         1,0, ArrayPush),
+    JS_INLINABLE_FN("pop",      array_pop,          0,0, ArrayPop),
+    JS_INLINABLE_FN("shift",    array_shift,        0,0, ArrayShift),
+    JS_FN("unshift",            array_unshift,      1,0),
+    JS_INLINABLE_FN("splice",   array_splice,       2,0, ArraySplice),
 
     /* Pythonic sequence methods. */
     JS_SELF_HOSTED_FN("concat",      "ArrayConcat",      1,0),
-    JS_INLINABLE_FN("slice",    array_slice,        2,JSFUN_GENERIC_NATIVE, ArraySlice),
+    JS_INLINABLE_FN("slice",    array_slice,        2,0, ArraySlice),
 
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayLastIndexOf", 1,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayIndexOf",     1,0),
     JS_SELF_HOSTED_FN("forEach",     "ArrayForEach",     1,0),
     JS_SELF_HOSTED_FN("map",         "ArrayMap",         1,0),
     JS_SELF_HOSTED_FN("filter",      "ArrayFilter",      1,0),
     JS_SELF_HOSTED_FN("reduce",      "ArrayReduce",      1,0),
     JS_SELF_HOSTED_FN("reduceRight", "ArrayReduceRight", 1,0),
@@ -3139,16 +3137,25 @@ static const JSFunctionSpec array_static
     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
     JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
     JS_SELF_HOSTED_FN("map",         "ArrayStaticMap",   2,0),
     JS_SELF_HOSTED_FN("filter",      "ArrayStaticFilter", 2,0),
     JS_SELF_HOSTED_FN("every",       "ArrayStaticEvery", 2,0),
     JS_SELF_HOSTED_FN("some",        "ArrayStaticSome",  2,0),
     JS_SELF_HOSTED_FN("reduce",      "ArrayStaticReduce", 2,0),
     JS_SELF_HOSTED_FN("reduceRight", "ArrayStaticReduceRight", 2,0),
+    JS_SELF_HOSTED_FN("join",        "ArrayStaticJoin", 2,0),
+    JS_SELF_HOSTED_FN("reverse",     "ArrayStaticReverse", 1,0),
+    JS_SELF_HOSTED_FN("sort",        "ArrayStaticSort", 2,0),
+    JS_SELF_HOSTED_FN("push",        "ArrayStaticPush", 2,0),
+    JS_SELF_HOSTED_FN("pop",         "ArrayStaticPop", 1,0),
+    JS_SELF_HOSTED_FN("shift",       "ArrayStaticShift", 1,0),
+    JS_SELF_HOSTED_FN("unshift",     "ArrayStaticUnshift", 2,0),
+    JS_SELF_HOSTED_FN("splice",      "ArrayStaticSplice", 3,0),
+    JS_SELF_HOSTED_FN("slice",       "ArrayStaticSlice", 3,0),
     JS_SELF_HOSTED_FN("from",        "ArrayFrom", 3,0),
     JS_FN("of",                 array_of,           0,0),
 
     JS_FS_END
 };
 
 const JSPropertySpec array_static_props[] = {
     JS_SELF_HOSTED_SYM_GET(species, "ArraySpecies", 0),
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -179,16 +179,22 @@ extern bool
 array_unshift(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
 array_slice(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern JSObject*
 array_slice_dense(JSContext* cx, HandleObject obj, int32_t begin, int32_t end, HandleObject result);
 
+extern bool
+array_reverse(JSContext* cx, unsigned argc, js::Value* vp);
+
+extern bool
+array_splice(JSContext* cx, unsigned argc, js::Value* vp);
+
 /*
  * Append the given (non-hole) value to the end of an array.  The array must be
  * a newborn array -- that is, one which has not been exposed to script for
  * arbitrary manipulation.  (This method optimizes on the assumption that
  * extending the array to accommodate the element will never make the array
  * sparse, which requires that the array be completely filled.)
  */
 extern bool
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_6/Array/generics.js
@@ -0,0 +1,331 @@
+var BUGNUMBER = 1263558;
+var summary = "Self-host all Array generics.";
+
+print(BUGNUMBER + ": " + summary);
+
+var arr, arrLike, tmp, f;
+
+function reset() {
+  arr = [5, 7, 13];
+  arrLike = {
+    length: 3,
+    0: 5,
+    1: 7,
+    2: 13,
+    toString() {
+      return "arrLike";
+    }
+  };
+  tmp = [];
+}
+function toString() {
+  return "G";
+}
+
+// Array.join (test this first to use it in remaining tests).
+reset();
+assertThrowsInstanceOf(() => Array.join(), TypeError);
+assertEq(Array.join(arr), "5,7,13");
+assertEq(Array.join(arr, "-"), "5-7-13");
+assertEq(Array.join(arrLike), "5,7,13");
+assertEq(Array.join(arrLike, "-"), "5-7-13");
+
+// Array.concat.
+reset();
+assertThrowsInstanceOf(() => Array.concat(), TypeError);
+assertEq(Array.join(Array.concat(arr), ","), "5,7,13");
+assertEq(Array.join(Array.concat(arr, 11), ","), "5,7,13,11");
+assertEq(Array.join(Array.concat(arr, 11, 17), ","), "5,7,13,11,17");
+assertEq(Array.join(Array.concat(arrLike), ","), "arrLike");
+assertEq(Array.join(Array.concat(arrLike, 11), ","), "arrLike,11");
+assertEq(Array.join(Array.concat(arrLike, 11, 17), ","), "arrLike,11,17");
+
+// Array.lastIndexOf.
+reset();
+assertThrowsInstanceOf(() => Array.lastIndexOf(), TypeError);
+assertEq(Array.lastIndexOf(arr), -1);
+assertEq(Array.lastIndexOf(arr, 1), -1);
+assertEq(Array.lastIndexOf(arr, 5), 0);
+assertEq(Array.lastIndexOf(arr, 7), 1);
+assertEq(Array.lastIndexOf(arr, 13, 1), -1);
+assertEq(Array.lastIndexOf(arrLike), -1);
+assertEq(Array.lastIndexOf(arrLike, 1), -1);
+assertEq(Array.lastIndexOf(arrLike, 5), 0);
+assertEq(Array.lastIndexOf(arrLike, 7), 1);
+assertEq(Array.lastIndexOf(arrLike, 13, 1), -1);
+
+// Array.indexOf.
+reset();
+assertThrowsInstanceOf(() => Array.indexOf(), TypeError);
+assertEq(Array.indexOf(arr), -1);
+assertEq(Array.indexOf(arr, 1), -1);
+assertEq(Array.indexOf(arr, 5), 0);
+assertEq(Array.indexOf(arr, 7), 1);
+assertEq(Array.indexOf(arr, 1, 5), -1);
+assertEq(Array.indexOf(arrLike), -1);
+assertEq(Array.indexOf(arrLike, 1), -1);
+assertEq(Array.indexOf(arrLike, 5), 0);
+assertEq(Array.indexOf(arrLike, 7), 1);
+assertEq(Array.indexOf(arrLike, 1, 5), -1);
+
+// Array.forEach.
+reset();
+assertThrowsInstanceOf(() => Array.forEach(), TypeError);
+assertThrowsInstanceOf(() => Array.forEach(arr), TypeError);
+assertThrowsInstanceOf(() => Array.forEach(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(this, ...args);
+};
+tmp = [];
+Array.forEach(arr, f);
+assertEq(tmp.join(","), "G,5,0,5,7,13," + "G,7,1,5,7,13," + "G,13,2,5,7,13");
+tmp = [];
+Array.forEach(arr, f, "T");
+assertEq(tmp.join(","), "T,5,0,5,7,13," + "T,7,1,5,7,13," + "T,13,2,5,7,13");
+tmp = [];
+Array.forEach(arrLike, f);
+assertEq(tmp.join(","), "G,5,0,arrLike," + "G,7,1,arrLike," + "G,13,2,arrLike");
+tmp = [];
+Array.forEach(arrLike, f, "T");
+assertEq(tmp.join(","), "T,5,0,arrLike," + "T,7,1,arrLike," + "T,13,2,arrLike");
+
+// Array.map.
+reset();
+assertThrowsInstanceOf(() => Array.map(), TypeError);
+assertThrowsInstanceOf(() => Array.map(arr), TypeError);
+assertThrowsInstanceOf(() => Array.map(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(this, ...args);
+  return args[0] * 2;
+}
+tmp = [];
+assertEq(Array.join(Array.map(arr, f), ","), "10,14,26");
+assertEq(tmp.join(","), "G,5,0,5,7,13," + "G,7,1,5,7,13," + "G,13,2,5,7,13");
+tmp = [];
+assertEq(Array.join(Array.map(arr, f, "T"), ","), "10,14,26");
+assertEq(tmp.join(","), "T,5,0,5,7,13," + "T,7,1,5,7,13," + "T,13,2,5,7,13");
+tmp = [];
+assertEq(Array.join(Array.map(arrLike, f), ","), "10,14,26");
+assertEq(tmp.join(","), "G,5,0,arrLike," + "G,7,1,arrLike," + "G,13,2,arrLike");
+tmp = [];
+assertEq(Array.join(Array.map(arrLike, f, "T"), ","), "10,14,26");
+assertEq(tmp.join(","), "T,5,0,arrLike," + "T,7,1,arrLike," + "T,13,2,arrLike");
+
+// Array.filter.
+reset();
+assertThrowsInstanceOf(() => Array.filter(), TypeError);
+assertThrowsInstanceOf(() => Array.filter(arr), TypeError);
+assertThrowsInstanceOf(() => Array.filter(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(this, ...args);
+  return args[0] < 10;
+}
+tmp = [];
+assertEq(Array.join(Array.filter(arr, f), ","), "5,7");
+assertEq(tmp.join(","), "G,5,0,5,7,13," + "G,7,1,5,7,13," + "G,13,2,5,7,13");
+tmp = [];
+assertEq(Array.join(Array.filter(arr, f, "T"), ","), "5,7");
+assertEq(tmp.join(","), "T,5,0,5,7,13," + "T,7,1,5,7,13," + "T,13,2,5,7,13");
+tmp = [];
+assertEq(Array.join(Array.filter(arrLike, f), ","), "5,7");
+assertEq(tmp.join(","), "G,5,0,arrLike," + "G,7,1,arrLike," + "G,13,2,arrLike");
+tmp = [];
+assertEq(Array.join(Array.filter(arrLike, f, "T"), ","), "5,7");
+assertEq(tmp.join(","), "T,5,0,arrLike," + "T,7,1,arrLike," + "T,13,2,arrLike");
+
+// Array.every.
+reset();
+assertThrowsInstanceOf(() => Array.every(), TypeError);
+assertThrowsInstanceOf(() => Array.every(arr), TypeError);
+assertThrowsInstanceOf(() => Array.every(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(this, ...args);
+  return args[0] < 6;
+}
+tmp = [];
+assertEq(Array.every(arr, f), false);
+assertEq(tmp.join(","), "G,5,0,5,7,13," + "G,7,1,5,7,13");
+tmp = [];
+assertEq(Array.every(arr, f, "T"), false);
+assertEq(tmp.join(","), "T,5,0,5,7,13," + "T,7,1,5,7,13");
+tmp = [];
+assertEq(Array.every(arrLike, f), false);
+assertEq(tmp.join(","), "G,5,0,arrLike," + "G,7,1,arrLike");
+tmp = [];
+assertEq(Array.every(arrLike, f, "T"), false);
+assertEq(tmp.join(","), "T,5,0,arrLike," + "T,7,1,arrLike");
+
+// Array.some.
+reset();
+assertThrowsInstanceOf(() => Array.some(), TypeError);
+assertThrowsInstanceOf(() => Array.some(arr), TypeError);
+assertThrowsInstanceOf(() => Array.some(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(this, ...args);
+  return args[0] == 7;
+}
+tmp = [];
+assertEq(Array.some(arr, f), true);
+assertEq(tmp.join(","), "G,5,0,5,7,13," + "G,7,1,5,7,13");
+tmp = [];
+assertEq(Array.some(arr, f, "T"), true);
+assertEq(tmp.join(","), "T,5,0,5,7,13," + "T,7,1,5,7,13");
+tmp = [];
+assertEq(Array.some(arrLike, f), true);
+assertEq(tmp.join(","), "G,5,0,arrLike," + "G,7,1,arrLike");
+tmp = [];
+assertEq(Array.some(arrLike, f, "T"), true);
+assertEq(tmp.join(","), "T,5,0,arrLike," + "T,7,1,arrLike");
+
+// Array.reduce.
+reset();
+assertThrowsInstanceOf(() => Array.reduce(), TypeError);
+assertThrowsInstanceOf(() => Array.reduce(arr), TypeError);
+assertThrowsInstanceOf(() => Array.reduce(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(...args);
+  return args[0] + args[1];
+}
+tmp = [];
+assertEq(Array.reduce(arr, f), 25);
+assertEq(tmp.join(","), "5,7,1,5,7,13," + "12,13,2,5,7,13");
+tmp = [];
+assertEq(Array.reduce(arr, f, 17), 42);
+assertEq(tmp.join(","), "17,5,0,5,7,13," + "22,7,1,5,7,13," + "29,13,2,5,7,13");
+tmp = [];
+assertEq(Array.reduce(arrLike, f), 25);
+assertEq(tmp.join(","), "5,7,1,arrLike," + "12,13,2,arrLike");
+tmp = [];
+assertEq(Array.reduce(arrLike, f, 17), 42);
+assertEq(tmp.join(","), "17,5,0,arrLike," + "22,7,1,arrLike," + "29,13,2,arrLike");
+
+// Array.reduceRight.
+reset();
+assertThrowsInstanceOf(() => Array.reduceRight(), TypeError);
+assertThrowsInstanceOf(() => Array.reduceRight(arr), TypeError);
+assertThrowsInstanceOf(() => Array.reduceRight(arrLike), TypeError);
+f = function(...args) {
+  tmp.push(...args);
+  return args[0] + args[1];
+}
+tmp = [];
+assertEq(Array.reduceRight(arr, f), 25);
+assertEq(tmp.join(","), "13,7,1,5,7,13," + "20,5,0,5,7,13");
+tmp = [];
+assertEq(Array.reduceRight(arr, f, 17), 42);
+assertEq(tmp.join(","), "17,13,2,5,7,13," + "30,7,1,5,7,13," + "37,5,0,5,7,13");
+tmp = [];
+assertEq(Array.reduceRight(arrLike, f), 25);
+assertEq(tmp.join(","), "13,7,1,arrLike," + "20,5,0,arrLike");
+tmp = [];
+assertEq(Array.reduceRight(arrLike, f, 17), 42);
+assertEq(tmp.join(","), "17,13,2,arrLike," + "30,7,1,arrLike," + "37,5,0,arrLike");
+
+// Array.reverse.
+reset();
+assertThrowsInstanceOf(() => Array.reverse(), TypeError);
+assertEq(Array.join(Array.reverse(arr), ","), "13,7,5");
+assertEq(Array.join(arr, ","), "13,7,5");
+assertEq(Array.join(Array.reverse(arrLike), ","), "13,7,5");
+assertEq(Array.join(arrLike, ","), "13,7,5");
+
+// Array.sort.
+reset();
+assertThrowsInstanceOf(() => Array.sort(), TypeError);
+f = function(x, y) {
+  return y - x;
+}
+assertEq(Array.join(Array.sort(arr), ","), "13,5,7");
+assertEq(Array.join(Array.sort(arr, f), ","), "13,7,5");
+assertEq(Array.join(Array.sort(arrLike), ","), "13,5,7");
+assertEq(Array.join(Array.sort(arrLike, f), ","), "13,7,5");
+
+// Array.push.
+reset();
+assertThrowsInstanceOf(() => Array.push(), TypeError);
+assertEq(Array.push(arr), 3);
+assertEq(Array.join(arr), "5,7,13");
+assertEq(Array.push(arr, 17), 4);
+assertEq(Array.join(arr), "5,7,13,17");
+assertEq(Array.push(arr, 19, 21), 6);
+assertEq(Array.join(arr), "5,7,13,17,19,21");
+assertEq(Array.push(arrLike), 3);
+assertEq(Array.join(arrLike), "5,7,13");
+assertEq(Array.push(arrLike, 17), 4);
+assertEq(Array.join(arrLike), "5,7,13,17");
+assertEq(Array.push(arrLike, 19, 21), 6);
+assertEq(Array.join(arrLike), "5,7,13,17,19,21");
+
+// Array.pop.
+reset();
+assertThrowsInstanceOf(() => Array.pop(), TypeError);
+assertEq(Array.pop(arr), 13);
+assertEq(Array.join(arr), "5,7");
+assertEq(Array.pop(arr), 7);
+assertEq(Array.join(arr), "5");
+assertEq(Array.pop(arrLike), 13);
+assertEq(Array.join(arrLike), "5,7");
+assertEq(Array.pop(arrLike), 7);
+assertEq(Array.join(arrLike), "5");
+
+// Array.shift.
+reset();
+assertThrowsInstanceOf(() => Array.shift(), TypeError);
+assertEq(Array.shift(arr), 5);
+assertEq(Array.join(arr), "7,13");
+assertEq(Array.shift(arr), 7);
+assertEq(Array.join(arr), "13");
+assertEq(Array.shift(arrLike), 5);
+assertEq(Array.join(arrLike), "7,13");
+assertEq(Array.shift(arrLike), 7);
+assertEq(Array.join(arrLike), "13");
+
+// Array.unshift.
+reset();
+assertThrowsInstanceOf(() => Array.unshift(), TypeError);
+assertEq(Array.unshift(arr), 3);
+assertEq(Array.join(arr), "5,7,13");
+assertEq(Array.unshift(arr, 17), 4);
+assertEq(Array.join(arr), "17,5,7,13");
+assertEq(Array.unshift(arr, 19, 21), 6);
+assertEq(Array.join(arr), "19,21,17,5,7,13");
+assertEq(Array.unshift(arrLike), 3);
+assertEq(Array.join(arrLike), "5,7,13");
+assertEq(Array.unshift(arrLike, 17), 4);
+assertEq(Array.join(arrLike), "17,5,7,13");
+assertEq(Array.unshift(arrLike, 19, 21), 6);
+assertEq(Array.join(arrLike), "19,21,17,5,7,13");
+
+// Array.splice.
+reset();
+assertThrowsInstanceOf(() => Array.splice(), TypeError);
+assertEq(Array.join(Array.splice(arr)), "");
+assertEq(Array.join(arr), "5,7,13");
+assertEq(Array.join(Array.splice(arr, 1)), "7,13");
+assertEq(Array.join(arr), "5");
+reset();
+assertEq(Array.join(Array.splice(arr, 1, 1)), "7");
+assertEq(Array.join(arr), "5,13");
+reset();
+assertEq(Array.join(Array.splice(arrLike)), "");
+assertEq(Array.join(arrLike), "5,7,13");
+assertEq(Array.join(Array.splice(arrLike, 1)), "7,13");
+assertEq(Array.join(arrLike), "5");
+reset();
+assertEq(Array.join(Array.splice(arrLike, 1, 1)), "7");
+assertEq(Array.join(arrLike), "5,13");
+
+// Array.slice.
+reset();
+assertThrowsInstanceOf(() => Array.slice(), TypeError);
+assertEq(Array.join(Array.slice(arr)), "5,7,13");
+assertEq(Array.join(Array.slice(arr, 1)), "7,13");
+assertEq(Array.join(Array.slice(arr, 1, 1)), "");
+assertEq(Array.join(Array.slice(arr, 1, 2)), "7");
+assertEq(Array.join(Array.slice(arrLike)), "5,7,13");
+assertEq(Array.join(Array.slice(arrLike, 1)), "7,13");
+assertEq(Array.join(Array.slice(arrLike, 1, 1)), "");
+assertEq(Array.join(Array.slice(arrLike, 1, 2)), "7");
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2243,16 +2243,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_INLINABLE_FN("std_Array",                 ArrayConstructor,             1,0, Array),
     JS_FN("std_Array_join",                      array_join,                   1,0),
     JS_INLINABLE_FN("std_Array_push",            array_push,                   1,0, ArrayPush),
     JS_INLINABLE_FN("std_Array_pop",             array_pop,                    0,0, ArrayPop),
     JS_INLINABLE_FN("std_Array_shift",           array_shift,                  0,0, ArrayShift),
     JS_FN("std_Array_unshift",                   array_unshift,                1,0),
     JS_INLINABLE_FN("std_Array_slice",           array_slice,                  2,0, ArraySlice),
     JS_FN("std_Array_sort",                      array_sort,                   1,0),
+    JS_FN("std_Array_reverse",                   array_reverse,                0,0),
+    JS_INLINABLE_FN("std_Array_splice",          array_splice,                 2,0, ArraySplice),
 
     JS_FN("std_Date_now",                        date_now,                     0,0),
     JS_FN("std_Date_valueOf",                    date_valueOf,                 0,0),
 
     JS_FN("std_Function_apply",                  fun_apply,                    2,0),
 
     JS_INLINABLE_FN("std_Math_floor",            math_floor,                   1,0, MathFloor),
     JS_INLINABLE_FN("std_Math_max",              math_max,                     2,0, MathMax),
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -534,23 +534,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     // |own| data property. So we add it to the ignore list here, and check it
     // separately.
     //
     // |Symbol.unscopables| should in principle be exposed, but it is
     // inconvenient (as it's a data property, unsupported by ClassSpec) and
     // low value.
     let propsToSkip = ['length', Symbol.unscopables];
 
-    // On the constructor, we want to skip all the non-standard "generic"
-    // functions.  We're trying to remove them anyway; no point doing extra work
-    // to expose them over Xrays.
-    let ctorPropsToSkip = ["join", "reverse", "sort", "push", "pop", "shift",
-                           "unshift", "splice", "slice"];
-    testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip,
-             ctorPropsToSkip);
+    testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
 
     let symbolProps = '';
     uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
     symbolProps = `trickyArray[uniqueSymbol] = 43;
                    trickyArray[Symbol.for("registrySymbolProp")] = 44;`;
     var trickyArray =
       iwin.eval(`var trickyArray = [];
                  trickyArray.primitiveProp = 42;