mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 09:15:35 +00:00
Bug 896608 - Implement ES6 %TypedArray%.{of, from}. r=evilpie, till
This commit is contained in:
parent
f7e80df874
commit
cc5f6453a1
@ -540,3 +540,155 @@ function TypedArrayIncludes(searchElement, fromIndex = 0) {
|
||||
// Step 11.
|
||||
return false;
|
||||
}
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.2.1 %TypedArray%.from(source[, mapfn[, thisArg]]).
|
||||
function TypedArrayStaticFrom(source, mapfn = undefined, thisArg = undefined) {
|
||||
// Step 1.
|
||||
var C = this;
|
||||
|
||||
// Step 2.
|
||||
if (!IsConstructor(C))
|
||||
ThrowError(JSMSG_NOT_CONSTRUCTOR, DecompileArg(1, C));
|
||||
|
||||
// Step 3.
|
||||
var f = mapfn;
|
||||
|
||||
// Step 4.
|
||||
if (f !== undefined && !IsCallable(f))
|
||||
ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(1, f));
|
||||
|
||||
// Steps 5-6.
|
||||
return TypedArrayFrom(C, undefined, source, f, thisArg);
|
||||
}
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.2.1.1 TypedArrayFrom().
|
||||
function TypedArrayFrom(constructor, target, items, mapfn, thisArg) {
|
||||
// Step 1.
|
||||
var C = constructor;
|
||||
|
||||
// Step 2.
|
||||
assert(C === undefined || target === undefined,
|
||||
"Neither of 'constructor' and 'target' is undefined");
|
||||
|
||||
// Step 3.
|
||||
assert(IsConstructor(C) || C === undefined,
|
||||
"'constructor' is neither an constructor nor undefined");
|
||||
|
||||
// Step 4.
|
||||
assert(target === undefined || IsTypedArray(target),
|
||||
"'target' is neither a typed array nor undefined");
|
||||
|
||||
// Step 5.
|
||||
assert(IsCallable(mapfn) || mapfn === undefined,
|
||||
"'target' is neither a function nor undefined");
|
||||
|
||||
// Steps 6-7.
|
||||
var mapping = mapfn !== undefined;
|
||||
var T = thisArg;
|
||||
|
||||
// Steps 8-9.
|
||||
var usingIterator = GetMethod(items, std_iterator);
|
||||
|
||||
// Step 10.
|
||||
if (usingIterator !== undefined) {
|
||||
// Steps 10.a-b.
|
||||
var iterator = GetIterator(items, usingIterator);
|
||||
|
||||
// Step 10.c.
|
||||
var values = new List();
|
||||
|
||||
// Steps 10.d-e.
|
||||
while (true) {
|
||||
// Steps 10.e.i-ii.
|
||||
var next = iterator.next();
|
||||
if (!IsObject(next))
|
||||
ThrowError(JSMSG_NEXT_RETURNED_PRIMITIVE);
|
||||
|
||||
// Steps 10.e.iii-vi.
|
||||
if (next.done)
|
||||
break;
|
||||
values.push(next.value);
|
||||
}
|
||||
|
||||
// Step 10.f.
|
||||
var len = values.length;
|
||||
|
||||
// Steps 10.g-h.
|
||||
// There is no need to implement the 22.2.2.1.2 - TypedArrayAllocOrInit() method,
|
||||
// since `%TypedArray%(object)` currently doesn't call this self-hosted TypedArrayFrom().
|
||||
var targetObj = new C(len);
|
||||
|
||||
// Steps 10.i-j.
|
||||
for (var k = 0; k < len; k++) {
|
||||
// Steps 10.j.i-ii.
|
||||
var kValue = values[k];
|
||||
|
||||
// Steps 10.j.iii-iv.
|
||||
var mappedValue = mapping ? callFunction(mapfn, T, kValue, k) : kValue;
|
||||
|
||||
// Steps 10.j.v-vi.
|
||||
targetObj[k] = mappedValue;
|
||||
}
|
||||
|
||||
// Step 10.k.
|
||||
// asserting that `values` is empty here would require removing them one by one from
|
||||
// the list's start in the loop above. That would introduce unacceptable overhead.
|
||||
// Additionally, the loop's logic is simple enough not to require the assert.
|
||||
|
||||
// Step 10.l.
|
||||
return targetObj;
|
||||
}
|
||||
|
||||
// Step 11 is an assertion: items is not an Iterator. Testing this is
|
||||
// literally the very last thing we did, so we don't assert here.
|
||||
|
||||
// Steps 12-13.
|
||||
var arrayLike = ToObject(items);
|
||||
|
||||
// Steps 14-16.
|
||||
var len = ToLength(arrayLike.length);
|
||||
|
||||
// Steps 17-18.
|
||||
// See comment for steps 10.g-h.
|
||||
var targetObj = new C(len);
|
||||
|
||||
// Steps 19-20.
|
||||
for (var k = 0; k < len; k++) {
|
||||
// Steps 20.a-c.
|
||||
var kValue = arrayLike[k];
|
||||
|
||||
// Steps 20.d-e.
|
||||
var mappedValue = mapping ? callFunction(mapfn, T, kValue, k) : kValue;
|
||||
|
||||
// Steps 20.f-g.
|
||||
targetObj[k] = mappedValue;
|
||||
}
|
||||
|
||||
// Step 21.
|
||||
return targetObj;
|
||||
}
|
||||
|
||||
// ES6 draft rev30 (2014/12/24) 22.2.2.2 %TypedArray%.of(...items).
|
||||
function TypedArrayStaticOf(/*...items*/) {
|
||||
// Step 1.
|
||||
var len = arguments.length;
|
||||
|
||||
// Step 2.
|
||||
var items = arguments;
|
||||
|
||||
// Step 3.
|
||||
var C = this;
|
||||
|
||||
// Steps 4-5.
|
||||
if (!IsConstructor(C))
|
||||
ThrowError(JSMSG_NOT_CONSTRUCTOR, typeof C);
|
||||
|
||||
var newObj = new C(len);
|
||||
|
||||
// Steps 6-7.
|
||||
for (var k = 0; k < len; k++)
|
||||
newObj[k] = items[k]
|
||||
|
||||
// Step 8.
|
||||
return newObj;
|
||||
}
|
||||
|
@ -43,7 +43,10 @@ var std_StopIteration = StopIteration;
|
||||
|
||||
|
||||
/* Spec: ECMAScript Language Specification, 5.1 edition, 8.8 */
|
||||
function List() {}
|
||||
function List() {
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
{
|
||||
let ListProto = std_Object_create(null);
|
||||
ListProto.indexOf = std_Array_indexOf;
|
||||
@ -103,7 +106,50 @@ function ToLength(v) {
|
||||
return std_Math_min(v, 0x1fffffffffffff);
|
||||
}
|
||||
|
||||
// Spec: ECMAScript Draft, 6th edition Oct 14, 2014, 7.2.4.
|
||||
/* Spec: ECMAScript Draft, 6th edition Oct 14, 2014, 7.2.4 */
|
||||
function SameValueZero(x, y) {
|
||||
return x === y || (x !== x && y !== y);
|
||||
}
|
||||
|
||||
/* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.3.8 */
|
||||
function GetMethod(O, P) {
|
||||
// Step 1.
|
||||
assert(IsPropertyKey(P), "Invalid property key");
|
||||
|
||||
// Steps 2-3.
|
||||
var func = ToObject(O)[P];
|
||||
|
||||
// Step 4.
|
||||
if (func === undefined || func === null)
|
||||
return undefined;
|
||||
|
||||
// Step 5.
|
||||
if (!IsCallable(func))
|
||||
ThrowError(JSMSG_NOT_FUNCTION, typeof func);
|
||||
|
||||
// Step 6.
|
||||
return func;
|
||||
}
|
||||
|
||||
/* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.2.7 */
|
||||
function IsPropertyKey(argument) {
|
||||
var type = typeof argument;
|
||||
return type === "string" || type === "symbol";
|
||||
}
|
||||
|
||||
/* Spec: ECMAScript Draft, 6th edition Dec 24, 2014, 7.4.1 */
|
||||
function GetIterator(obj, method) {
|
||||
// Steps 1-2.
|
||||
if (arguments.length === 1)
|
||||
method = GetMethod(obj, std_iterator);
|
||||
|
||||
// Steps 3-4.
|
||||
var iterator = callFunction(method, obj);
|
||||
|
||||
// Step 5.
|
||||
if (!IsObject(iterator))
|
||||
ThrowError(JSMSG_NOT_ITERABLE, ToString(iterator));
|
||||
|
||||
// Step 6.
|
||||
return iterator;
|
||||
}
|
||||
|
54
js/src/tests/ecma_6/TypedArray/from_basics.js
Normal file
54
js/src/tests/ecma_6/TypedArray/from_basics.js
Normal file
@ -0,0 +1,54 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// 'from' method is identical for all typed array constructors.
|
||||
assertEq(constructors[0].from === constructor.from, true);
|
||||
|
||||
// %TypedArray%.from copies arrays.
|
||||
var src = new constructor([1, 2, 3]), copy = constructor.from(src);
|
||||
assertEq(copy === src, false);
|
||||
assertEq(copy instanceof constructor, true);
|
||||
assertDeepEq(copy, src);
|
||||
|
||||
// Non-element properties are not copied.
|
||||
var a = new constructor([0, 1]);
|
||||
a.name = "lisa";
|
||||
assertDeepEq(constructor.from(a), new constructor([0, 1]));
|
||||
|
||||
// %TypedArray%.from can copy non-iterable objects, if they're array-like.
|
||||
src = {0: 0, 1: 1, length: 2};
|
||||
copy = constructor.from(src);
|
||||
assertEq(copy instanceof constructor, true);
|
||||
assertDeepEq(copy, new constructor([0, 1]));
|
||||
|
||||
// Properties past the .length are not copied.
|
||||
src = {0: "0", 1: "1", 2: "two", 9: "nine", name: "lisa", length: 2};
|
||||
assertDeepEq(constructor.from(src), new constructor([0, 1]));
|
||||
|
||||
// If an object has neither an @@iterator method nor .length,
|
||||
// then it's treated as zero-length.
|
||||
assertDeepEq(constructor.from({}), new constructor());
|
||||
|
||||
// Primitives will be coerced to primitive wrapper first.
|
||||
assertDeepEq(constructor.from(1), new constructor());
|
||||
assertDeepEq(constructor.from("123"), new constructor([1, 2, 3]));
|
||||
assertDeepEq(constructor.from(true), new constructor());
|
||||
assertDeepEq(constructor.from(Symbol()), new constructor());
|
||||
|
||||
// Source object property order doesn't matter.
|
||||
src = {length: 2, 1: "1", 0: "0"};
|
||||
assertDeepEq(constructor.from(src), new constructor([0, 1]));
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
79
js/src/tests/ecma_6/TypedArray/from_constructor.js
Normal file
79
js/src/tests/ecma_6/TypedArray/from_constructor.js
Normal file
@ -0,0 +1,79 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// %TypedArray%.from can be applied to any constructor.
|
||||
// For example, the Date builtin constructor.
|
||||
// Unlike Array.from, %TypedArray%.from doesn't set the 'length' property at the end.
|
||||
var d = constructor.from.call(Date, ["A", "B"]);
|
||||
assertEq(d instanceof constructor, false);
|
||||
assertEq(Object.prototype.toString.call(d), "[object Date]");
|
||||
assertEq(Object.getPrototypeOf(d), Date.prototype);
|
||||
assertEq(d.length, undefined);
|
||||
assertEq(d[0], "A");
|
||||
assertEq(d[1], "B");
|
||||
|
||||
// Or RegExp.
|
||||
var obj = constructor.from.call(RegExp, [1]);
|
||||
assertEq(obj instanceof constructor, false);
|
||||
assertEq(Object.getPrototypeOf(obj), RegExp.prototype);
|
||||
assertEq(Object.getOwnPropertyNames(obj).join(","),
|
||||
"0,lastIndex,source,global,ignoreCase,multiline,sticky");
|
||||
assertEq(obj.length, undefined);
|
||||
|
||||
// Or any JS function.
|
||||
function C(arg) {
|
||||
this.args = arguments;
|
||||
}
|
||||
var c = constructor.from.call(C, {length: 1, 0: "zero"});
|
||||
assertEq(c instanceof C, true);
|
||||
assertEq(c.args.length, 1);
|
||||
assertEq(c.args[0], 1);
|
||||
assertEq(c.length, undefined);
|
||||
assertEq(c[0], "zero");
|
||||
|
||||
// Note %TypedArray%.from(iterable) calls 'this' with an argument whose value is
|
||||
// `[...iterable].length`, while Array.from(iterable) doesn't pass any argument.
|
||||
assertEq(constructor.from.call(Object, []) instanceof Number, true);
|
||||
assertDeepEq(constructor.from.call(Object, []), new Number(0));
|
||||
assertEq(constructor.from.call(Number,[1, , "a"]) + 1, 4);
|
||||
constructor.from.call(function(len){
|
||||
assertEq(len, 3);
|
||||
}, Array(3));
|
||||
|
||||
// If the 'this' value passed to %TypedArray.from is not a constructor,
|
||||
// then an exception is thrown, while Array.from will use Array as it's constructor.
|
||||
var arr = [3, 4, 5];
|
||||
var nonconstructors = [
|
||||
{}, Math, Object.getPrototypeOf, undefined, 17,
|
||||
() => ({}) // arrow functions are not constructors
|
||||
];
|
||||
for (var v of nonconstructors) {
|
||||
assertThrowsInstanceOf(() => {
|
||||
constructor.from.call(v, arr);
|
||||
}, TypeError);
|
||||
}
|
||||
|
||||
// %TypedArray%.from does not get confused if global constructors for typed arrays
|
||||
// are replaced with another constructor.
|
||||
function NotArray() {
|
||||
}
|
||||
var RealArray = constructor;
|
||||
NotArray.from = constructor.from;
|
||||
this[constructor.name] = NotArray;
|
||||
assertEq(RealArray.from([1]) instanceof RealArray, true);
|
||||
assertEq(NotArray.from([1]) instanceof NotArray, true);
|
||||
this[constructor.name] = RealArray;
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
109
js/src/tests/ecma_6/TypedArray/from_errors.js
Normal file
109
js/src/tests/ecma_6/TypedArray/from_errors.js
Normal file
@ -0,0 +1,109 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// %TypedArray%.from throws if the argument is undefined or null.
|
||||
assertThrowsInstanceOf(() => constructor.from(), TypeError);
|
||||
assertThrowsInstanceOf(() => constructor.from(undefined), TypeError);
|
||||
assertThrowsInstanceOf(() => constructor.from(null), TypeError);
|
||||
|
||||
// %TypedArray%.from throws if an element can't be defined on the new object.
|
||||
function ObjectWithReadOnlyElement() {
|
||||
Object.defineProperty(this, "0", {value: null});
|
||||
this.length = 0;
|
||||
}
|
||||
ObjectWithReadOnlyElement.from = constructor.from;
|
||||
assertDeepEq(ObjectWithReadOnlyElement.from([]), new ObjectWithReadOnlyElement);
|
||||
assertThrowsInstanceOf(() => ObjectWithReadOnlyElement.from([1]), TypeError);
|
||||
|
||||
// The same, but via preventExtensions.
|
||||
function InextensibleObject() {
|
||||
Object.preventExtensions(this);
|
||||
}
|
||||
InextensibleObject.from = constructor.from;
|
||||
assertThrowsInstanceOf(() => InextensibleObject.from([1]), TypeError);
|
||||
|
||||
// The same, but via a readonly property on its __proto__.
|
||||
function ObjectWithReadOnlyElementOnProto() {
|
||||
return Object.create({
|
||||
get 0(){}
|
||||
});
|
||||
}
|
||||
ObjectWithReadOnlyElementOnProto.from = constructor.from;
|
||||
assertThrowsInstanceOf(() => ObjectWithReadOnlyElementOnProto.from([1]), TypeError);
|
||||
|
||||
// Unlike Array.from, %TypedArray%.from doesn't get or set the length property.
|
||||
function ObjectWithThrowingLengthGetterSetter() {
|
||||
Object.defineProperty(this, "length", {
|
||||
configurable: true,
|
||||
get() { throw new RangeError("getter!"); },
|
||||
set() { throw new RangeError("setter!"); }
|
||||
});
|
||||
}
|
||||
ObjectWithThrowingLengthGetterSetter.from = constructor.from;
|
||||
assertEq(ObjectWithThrowingLengthGetterSetter.from(["foo"])[0], "foo");
|
||||
|
||||
// %TypedArray%.from throws if mapfn is neither callable nor undefined.
|
||||
assertThrowsInstanceOf(() => constructor.from([3, 4, 5], {}), TypeError);
|
||||
assertThrowsInstanceOf(() => constructor.from([3, 4, 5], "also not a function"), TypeError);
|
||||
assertThrowsInstanceOf(() => constructor.from([3, 4, 5], null), TypeError);
|
||||
|
||||
// Even if the function would not have been called.
|
||||
assertThrowsInstanceOf(() => constructor.from([], JSON), TypeError);
|
||||
|
||||
// If mapfn is not undefined and not callable, the error happens before anything else.
|
||||
// Before calling the constructor, before touching the arrayLike.
|
||||
var log = "";
|
||||
function C() {
|
||||
log += "C";
|
||||
obj = this;
|
||||
}
|
||||
var p = new Proxy({}, {
|
||||
has: function () { log += "1"; },
|
||||
get: function () { log += "2"; },
|
||||
getOwnPropertyDescriptor: function () { log += "3"; }
|
||||
});
|
||||
assertThrowsInstanceOf(() => constructor.from.call(C, p, {}), TypeError);
|
||||
assertEq(log, "");
|
||||
|
||||
// If mapfn throws, the new object has already been created.
|
||||
var arrayish = {
|
||||
get length() { log += "l"; return 1; },
|
||||
get 0() { log += "0"; return "q"; }
|
||||
};
|
||||
log = "";
|
||||
var exc = {surprise: "ponies"};
|
||||
assertThrowsValue(() => constructor.from.call(C, arrayish, () => { throw exc; }), exc);
|
||||
assertEq(log, "lC0");
|
||||
assertEq(obj instanceof C, true);
|
||||
|
||||
// It's a TypeError if the @@iterator property is a primitive (except null and undefined).
|
||||
for (var primitive of ["foo", 17, Symbol(), true]) {
|
||||
assertThrowsInstanceOf(() => constructor.from({[Symbol.iterator] : primitive}), TypeError);
|
||||
}
|
||||
assertDeepEq(constructor.from({[Symbol.iterator]: null}), new constructor());
|
||||
assertDeepEq(constructor.from({[Symbol.iterator]: undefined}), new constructor());
|
||||
|
||||
// It's a TypeError if the iterator's .next() method returns a primitive.
|
||||
for (var primitive of [undefined, null, "foo", 17, Symbol(), true]) {
|
||||
assertThrowsInstanceOf(
|
||||
() => constructor.from({
|
||||
[Symbol.iterator]() {
|
||||
return {next() { return primitive; }};
|
||||
}
|
||||
}),
|
||||
TypeError);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
61
js/src/tests/ecma_6/TypedArray/from_iterable.js
Normal file
61
js/src/tests/ecma_6/TypedArray/from_iterable.js
Normal file
@ -0,0 +1,61 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// %TypedArray%.from works on arguments objects.
|
||||
(function () {
|
||||
assertDeepEq(constructor.from(arguments), new constructor(["0", "1", undefined]));
|
||||
})("0", "1", undefined);
|
||||
|
||||
// If an object has both .length and [@@iterator] properties, [@@iterator] is used.
|
||||
var a = ['0', '1', '2', '3', '4'];
|
||||
a[Symbol.iterator] = function* () {
|
||||
for (var i = 5; i--; )
|
||||
yield this[i];
|
||||
};
|
||||
|
||||
var log = '';
|
||||
function f(x) {
|
||||
log += x;
|
||||
return x + x;
|
||||
}
|
||||
|
||||
var b = constructor.from(a, f);
|
||||
assertDeepEq(b, new constructor(['44', '33', '22', '11', '00']));
|
||||
assertEq(log, '43210');
|
||||
|
||||
// In fact, if [@@iterator] is present, .length isn't queried at all.
|
||||
var pa = new Proxy(a, {
|
||||
has: function (target, id) {
|
||||
if (id === "length")
|
||||
throw new Error(".length should not be queried (has)");
|
||||
return id in target;
|
||||
},
|
||||
get: function (target, id) {
|
||||
if (id === "length")
|
||||
throw new Error(".length should not be queried (get)");
|
||||
return target[id];
|
||||
},
|
||||
getOwnPropertyDescriptor: function (target, id) {
|
||||
if (id === "length")
|
||||
throw new Error(".length should not be queried (getOwnPropertyDescriptor)");
|
||||
return Object.getOwnPropertyDescriptor(target, id)
|
||||
}
|
||||
});
|
||||
log = "";
|
||||
b = constructor.from(pa, f);
|
||||
assertDeepEq(b, new constructor(['44', '33', '22', '11', '00']));
|
||||
assertEq(log, '43210');
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
56
js/src/tests/ecma_6/TypedArray/from_mapping.js
Normal file
56
js/src/tests/ecma_6/TypedArray/from_mapping.js
Normal file
@ -0,0 +1,56 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// If the mapfn argument to %TypedArray%.from is undefined, don't map.
|
||||
assertDeepEq(constructor.from([3, 4, 5], undefined), new constructor([3, 4, 5]));
|
||||
assertDeepEq(constructor.from([4, 5, 6], undefined, Math), new constructor([4, 5, 6]));
|
||||
|
||||
// mapfn is called with two arguments: value and index.
|
||||
var log = [];
|
||||
function f(...args) {
|
||||
log.push(args);
|
||||
return log.length;
|
||||
}
|
||||
assertDeepEq(constructor.from(['a', 'e', 'i', 'o', 'u'], f), new constructor([1, 2, 3, 4, 5]));
|
||||
assertDeepEq(log, [['a', 0], ['e', 1], ['i', 2], ['o', 3], ['u', 4]]);
|
||||
|
||||
// If the object to be copied is non-iterable, mapfn is still called with two
|
||||
// arguments.
|
||||
log = [];
|
||||
assertDeepEq(constructor.from({0: "zero", 1: "one", length: 2}, f), new constructor([1, 2]));
|
||||
assertDeepEq(log, [["zero", 0], ["one", 1]]);
|
||||
|
||||
// If the object to be copied is iterable and the constructor is not Array,
|
||||
// mapfn is still called with two arguments.
|
||||
log = [];
|
||||
function C() {}
|
||||
C.from = constructor.from;
|
||||
var c = new C;
|
||||
c[0] = 1;
|
||||
c[1] = 2;
|
||||
assertDeepEq(C.from(["zero", "one"], f), c);
|
||||
assertDeepEq(log, [["zero", 0], ["one", 1]]);
|
||||
|
||||
// The mapfn is called even if the value to be mapped is undefined.
|
||||
assertDeepEq(constructor.from([0, 1, , 3], String), new constructor(["0", "1", "undefined", "3"]));
|
||||
var arraylike = {length: 4, "0": 0, "1": 1, "3": 3};
|
||||
assertDeepEq(constructor.from(arraylike, String), new constructor(["0", "1", "undefined", "3"]));
|
||||
}
|
||||
|
||||
// %TypedArray%.from(obj, map) is not exactly the same as %TypedArray%.from(obj).map(mapFn).
|
||||
assertDeepEq(Int8Array.from([150], v => v / 2), new Int8Array([75]));
|
||||
// Uncomment the following line when we implement the .map method.
|
||||
// assertDeepEq(Int8Array.from([150]).map(v => v / 2), new Int8Array([-53]));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
69
js/src/tests/ecma_6/TypedArray/from_proxy.js
Normal file
69
js/src/tests/ecma_6/TypedArray/from_proxy.js
Normal file
@ -0,0 +1,69 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// Two tests involving %TypedArray%.from and a Proxy.
|
||||
var log = [];
|
||||
function LoggingProxy(target) {
|
||||
log.push("target", target);
|
||||
var h = {
|
||||
defineProperty: function (t, id) {
|
||||
log.push("define", id);
|
||||
return undefined;
|
||||
},
|
||||
has: function (t, id) {
|
||||
log.push("has", id);
|
||||
return id in t;
|
||||
},
|
||||
get: function (t, id) {
|
||||
log.push("get", id);
|
||||
return t[id];
|
||||
},
|
||||
set: function (t, id, v) {
|
||||
log.push("set", id);
|
||||
t[id] = v;
|
||||
}
|
||||
};
|
||||
return new Proxy(Object(target), h);
|
||||
}
|
||||
|
||||
// Unlike Array.from, %TypedArray%.from uses [[Put]] instead of [[DefineOwnProperty]].
|
||||
// Hence, it calls handler.set to create new elements rather than handler.defineProperty.
|
||||
// Additionally, it doesn't set the length property at the end.
|
||||
LoggingProxy.from = constructor.from;
|
||||
LoggingProxy.from([3, 4, 5]);
|
||||
assertDeepEq(log, ["target", 3, "set", "0", "set", "1", "set", "2"]);
|
||||
|
||||
// When the argument passed to %TypedArray%.from is a Proxy, %TypedArray%.from
|
||||
// calls handler.get on it.
|
||||
log = [];
|
||||
assertDeepEq(constructor.from(new LoggingProxy([3, 4, 5])), new constructor([3, 4, 5]));
|
||||
assertDeepEq(log, ["target", [3, 4, 5],
|
||||
"get", Symbol.iterator,
|
||||
"get", "length", "get", "0",
|
||||
"get", "length", "get", "1",
|
||||
"get", "length", "get", "2",
|
||||
"get", "length"]);
|
||||
|
||||
// Array-like iteration only gets the length once.
|
||||
log = [];
|
||||
var arr = [5, 6, 7];
|
||||
arr[Symbol.iterator] = undefined;
|
||||
assertDeepEq(constructor.from(new LoggingProxy(arr)), new constructor([5, 6, 7]));
|
||||
assertDeepEq(log, ["target", [5, 6, 7],
|
||||
"get", Symbol.iterator,
|
||||
"get", "length",
|
||||
"get", "0", "get", "1", "get", "2"]);
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
44
js/src/tests/ecma_6/TypedArray/from_realms.js
Normal file
44
js/src/tests/ecma_6/TypedArray/from_realms.js
Normal file
@ -0,0 +1,44 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
if (typeof newGlobal !== 'function')
|
||||
break;
|
||||
|
||||
// G[constructor.name].from, where G is any global, produces an array whose prototype
|
||||
// is G[constructor.name].prototype.
|
||||
var g = newGlobal();
|
||||
var ga = g[constructor.name].from([1, 2, 3]);
|
||||
assertEq(ga instanceof g[constructor.name], true);
|
||||
|
||||
// %TypedArray%.from can be applied to a constructor from another realm.
|
||||
var p = constructor.from.call(g[constructor.name], [1, 2, 3]);
|
||||
assertEq(p instanceof g[constructor.name], true);
|
||||
var q = g[constructor.name].from.call(constructor, [3, 4, 5]);
|
||||
assertEq(q instanceof constructor, true);
|
||||
|
||||
// The default 'this' value received by a non-strict mapping function is
|
||||
// that function's global, not %TypedArray%.from's global or the caller's global.
|
||||
var h = newGlobal(), result = undefined;
|
||||
h.mainGlobal = this;
|
||||
h.eval("function f() { mainGlobal.result = this; }");
|
||||
g[constructor.name].from.call(constructor, [5, 6, 7], h.f);
|
||||
// (Give each global in the test a name, for better error messages. But use
|
||||
// globalName, because window.name is complicated.)
|
||||
this.globalName = "main";
|
||||
g.globalName = "g";
|
||||
h.globalName = "h";
|
||||
assertEq(result.globalName, "h");
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
23
js/src/tests/ecma_6/TypedArray/from_string.js
Normal file
23
js/src/tests/ecma_6/TypedArray/from_string.js
Normal file
@ -0,0 +1,23 @@
|
||||
// %TypedArray%.from called on Array should also handle strings correctly.
|
||||
var from = Int8Array.from.bind(Array);
|
||||
|
||||
// %TypedArray%.from on a string iterates over the string.
|
||||
assertDeepEq(from("test string"),
|
||||
['t', 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g']);
|
||||
|
||||
// %TypedArray%.from on a string handles surrogate pairs correctly.
|
||||
var gclef = "\uD834\uDD1E"; // U+1D11E MUSICAL SYMBOL G CLEF
|
||||
assertDeepEq(from(gclef), [gclef]);
|
||||
assertDeepEq(from(gclef + " G"), [gclef, " ", "G"]);
|
||||
|
||||
// %TypedArray%.from on a string calls the @@iterator method.
|
||||
String.prototype[Symbol.iterator] = function* () { yield 1; yield 2; };
|
||||
assertDeepEq(from("anything"), [1, 2]);
|
||||
|
||||
// If the iterator method is deleted, Strings are still arraylike.
|
||||
delete String.prototype[Symbol.iterator];
|
||||
assertDeepEq(from("works"), ['w', 'o', 'r', 'k', 's']);
|
||||
assertDeepEq(from(gclef), ['\uD834', '\uDD1E']);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
24
js/src/tests/ecma_6/TypedArray/from_surfaces.js
Normal file
24
js/src/tests/ecma_6/TypedArray/from_surfaces.js
Normal file
@ -0,0 +1,24 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// Check superficial features of %TypeArray%.from.
|
||||
var desc = Object.getOwnPropertyDescriptor(constructor.__proto__, "from");
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.enumerable, false);
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(constructor.from.length, 1);
|
||||
assertThrowsInstanceOf(() => new constructor.from(), TypeError); // not a constructor
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
71
js/src/tests/ecma_6/TypedArray/from_this.js
Normal file
71
js/src/tests/ecma_6/TypedArray/from_this.js
Normal file
@ -0,0 +1,71 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
// The third argument to %TypedArray%.from is passed as the 'this' value to the
|
||||
// mapping function.
|
||||
var hits = 0, obj = {};
|
||||
function f(x) {
|
||||
assertEq(this, obj);
|
||||
hits++;
|
||||
}
|
||||
constructor.from(["a", "b", "c"], f, obj);
|
||||
assertEq(hits, 3);
|
||||
|
||||
// Without an argument, undefined is passed...
|
||||
hits = 0;
|
||||
function gs(x) {
|
||||
"use strict";
|
||||
assertEq(this, undefined);
|
||||
hits++;
|
||||
}
|
||||
constructor.from("def", gs);
|
||||
assertEq(hits, 3);
|
||||
|
||||
// ...and if the mapping function is non-strict, that means the global is
|
||||
// passed.
|
||||
var global = this;
|
||||
hits = 0;
|
||||
function g(x) {
|
||||
assertEq(this, global);
|
||||
hits++;
|
||||
}
|
||||
constructor.from("ghi", g);
|
||||
assertEq(hits, 3);
|
||||
|
||||
// A primitive value can be passed.
|
||||
for (var v of [0, "str", undefined]) {
|
||||
hits = 0;
|
||||
var mapfn = function h(x) {
|
||||
"use strict";
|
||||
assertEq(this, v);
|
||||
hits++;
|
||||
};
|
||||
constructor.from("pq", mapfn, v);
|
||||
assertEq(hits, 2);
|
||||
}
|
||||
|
||||
// ...and if the mapping function is non-strict, primitive values will
|
||||
// be wrapped to objects.
|
||||
for (var v of [0, "str", true]) {
|
||||
hits = 0;
|
||||
var mapfn = function h(x) {
|
||||
assertDeepEq(this, Object(v));
|
||||
hits++;
|
||||
};
|
||||
constructor.from("pq", mapfn, v);
|
||||
assertEq(hits, 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
100
js/src/tests/ecma_6/TypedArray/of.js
Normal file
100
js/src/tests/ecma_6/TypedArray/of.js
Normal file
@ -0,0 +1,100 @@
|
||||
const constructors = [
|
||||
Int8Array,
|
||||
Uint8Array,
|
||||
Uint8ClampedArray,
|
||||
Int16Array,
|
||||
Uint16Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
Float32Array,
|
||||
Float64Array
|
||||
];
|
||||
|
||||
for (var constructor of constructors) {
|
||||
assertEq(constructor.of.length, 0);
|
||||
|
||||
assertDeepEq(Object.getOwnPropertyDescriptor(constructor.__proto__, "of"), {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: constructor.of,
|
||||
writable: true
|
||||
});
|
||||
|
||||
// Basic tests.
|
||||
assertEq(constructor.of().constructor, constructor);
|
||||
assertEq(constructor.of() instanceof constructor, true);
|
||||
assertDeepEq(constructor.of(10), new constructor([10]));
|
||||
assertDeepEq(constructor.of(1, 2, 3), new constructor([1, 2, 3]));
|
||||
assertDeepEq(constructor.of("1", "2", "3"), new constructor([1, 2, 3]));
|
||||
|
||||
// This method can be transplanted to other constructors.
|
||||
assertDeepEq(constructor.of.call(Array, 1, 2, 3), [1, 2, 3]);
|
||||
|
||||
var hits = 0;
|
||||
assertDeepEq(constructor.of.call(function(len) {
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(len, 3);
|
||||
hits++;
|
||||
return {};
|
||||
}, "a", "b", "c"), {
|
||||
0: "a",
|
||||
1: "b",
|
||||
2: "c"
|
||||
});
|
||||
assertEq(hits, 1);
|
||||
|
||||
// Behavior across compartments.
|
||||
if (typeof newGlobal === "function") {
|
||||
var newC = newGlobal()[constructor.name];
|
||||
assertEq(newC.of() instanceof newC, true);
|
||||
assertEq(newC.of() instanceof constructor, false);
|
||||
assertEq(newC.of.call(constructor) instanceof constructor, true);
|
||||
}
|
||||
|
||||
// Throws if `this` isn't a constructor.
|
||||
var invalidConstructors = [undefined, null, 1, false, "", Symbol(), [], {}, /./,
|
||||
constructor.of, () => {}];
|
||||
invalidConstructors.forEach(C => {
|
||||
assertThrowsInstanceOf(() => {
|
||||
constructor.of.call(C);
|
||||
}, TypeError);
|
||||
});
|
||||
|
||||
// FIXME: Should throw if `this` is a method definition or a getter/setter function, see bug 1059908.
|
||||
constructor.of.call({method() {}}.method);
|
||||
constructor.of.call(Object.getOwnPropertyDescriptor({get getter() {}}, "getter").get);
|
||||
|
||||
// Generators are also legal constructors.
|
||||
assertEq(constructor.of.call(function*(len) {
|
||||
return len;
|
||||
}, "a", "b", "c").next().value, 3);
|
||||
|
||||
// An exception might be thrown in a strict assignment to the new object's indexed properties.
|
||||
assertThrowsInstanceOf(() => {
|
||||
constructor.of.call(function() {
|
||||
return {get 0() {}};
|
||||
}, "a");
|
||||
}, TypeError);
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
constructor.of.call(function() {
|
||||
return Object("1");
|
||||
}, "a");
|
||||
}, TypeError);
|
||||
|
||||
assertThrowsInstanceOf(() => {
|
||||
constructor.of.call(function() {
|
||||
return Object.create({
|
||||
set 0(v) {
|
||||
throw new TypeError;
|
||||
}
|
||||
});
|
||||
}, "a");
|
||||
}, TypeError);
|
||||
}
|
||||
|
||||
assertDeepEq(Float32Array.of(0.1, null, undefined, NaN), new Float32Array([0.1, 0, NaN, NaN]));
|
||||
assertDeepEq(Float64Array.of(0.1, null, undefined, NaN), new Float64Array([0.1, 0, NaN, NaN]));
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
@ -833,7 +833,8 @@ TypedArrayObject::protoFunctions[] = {
|
||||
|
||||
/* static */ const JSFunctionSpec
|
||||
TypedArrayObject::staticFunctions[] = {
|
||||
// Coming soon...
|
||||
JS_SELF_HOSTED_FN("from", "TypedArrayStaticFrom", 3, 0),
|
||||
JS_SELF_HOSTED_FN("of", "TypedArrayStaticOf", 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user