Bug 1121938 - Implement TypedArray.prototype.toString and .toLocaleString. r=waldo, r=froydnj

This commit is contained in:
André Bargull 2016-07-29 09:04:06 -07:00
Родитель d10c483558
Коммит 878648c8cf
16 изменённых файлов: 493 добавлений и 40 удалений

Просмотреть файл

@ -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]",
},

Просмотреть файл

@ -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];
}
/**

Просмотреть файл

@ -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();

Просмотреть файл

@ -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.

Просмотреть файл

@ -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; }

Просмотреть файл

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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);

Просмотреть файл

@ -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
};

Просмотреть файл

@ -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]');
}

Просмотреть файл

@ -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");
}

Просмотреть файл

@ -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) {