From 878648c8cfe238ba186ea2ee7afe07333d5a37c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Fri, 29 Jul 2016 09:04:06 -0700 Subject: [PATCH] Bug 1121938 - Implement TypedArray.prototype.toString and .toLocaleString. r=waldo, r=froydnj --- .../test/browser_webconsole_output_02.js | 2 +- devtools/server/actors/webaudio.js | 2 +- ...test_tcpsocket_client_and_server_basics.js | 3 + js/src/builtin/TypedArray.js | 130 +++++++++++++----- js/src/jit-test/tests/ion/bug858586.js | 7 + js/src/tests/Intl/TypedArray/shell.js | 0 .../tests/Intl/TypedArray/toLocaleString.js | 103 ++++++++++++++ js/src/tests/ecma_6/TypedArray/set.js | 33 +++++ .../TypedArray/toLocaleString-detached.js | 50 +++++++ .../TypedArray/toLocaleString-nointl.js | 52 +++++++ .../tests/ecma_6/TypedArray/toLocaleString.js | 92 +++++++++++++ js/src/tests/ecma_6/TypedArray/toString.js | 48 +++++++ js/src/vm/TypedArrayObject.cpp | 4 +- js/xpconnect/tests/unit/test_bug1033927.js | 2 +- .../osfile/modules/osfile_unix_front.jsm | 2 +- .../tests/xpcshell/test_Promise.js | 3 +- 16 files changed, 493 insertions(+), 40 deletions(-) create mode 100644 js/src/tests/Intl/TypedArray/shell.js create mode 100644 js/src/tests/Intl/TypedArray/toLocaleString.js create mode 100644 js/src/tests/ecma_6/TypedArray/set.js create mode 100644 js/src/tests/ecma_6/TypedArray/toLocaleString-detached.js create mode 100644 js/src/tests/ecma_6/TypedArray/toLocaleString-nointl.js create mode 100644 js/src/tests/ecma_6/TypedArray/toLocaleString.js create mode 100644 js/src/tests/ecma_6/TypedArray/toString.js diff --git a/devtools/client/webconsole/test/browser_webconsole_output_02.js b/devtools/client/webconsole/test/browser_webconsole_output_02.js index f919a77bc95b..8018669a9f38 100644 --- a/devtools/client/webconsole/test/browser_webconsole_output_02.js +++ b/devtools/client/webconsole/test/browser_webconsole_output_02.js @@ -97,7 +97,7 @@ var inputTests = [ { input: "window.typedarray1", output: "Int32Array [ 1, 287, 8651, 40983, 8754 ]", - printOutput: "[object Int32Array]", + printOutput: "1,287,8651,40983,8754", inspectable: true, variablesViewLabel: "Int32Array[5]", }, diff --git a/devtools/server/actors/webaudio.js b/devtools/server/actors/webaudio.js index 4fb141918089..d9035a9072ad 100644 --- a/devtools/server/actors/webaudio.js +++ b/devtools/server/actors/webaudio.js @@ -802,7 +802,7 @@ function InvalidCommandError() { * or "Float32Array". */ function getConstructorName(obj) { - return obj.toString().match(/\[object ([^\[\]]*)\]\]?$/)[1]; + return Object.prototype.toString.call(obj).match(/\[object ([^\[\]]*)\]\]?$/)[1]; } /** diff --git a/dom/network/tests/test_tcpsocket_client_and_server_basics.js b/dom/network/tests/test_tcpsocket_client_and_server_basics.js index 04a075f9aba2..af1ea5763e08 100644 --- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js +++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js @@ -347,6 +347,9 @@ function* test_basics() { serverQueue = connectedResult.queue; // -- Attempt to send non-string data. + // Restore the original behavior by replacing toString with + // Object.prototype.toString. (bug 1121938) + bigUint8Array.toString = Object.prototype.toString; is(clientSocket.send(bigUint8Array), true, 'Client sending a large non-string should only send a small string.'); clientSocket.close(); diff --git a/js/src/builtin/TypedArray.js b/js/src/builtin/TypedArray.js index c073e3317fa1..7b459ddb9d2b 100644 --- a/js/src/builtin/TypedArray.js +++ b/js/src/builtin/TypedArray.js @@ -29,7 +29,7 @@ function IsDetachedBuffer(buffer) { // optimization in place there to avoid incurring the cost here. An // alternative is to give SharedArrayBuffer the same layout as ArrayBuffer. if (IsSharedArrayBuffer(buffer)) - return false; + return false; var flags = UnsafeGetInt32FromReservedSlot(buffer, JS_ARRAYBUFFER_FLAGS_SLOT); return (flags & JS_ARRAYBUFFER_DETACHED_FLAG) !== 0; @@ -171,11 +171,10 @@ function TypedArrayEvery(callbackfn, thisArg = undefined) { // Steps 3-5. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 6. if (arguments.length === 0) @@ -260,11 +259,10 @@ function TypedArrayFilter(callbackfn, thisArg = undefined) { // Step 4. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 5. if (arguments.length === 0) @@ -329,11 +327,10 @@ function TypedArrayFind(predicate, thisArg = undefined) { // Steps 3-5. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 6. if (arguments.length === 0) @@ -372,11 +369,10 @@ function TypedArrayFindIndex(predicate, thisArg = undefined) { // Steps 3-5. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 6. if (arguments.length === 0) @@ -413,11 +409,10 @@ function TypedArrayForEach(callbackfn, thisArg = undefined) { // Step 3-4. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 5. if (arguments.length === 0) @@ -518,7 +513,7 @@ function TypedArrayJoin(separator) { var element0 = O[0]; // Steps 10-11. - // Omit the 'if' clause in step 10, since typed arrays can not have undefined or null elements. + // Omit the 'if' clause in step 10, since typed arrays can't have undefined or null elements. var R = ToString(element0); // Steps 12-13. @@ -530,7 +525,7 @@ function TypedArrayJoin(separator) { var element = O[k]; // Steps 13.c-13.d. - // Omit the 'if' clause in step 13.c, since typed arrays can not have undefined or null elements. + // Omit the 'if' clause in step 13.c, since typed arrays can't have undefined or null elements. var next = ToString(element); // Step 13.e. @@ -607,11 +602,10 @@ function TypedArrayMap(callbackfn, thisArg = undefined) { // Step 4. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 5. if (arguments.length === 0) @@ -657,11 +651,10 @@ function TypedArrayReduce(callbackfn/*, initialValue*/) { // Steps 3-5. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 6. if (arguments.length === 0) @@ -704,11 +697,10 @@ function TypedArrayReduceRight(callbackfn/*, initialValue*/) { // Steps 3-5. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 6. if (arguments.length === 0) @@ -870,7 +862,7 @@ function SetFromTypedArray(target, typedArray, targetOffset, targetLength) { } // ES6 draft 20150304 %TypedArray%.prototype.set -function TypedArraySet(overloaded, offset) { +function TypedArraySet(overloaded, offset = 0) { // Steps 2-5, either algorithm. var target = this; if (!IsObject(target) || !IsTypedArray(target)) { @@ -971,11 +963,10 @@ function TypedArraySome(callbackfn, thisArg = undefined) { // Steps 3-5. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(O); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, O, O, "TypedArrayLength"); - } // Step 6. if (arguments.length === 0) @@ -1051,11 +1042,10 @@ function TypedArraySort(comparefn) { // Step 3. var len; - if (isTypedArray) { + if (isTypedArray) len = TypedArrayLength(obj); - } else { + else len = callFunction(CallTypedArrayMethodIfWrapped, obj, obj, "TypedArrayLength"); - } if (comparefn === undefined) { comparefn = TypedArrayCompare; @@ -1104,6 +1094,78 @@ function TypedArraySort(comparefn) { return QuickSort(obj, len, comparefn); } +// ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f +// 22.2.3.28 %TypedArray%.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ]) +// ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276 +// 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]]) +function TypedArrayToLocaleString(locales = undefined, options = undefined) { + // ValidateTypedArray, then step 1. + var array = this; + + // This function is not generic. + // We want to make sure that we have an attached buffer, per spec prose. + var isTypedArray = IsTypedArrayEnsuringArrayBuffer(array); + + // If we got here, `this` is either a typed array or a cross-compartment + // wrapper for one. + + // Step 2. + var len; + if (isTypedArray) + len = TypedArrayLength(array); + else + len = callFunction(CallTypedArrayMethodIfWrapped, array, array, "TypedArrayLength"); + + // Step 4. + if (len === 0) + return ""; + + // Step 5. + var firstElement = array[0]; + + // Steps 6-7. + // Omit the 'if' clause in step 6, since typed arrays can't have undefined + // or null elements. +#if EXPOSE_INTL_API + var R = ToString(callContentFunction(firstElement.toLocaleString, firstElement, locales, options)); +#else + var R = ToString(callContentFunction(firstElement.toLocaleString, firstElement)); +#endif + + // Step 3 (reordered). + // We don't (yet?) implement locale-dependent separators. + var separator = ","; + + // Steps 8-9. + for (var k = 1; k < len; k++) { + // Step 9.a. + var S = R + separator; + + // Step 9.b. + var nextElement = array[k]; + + // Step 9.c *should* be unreachable: typed array elements are numbers. + // But bug 1079853 means |nextElement| *could* be |undefined|, if the + // previous iteration's step 9.d or step 7 detached |array|'s buffer. + // Conveniently, if this happens, evaluating |nextElement.toLocaleString| + // throws the required TypeError, and the only observable difference is + // the error message. So despite bug 1079853, we can skip step 9.c. + + // Step 9.d. +#if EXPOSE_INTL_API + R = ToString(callContentFunction(nextElement.toLocaleString, nextElement, locales, options)); +#else + R = ToString(callContentFunction(nextElement.toLocaleString, nextElement)); +#endif + + // Step 9.e. + R = S + R; + } + + // Step 10. + return R; +} + // ES6 draft 20150304 %TypedArray%.prototype.subarray function TypedArraySubarray(begin, end) { // Step 1. diff --git a/js/src/jit-test/tests/ion/bug858586.js b/js/src/jit-test/tests/ion/bug858586.js index ce4de29b1499..73e10872d404 100644 --- a/js/src/jit-test/tests/ion/bug858586.js +++ b/js/src/jit-test/tests/ion/bug858586.js @@ -1,3 +1,10 @@ +// This test case was created before %TypedArrayPrototype%.toString was +// implemented. Now that we've got %TypedArrayPrototype%.toString the test will +// attempt to create a 300300001 character long string and either timeout or +// throw an oom error. Restore the original behavior by replacing toString with +// Object.prototype.toString. +Uint8ClampedArray.prototype.toString = Object.prototype.toString; + function A(a) { this.a = a; } A.prototype.foo = function (x) {}; function B(b) { this.b = b; } diff --git a/js/src/tests/Intl/TypedArray/shell.js b/js/src/tests/Intl/TypedArray/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/Intl/TypedArray/toLocaleString.js b/js/src/tests/Intl/TypedArray/toLocaleString.js new file mode 100644 index 000000000000..7a5d0be30a61 --- /dev/null +++ b/js/src/tests/Intl/TypedArray/toLocaleString.js @@ -0,0 +1,103 @@ +if (typeof Intl === "object") { + const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + const localeSep = [,,].toLocaleString(); + + const originalNumberToLocaleString = Number.prototype.toLocaleString; + + // Missing arguments are passed as |undefined|. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 2); + assertEq(arguments[0], undefined); + assertEq(arguments[1], undefined); + return "pass"; + }; + + // Single element case. + assertEq(new constructor(1).toLocaleString(), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + // Missing options is passed as |undefined|. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 2); + assertEq(arguments[0], locales); + assertEq(arguments[1], undefined); + return "pass"; + }; + let locales = {}; + + // Single element case. + assertEq(new constructor(1).toLocaleString(locales), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(locales), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + // Ensure "locales" and "options" arguments are passed to the array elements. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 2); + assertEq(arguments[0], locales); + assertEq(arguments[1], options); + return "pass"; + }; + let locales = {}; + let options = {}; + + // Single element case. + assertEq(new constructor(1).toLocaleString(locales, options), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(locales, options), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + assertEq(new Float32Array([NaN]).toLocaleString("ar"), "ليس رقم"); + assertEq(new Float64Array([NaN]).toLocaleString(["zh-hant", "ar"]), "非數值"); + assertEq(new Float32Array([Infinity]).toLocaleString(["dz"]), "གྲངས་མེད"); + assertEq(new Float64Array([-Infinity]).toLocaleString(["fr", "en"]), "-∞"); + + const sampleValues = [-0, +0, -1, +1, -2, +2, -0.5, +0.5]; + const sampleLocales = [ + void 0, + "en", + "th-th-u-nu-thai", + ["tlh", "de"], + ]; + const sampleOptions = [ + void 0, + {}, + {style: "percent"}, + {style: "currency", currency: "USD", minimumIntegerDigits: 4}, + ]; + for (let locale of sampleLocales) { + for (let options of sampleOptions) { + let nf = new Intl.NumberFormat(locale, options); + for (let constructor of constructors) { + let typedArray = new constructor(sampleValues); + let expected = [].map.call(typedArray, nf.format).join(localeSep); + assertEq(typedArray.toLocaleString(locale, options), expected); + } + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/set.js b/js/src/tests/ecma_6/TypedArray/set.js new file mode 100644 index 000000000000..9f814c6d6133 --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/set.js @@ -0,0 +1,33 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); + +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +// %TypedArrayPrototype% has an own "set" function property. +assertEq(TypedArrayPrototype.hasOwnProperty("set"), true); +assertEq(typeof TypedArrayPrototype.set, "function"); + +// The concrete TypedArray prototypes do not have an own "set" property. +assertEq(constructors.every(c => !c.hasOwnProperty("set")), true); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArrayPrototype, "set"), { + value: TypedArrayPrototype.set, + writable: true, + enumerable: false, + configurable: true, +}); + +assertEq(TypedArrayPrototype.set.name, "set"); +assertEq(TypedArrayPrototype.set.length, 1); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/toLocaleString-detached.js b/js/src/tests/ecma_6/TypedArray/toLocaleString-detached.js new file mode 100644 index 000000000000..e019501b8fcb --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/toLocaleString-detached.js @@ -0,0 +1,50 @@ +if (typeof detachArrayBuffer === "function") { + const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + const originalNumberToLocaleString = Number.prototype.toLocaleString; + + // Throws if array buffer is detached. + for (let constructor of constructors) { + let typedArray = new constructor(42); + detachArrayBuffer(typedArray.buffer, "same-data"); + assertThrowsInstanceOf(() => typedArray.toLocaleString(), TypeError); + } + + // Throws a TypeError if detached in Number.prototype.toLocaleString. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + "use strict"; + if (!detached) { + detachArrayBuffer(typedArray.buffer, "same-data"); + detached = true; + } + return this; + }; + + // No error for single element arrays. + let detached = false; + let typedArray = new constructor(1); + assertEq(typedArray.toLocaleString(), "0"); + assertEq(detached, true); + + // TypeError if more than one element is present. + detached = false; + typedArray = new constructor(2); + assertThrowsInstanceOf(() => typedArray.toLocaleString(), TypeError); + assertEq(detached, true); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/toLocaleString-nointl.js b/js/src/tests/ecma_6/TypedArray/toLocaleString-nointl.js new file mode 100644 index 000000000000..846433ddee9f --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/toLocaleString-nointl.js @@ -0,0 +1,52 @@ +if (typeof Intl !== "object") { + const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, + ]; + + const localeSep = [,,].toLocaleString(); + + const originalNumberToLocaleString = Number.prototype.toLocaleString; + + // Ensure no arguments are passed to the array elements. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 0); + return "pass"; + }; + + // Single element case. + assertEq(new constructor(1).toLocaleString(), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; + + // Ensure no arguments are passed to the array elements even if supplied. + for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + assertEq(arguments.length, 0); + return "pass"; + }; + let locales = {}; + let options = {}; + + // Single element case. + assertEq(new constructor(1).toLocaleString(locales, options), "pass"); + + // More than one element. + assertEq(new constructor(2).toLocaleString(locales, options), "pass" + localeSep + "pass"); + } + Number.prototype.toLocaleString = originalNumberToLocaleString; +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/toLocaleString.js b/js/src/tests/ecma_6/TypedArray/toLocaleString.js new file mode 100644 index 000000000000..aa921f59e0e3 --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/toLocaleString.js @@ -0,0 +1,92 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); + +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, +]; + +// %TypedArrayPrototype% has an own "toLocaleString" function property. +assertEq(TypedArrayPrototype.hasOwnProperty("toLocaleString"), true); +assertEq(typeof TypedArrayPrototype.toLocaleString, "function"); + +// The initial value of %TypedArrayPrototype%.toLocaleString is not Array.prototype.toLocaleString. +assertEq(TypedArrayPrototype.toLocaleString === Array.prototype.toLocaleString, false); + +// The concrete TypedArray prototypes do not have an own "toLocaleString" property. +assertEq(constructors.every(c => !c.hasOwnProperty("toLocaleString")), true); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArrayPrototype, "toLocaleString"), { + value: TypedArrayPrototype.toLocaleString, + writable: true, + enumerable: false, + configurable: true, +}); + +assertEq(TypedArrayPrototype.toLocaleString.name, "toLocaleString"); +assertEq(TypedArrayPrototype.toLocaleString.length, 0); + +// It's not a generic method. +assertThrowsInstanceOf(() => TypedArrayPrototype.toLocaleString.call(), TypeError); +for (let invalid of [void 0, null, {}, [], function(){}, true, 0, "", Symbol()]) { + assertThrowsInstanceOf(() => TypedArrayPrototype.toLocaleString.call(invalid), TypeError); +} + +const localeOne = 1..toLocaleString(), + localeTwo = 2..toLocaleString(), + localeSep = [,,].toLocaleString(); + +for (let constructor of constructors) { + assertEq(new constructor([]).toLocaleString(), ""); + assertEq(new constructor([1]).toLocaleString(), localeOne); + assertEq(new constructor([1, 2]).toLocaleString(), localeOne + localeSep + localeTwo); +} + +const originalNumberToLocaleString = Number.prototype.toLocaleString; + +// Calls Number.prototype.toLocaleString on each element. +for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + "use strict"; + + // Ensure this-value is not boxed. + assertEq(typeof this, "number"); + + // Test ToString is applied. + return { + valueOf: () => { + throw new Error("valueOf called"); + }, + toString: () => { + return this + 10; + } + }; + }; + let typedArray = new constructor([1, 2]); + assertEq(typedArray.toLocaleString(), "11" + localeSep + "12"); +} +Number.prototype.toLocaleString = originalNumberToLocaleString; + +// Calls Number.prototype.toLocaleString from the current Realm. +const otherGlobal = newGlobal(); +for (let constructor of constructors) { + Number.prototype.toLocaleString = function() { + "use strict"; + called = true; + return this; + }; + let typedArray = new otherGlobal[constructor.name]([1]); + let called = false; + assertEq(TypedArrayPrototype.toLocaleString.call(typedArray), "1"); + assertEq(called, true); +} +Number.prototype.toLocaleString = originalNumberToLocaleString; + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/toString.js b/js/src/tests/ecma_6/TypedArray/toString.js new file mode 100644 index 000000000000..c3e90f69c814 --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/toString.js @@ -0,0 +1,48 @@ +const TypedArrayPrototype = Object.getPrototypeOf(Int8Array.prototype); + +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, +]; + +// %TypedArrayPrototype% has an own "toString" property. +assertEq(TypedArrayPrototype.hasOwnProperty("toString"), true); + +// The initial value of %TypedArrayPrototype%.toString is Array.prototype.toString. +assertEq(TypedArrayPrototype.toString, Array.prototype.toString); + +// The concrete TypedArray prototypes do not have an own "toString" property. +assertEq(constructors.every(c => !c.hasOwnProperty("toString")), true); + +assertDeepEq(Object.getOwnPropertyDescriptor(TypedArrayPrototype, "toString"), { + value: TypedArrayPrototype.toString, + writable: true, + enumerable: false, + configurable: true, +}); + +for (let constructor of constructors) { + assertEq(new constructor([]).toString(), ""); + assertEq(new constructor([1]).toString(), "1"); + assertEq(new constructor([1, 2]).toString(), "1,2"); +} + +assertEq(new Int8Array([-1, 2, -3, 4, NaN]).toString(), "-1,2,-3,4,0"); +assertEq(new Uint8Array([255, 2, 3, 4, NaN]).toString(), "255,2,3,4,0"); +assertEq(new Uint8ClampedArray([255, 256, 2, 3, 4, NaN]).toString(), "255,255,2,3,4,0"); +assertEq(new Int16Array([-1, 2, -3, 4, NaN]).toString(), "-1,2,-3,4,0"); +assertEq(new Uint16Array([-1, 2, 3, 4, NaN]).toString(), "65535,2,3,4,0"); +assertEq(new Int32Array([-1, 2, -3, 4, NaN]).toString(), "-1,2,-3,4,0"); +assertEq(new Uint32Array([-1, 2, 3, 4, NaN]).toString(), "4294967295,2,3,4,0"); +assertEq(new Float32Array([-0, 0, 0.5, -0.5, NaN, Infinity, -Infinity]).toString(), "0,0,0.5,-0.5,NaN,Infinity,-Infinity"); +assertEq(new Float64Array([-0, 0, 0.5, -0.5, NaN, Infinity, -Infinity]).toString(), "0,0,0.5,-0.5,NaN,Infinity,-Infinity"); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 1aadb26af1f4..4a6f415222ac 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1353,7 +1353,7 @@ TypedArrayObject::protoFunctions[] = { #if 0 /* disabled until perf-testing is completed */ JS_SELF_HOSTED_FN("set", "TypedArraySet", 2, 0), #else - JS_FN("set", TypedArrayObject::set, 2, 0), + JS_FN("set", TypedArrayObject::set, 1, 0), #endif JS_SELF_HOSTED_FN("copyWithin", "TypedArrayCopyWithin", 3, 0), JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 2, 0), @@ -1377,6 +1377,8 @@ TypedArrayObject::protoFunctions[] = { JS_SELF_HOSTED_FN("values", "TypedArrayValues", 0, 0), JS_SELF_HOSTED_SYM_FN(iterator, "TypedArrayValues", 0, 0), JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0), + JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0), + JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0), JS_FS_END }; diff --git a/js/xpconnect/tests/unit/test_bug1033927.js b/js/xpconnect/tests/unit/test_bug1033927.js index 8e8d31563f9f..8c3ce7c265a7 100644 --- a/js/xpconnect/tests/unit/test_bug1033927.js +++ b/js/xpconnect/tests/unit/test_bug1033927.js @@ -4,5 +4,5 @@ function run_test() { var xhr = Cu.evalInSandbox('new XMLHttpRequest()', sb); do_check_eq(xhr.toString(), '[object XMLHttpRequest]'); do_check_eq((new sb.Object()).toString(), '[object Object]'); - do_check_eq((new sb.Uint16Array()).toString(), '[object Uint16Array]'); + do_check_eq(sb.Object.prototype.toString.call(new sb.Uint16Array()), '[object Uint16Array]'); } diff --git a/toolkit/components/osfile/modules/osfile_unix_front.jsm b/toolkit/components/osfile/modules/osfile_unix_front.jsm index 0c3620b325ed..b3860aae81eb 100644 --- a/toolkit/components/osfile/modules/osfile_unix_front.jsm +++ b/toolkit/components/osfile/modules/osfile_unix_front.jsm @@ -1158,7 +1158,7 @@ date = date.getTime(); } - if (isNaN(date)) { + if (typeof date !== "number" || isNaN(date)) { throw new TypeError("|date| parameter of " + fn + " must be a " + "|Date| instance or number"); } diff --git a/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js index 4b0628f126c3..c9adc2d102a6 100644 --- a/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js +++ b/toolkit/components/promiseworker/tests/xpcshell/test_Promise.js @@ -98,7 +98,8 @@ add_task(function* test_transfer_with_meta() { Assert.equal(array.buffer.byteLength, 0, "The buffer has been detached"); // Check that the result is correct - Assert.equal(result.toString(), "[object Uint8Array]", "The result appears to be a Typed Array"); + Assert.equal(Object.prototype.toString.call(result), "[object Uint8Array]", + "The result appears to be a Typed Array"); Assert.equal(result.byteLength, 4, "The result has the right size"); for (let i = 0; i < 4; ++i) {