diff options
Diffstat (limited to 'js/src/tests/non262/Iterator')
186 files changed, 4703 insertions, 0 deletions
diff --git a/js/src/tests/non262/Iterator/constructor-subclassable.js b/js/src/tests/non262/Iterator/constructor-subclassable.js new file mode 100644 index 0000000000..9f65c65ad0 --- /dev/null +++ b/js/src/tests/non262/Iterator/constructor-subclassable.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator constructor can be subclassed. +---*/ + +class TestIterator extends Iterator { +} + +assertEq(new TestIterator() instanceof Iterator, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/constructor-throw-when-called-directly.js b/js/src/tests/non262/Iterator/constructor-throw-when-called-directly.js new file mode 100644 index 0000000000..2b49b2d4ba --- /dev/null +++ b/js/src/tests/non262/Iterator/constructor-throw-when-called-directly.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator constructor throws when called directly. +---*/ + +assertThrowsInstanceOf(() => new Iterator(), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/constructor-throw-without-new.js b/js/src/tests/non262/Iterator/constructor-throw-without-new.js new file mode 100644 index 0000000000..85497fcd63 --- /dev/null +++ b/js/src/tests/non262/Iterator/constructor-throw-without-new.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator constructor throws when called without new. +---*/ + +assertThrowsInstanceOf(() => Iterator(), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/constructor.js b/js/src/tests/non262/Iterator/constructor.js new file mode 100644 index 0000000000..56d8271e82 --- /dev/null +++ b/js/src/tests/non262/Iterator/constructor.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The Iterator constructor is a built-in function. +---*/ + +assertEq(typeof Iterator, 'function'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/Iterator.from-descriptor.js b/js/src/tests/non262/Iterator/from/Iterator.from-descriptor.js new file mode 100644 index 0000000000..19bdde852b --- /dev/null +++ b/js/src/tests/non262/Iterator/from/Iterator.from-descriptor.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.from +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator, 'from'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/Iterator.from-length.js b/js/src/tests/non262/Iterator/from/Iterator.from-length.js new file mode 100644 index 0000000000..0833538da1 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/Iterator.from-length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.from. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.from, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/Iterator.from-name.js b/js/src/tests/non262/Iterator/from/Iterator.from-name.js new file mode 100644 index 0000000000..6f48d3e891 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/Iterator.from-name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.from. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.from, 'name'); +assertEq(propDesc.value, 'from'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/call-from-with-different-this.js b/js/src/tests/non262/Iterator/from/call-from-with-different-this.js new file mode 100644 index 0000000000..90328d117d --- /dev/null +++ b/js/src/tests/non262/Iterator/from/call-from-with-different-this.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const iter = { + next: () => ({done: false, value: 0}), +}; +const wrap = Iterator.from.call(undefined, iter); + +const result = wrap.next(); +assertEq(result.done, false); +assertEq(result.value, 0); + +const returnResult = wrap.return(1); +assertEq(returnResult.done, true); +assertEq(returnResult.value, 1); + +assertThrowsInstanceOf(() => wrap.throw(new Error()), Error); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/iterator-not-callable-throws.js b/js/src/tests/non262/Iterator/from/iterator-not-callable-throws.js new file mode 100644 index 0000000000..a7c98f9e2a --- /dev/null +++ b/js/src/tests/non262/Iterator/from/iterator-not-callable-throws.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator.from throws when called with an object with a non-callable @@iterator property. +---*/ + +assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: 0 }), TypeError); +assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: false }), TypeError); +assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: "" }), TypeError); +assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: {} }), TypeError); +assertThrowsInstanceOf(() => Iterator.from({ [Symbol.iterator]: Symbol('') }), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/modify-next.js b/js/src/tests/non262/Iterator/from/modify-next.js new file mode 100644 index 0000000000..d347abd2cd --- /dev/null +++ b/js/src/tests/non262/Iterator/from/modify-next.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const iter = { + next: () => ({ done: false, value: 0 }), +}; + +const wrap = Iterator.from(iter); + +iter.next = () => ({ done: true, value: undefined }); + +let {done, value} = wrap.next(); +assertEq(done, false); +assertEq(value, 0); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/modify-return.js b/js/src/tests/non262/Iterator/from/modify-return.js new file mode 100644 index 0000000000..733eec69d7 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/modify-return.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const iter = { + next: () => ({ done: false, value: 0 }), + return: (value) => ({ done: true, value }), +}; + +const wrap = Iterator.from(iter); + +let {done, value} = wrap.return(1); +assertEq(done, true); +assertEq(value, 1); + +iter.return = () => { throw new Error(); }; +assertThrowsInstanceOf(wrap.return, Error); + +iter.return = null; +let nullResult = wrap.return(2); +assertEq(nullResult.done, true); +assertEq(nullResult.value, 2); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/modify-throw.js b/js/src/tests/non262/Iterator/from/modify-throw.js new file mode 100644 index 0000000000..0db7cf6f61 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/modify-throw.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const iter = { + next: () => ({ done: false, value: 0 }), + throw: (value) => ({ done: true, value }), +}; + +const wrap = Iterator.from(iter); + +let {done, value} = wrap.throw(0); +assertEq(done, true); +assertEq(value, 0); + +class TestError extends Error {} +iter.throw = () => { throw new TestError(); }; +assertThrowsInstanceOf(() => wrap.throw(), TestError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/o-not-object-throws.js b/js/src/tests/non262/Iterator/from/o-not-object-throws.js new file mode 100644 index 0000000000..3e6c00e59a --- /dev/null +++ b/js/src/tests/non262/Iterator/from/o-not-object-throws.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator.from throws when called with a non-object. +---*/ + +assertThrowsInstanceOf(() => Iterator.from(undefined), TypeError); +assertThrowsInstanceOf(() => Iterator.from(null), TypeError); +assertThrowsInstanceOf(() => Iterator.from(0), TypeError); +assertThrowsInstanceOf(() => Iterator.from(false), TypeError); +assertThrowsInstanceOf(() => Iterator.from(Symbol('')), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/proxy-not-wrapped.js b/js/src/tests/non262/Iterator/from/proxy-not-wrapped.js new file mode 100644 index 0000000000..df38e0fc86 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/proxy-not-wrapped.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + + const item = Reflect[key](...args); + if (typeof item === 'function') + return (...args) => new Proxy(item.apply(receiver, args), handlerProxy); + return item; + }, +}); + +class Iter extends Iterator { + [Symbol.iterator]() { + return this; + } + next() { + return { done: false, value: 0 }; + } +} +const iter = new Iter(); +const proxy = new Proxy(iter, handlerProxy); +const wrap = Iterator.from(proxy); + +assertEq( + log.join('\n'), + `get: Symbol(Symbol.iterator) +get: next +getPrototypeOf: undefined` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/proxy-wrap-next.js b/js/src/tests/non262/Iterator/from/proxy-wrap-next.js new file mode 100644 index 0000000000..7630535402 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/proxy-wrap-next.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1].toString()}`); + + const item = Reflect[key](...args); + if (typeof item === 'function') + return item.bind(receiver); + return item; + }, +}); +const iter = new Proxy({ + next: () => ({ done: false, value: 0 }), +}, handlerProxy); + +const wrap = Iterator.from(iter); +// Call next multiple times. Should not call `get` on proxy. +wrap.next(); +wrap.next(); +wrap.next(); + +assertEq( + log.join('\n'), + `get: Symbol(Symbol.iterator) +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/proxy-wrap-return.js b/js/src/tests/non262/Iterator/from/proxy-wrap-return.js new file mode 100644 index 0000000000..34caeb2176 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/proxy-wrap-return.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1].toString()}`); + + const item = Reflect[key](...args); + if (typeof item === 'function') + return item.bind(receiver); + return item; + }, +}); +const iter = new Proxy({ + next: () => ({ done: false, value: 0 }), + return: (value) => ({ done: true, value }), +}, handlerProxy); + +const wrap = Iterator.from(iter); +wrap.return(); +wrap.return(); + +assertEq( + log.join('\n'), + `get: Symbol(Symbol.iterator) +get: next +get: return +get: return` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/proxy-wrap-throw.js b/js/src/tests/non262/Iterator/from/proxy-wrap-throw.js new file mode 100644 index 0000000000..92a51f4a26 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/proxy-wrap-throw.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1].toString()}`); + + const item = Reflect[key](...args); + if (typeof item === 'function') + return item.bind(receiver); + return item; + }, +}); +const iter = new Proxy({ + next: () => ({ done: false, value: 0 }), + throw: (value) => ({ done: true, value }), +}, handlerProxy); + +const wrap = Iterator.from(iter); +wrap.throw(); +wrap.throw(); + +assertEq( + log.join('\n'), + `get: Symbol(Symbol.iterator) +get: next +get: throw +get: throw` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/return-iterator-if-iterable.js b/js/src/tests/non262/Iterator/from/return-iterator-if-iterable.js new file mode 100644 index 0000000000..e83e25185d --- /dev/null +++ b/js/src/tests/non262/Iterator/from/return-iterator-if-iterable.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator.from returns O if it is iterable, an iterator, and an instance of Iterator. +---*/ + +class TestIterator extends Iterator { + [Symbol.iterator]() { + return this; + } + + next() { + return { done: false, value: this.value++ }; + } + + value = 0; +} + +const iter = new TestIterator(); +assertEq(iter, Iterator.from(iter)); + +const arrayIter = [1, 2, 3][Symbol.iterator](); +assertEq(arrayIter, Iterator.from(arrayIter)); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/return-wrapper-if-not-iterable.js b/js/src/tests/non262/Iterator/from/return-wrapper-if-not-iterable.js new file mode 100644 index 0000000000..09b974f327 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/return-wrapper-if-not-iterable.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator.from returns an iterator wrapper if O is not an iterable. +---*/ + +class TestIterator { + next() { + return { done: false, value: 0 }; + } +} + +const iter = new TestIterator(); +assertEq( + Symbol.iterator in iter, + false, + 'iter is not an iterable.' +); + +const wrapper = Iterator.from(iter); +assertEq(iter !== wrapper, true); +assertEq( + Symbol.iterator in wrapper, + true, + 'wrapper is an iterable.' +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/return-wrapper-if-not-iterator-instance.js b/js/src/tests/non262/Iterator/from/return-wrapper-if-not-iterator-instance.js new file mode 100644 index 0000000000..4045801277 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/return-wrapper-if-not-iterator-instance.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Iterator.from returns an iterator wrapper if O is not an instance of Iterator. +---*/ + +class TestIterator { + [Symbol.iterator]() { + return this; + } + + next() { + return { done: false, value: 0 }; + } +} + +const iter = new TestIterator(); +assertEq(iter instanceof Iterator, false); + +const wrapper = Iterator.from(iter); +assertEq(iter !== wrapper, true); +assertEq(wrapper instanceof Iterator, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-functions-on-other-global.js b/js/src/tests/non262/Iterator/from/wrap-functions-on-other-global.js new file mode 100644 index 0000000000..186e4e62d0 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-functions-on-other-global.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +class TestError extends Error {} + +function checkIterResult({done, value}, expectedDone, expectedValue) { + assertEq(done, expectedDone); + assertEq(value, expectedValue); +} + +const iter = { + next(value) { + return {done: false, value: arguments.length}; + }, + return() { + throw new TestError(); + }, + throw: (value) => ({done: true, value}), +}; +const thisWrap = Iterator.from(iter); +const otherGlobal = newGlobal({newCompartment: true}); +const otherWrap = otherGlobal.Iterator.from(iter); + +checkIterResult(thisWrap.next.call(otherWrap), false, 0); +checkIterResult(thisWrap.next.call(otherWrap, 'value'), false, 1); + +assertThrowsInstanceOf(thisWrap.return.bind(otherWrap), TestError); + +checkIterResult(thisWrap.throw.call(otherWrap, 'value'), true, 'value'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-method-with-non-wrap-this-throws.js b/js/src/tests/non262/Iterator/from/wrap-method-with-non-wrap-this-throws.js new file mode 100644 index 0000000000..7ff8539923 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-method-with-non-wrap-this-throws.js @@ -0,0 +1,42 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// All methods on %WrapForValidIteratorPrototype% require an [[Iterated]] +// internal slot on the `this` object. + +class TestIterator { + next() { + return { + done: false, + value: 0, + }; + } +} + +const nextMethod = Iterator.from(new TestIterator()).next; +assertThrowsInstanceOf(() => nextMethod.call(undefined), TypeError); +assertThrowsInstanceOf(() => nextMethod.call(null), TypeError); +assertThrowsInstanceOf(() => nextMethod.call(0), TypeError); +assertThrowsInstanceOf(() => nextMethod.call(false), TypeError); +assertThrowsInstanceOf(() => nextMethod.call('test'), TypeError); +assertThrowsInstanceOf(() => nextMethod.call(Object(1)), TypeError); +assertThrowsInstanceOf(() => nextMethod.call({}), TypeError); + +const returnMethod = Iterator.from(new TestIterator()).next; +assertThrowsInstanceOf(() => returnMethod.call(undefined), TypeError); +assertThrowsInstanceOf(() => returnMethod.call(null), TypeError); +assertThrowsInstanceOf(() => returnMethod.call(0), TypeError); +assertThrowsInstanceOf(() => returnMethod.call(false), TypeError); +assertThrowsInstanceOf(() => returnMethod.call('test'), TypeError); +assertThrowsInstanceOf(() => returnMethod.call(Object(1)), TypeError); +assertThrowsInstanceOf(() => returnMethod.call({}), TypeError); + +const throwMethod = Iterator.from(new TestIterator()).next; +assertThrowsInstanceOf(() => throwMethod.call(undefined), TypeError); +assertThrowsInstanceOf(() => throwMethod.call(null), TypeError); +assertThrowsInstanceOf(() => throwMethod.call(0), TypeError); +assertThrowsInstanceOf(() => throwMethod.call(false), TypeError); +assertThrowsInstanceOf(() => throwMethod.call('test'), TypeError); +assertThrowsInstanceOf(() => throwMethod.call(Object(1)), TypeError); +assertThrowsInstanceOf(() => throwMethod.call({}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-new-global.js b/js/src/tests/non262/Iterator/from/wrap-new-global.js new file mode 100644 index 0000000000..a9a26b41b0 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-new-global.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const otherGlobal = newGlobal({newCompartment: true}); + +const iter = [1, 2, 3].values(); +assertEq(iter, Iterator.from(iter)); +assertEq(iter !== otherGlobal.Iterator.from(iter), true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-next-forwards-value.js b/js/src/tests/non262/Iterator/from/wrap-next-forwards-value.js new file mode 100644 index 0000000000..095ddd692a --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-next-forwards-value.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +class Iter { + next(value) { + this.v = value; + return { done: false, value }; + } +} + +const iter = new Iter(); +const wrap = Iterator.from(iter); +assertEq(iter !== wrap, true); + +assertEq(iter.v, undefined); +wrap.next(1); +assertEq(iter.v, 1); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-next-not-object-throws.js b/js/src/tests/non262/Iterator/from/wrap-next-not-object-throws.js new file mode 100644 index 0000000000..0e5b559f3b --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-next-not-object-throws.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const iter = (value) => Iterator.from({ + next: () => value, +}); + +assertThrowsInstanceOf(() => iter(undefined).next(), TypeError); +assertThrowsInstanceOf(() => iter(null).next(), TypeError); +assertThrowsInstanceOf(() => iter(0).next(), TypeError); +assertThrowsInstanceOf(() => iter(false).next(), TypeError); +assertThrowsInstanceOf(() => iter('test').next(), TypeError); +assertThrowsInstanceOf(() => iter(Symbol('')).next(), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-return-closes-iterator.js b/js/src/tests/non262/Iterator/from/wrap-return-closes-iterator.js new file mode 100644 index 0000000000..22518871f3 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-return-closes-iterator.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +class Iter { + next() { + if (this.closed) + return { done: true, value: undefined }; + return { done: false, value: 0 }; + } + + return(value) { + this.closed = true; + return { done: true, value }; + } +} + +const iter = new Iter(); +const wrap = Iterator.from(iter); +assertEq(iter.closed, undefined); + +let result = wrap.next(); +assertEq(result.done, false); +assertEq(result.value, 0); + +result = wrap.return(1); +assertEq(result.done, true); +assertEq(result.value, 1); + +assertEq(iter.closed, true); +result = wrap.next(); +assertEq(result.done, true); +assertEq(result.value, undefined); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/from/wrap-throw.js b/js/src/tests/non262/Iterator/from/wrap-throw.js new file mode 100644 index 0000000000..ffe17c80e8 --- /dev/null +++ b/js/src/tests/non262/Iterator/from/wrap-throw.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +class Iter { + next() { + return { done: false, value: 0 }; + } +} + +const iter = new Iter(); +const wrap = Iterator.from(iter); + +assertThrowsInstanceOf(() => wrap.throw(new Error()), Error); +assertThrows(() => wrap.throw()); +assertThrows(() => wrap.throw(1)); + +class IterThrowNull { + next() { + return { done: false, value: 0 }; + } + throw = null; +} + +const iterNull = new IterThrowNull(); +const wrapNull = Iterator.from(iter); + +assertThrowsInstanceOf(() => wrapNull.throw(new Error()), Error); +assertThrows(() => wrapNull.throw()); +assertThrows(() => wrapNull.throw(1)); + +class IterWithThrow { + next() { + return { done: false, value: 0 }; + } + + throw(value) { + return value; + } +} + +const iterWithThrow = new IterWithThrow(); +const wrapWithThrow = Iterator.from(iterWithThrow); + +assertEq(wrapWithThrow.throw(1), 1); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/iterator.js b/js/src/tests/non262/Iterator/iterator.js new file mode 100644 index 0000000000..ad48fc2061 --- /dev/null +++ b/js/src/tests/non262/Iterator/iterator.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Property descriptor of Iterator. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(this, 'Iterator'); +assertEq(propDesc.value, Iterator); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/length.js b/js/src/tests/non262/Iterator/length.js new file mode 100644 index 0000000000..07d1e4f804 --- /dev/null +++ b/js/src/tests/non262/Iterator/length.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The "length" property of Iterator +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator, 'length'); +assertEq(propDesc.value, 0); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/name.js b/js/src/tests/non262/Iterator/name.js new file mode 100644 index 0000000000..c11e570228 --- /dev/null +++ b/js/src/tests/non262/Iterator/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The "name" property of Iterator +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator, 'name'); +assertEq(propDesc.value, 'Iterator'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/proto.js b/js/src/tests/non262/Iterator/proto.js new file mode 100644 index 0000000000..2ab92641e7 --- /dev/null +++ b/js/src/tests/non262/Iterator/proto.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The prototype of the Iterator constructor is the intrinsic object %FunctionPrototype%. +---*/ + +assertEq(Object.getPrototypeOf(Iterator), Function.prototype); + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator, 'prototype'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, false); + +// Make sure @@toStringTag hasn't been set. +assertEq(Symbol.toStringTag in propDesc.value, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/asIndexedPairs/asIndexedPairs.js b/js/src/tests/non262/Iterator/prototype/asIndexedPairs/asIndexedPairs.js new file mode 100644 index 0000000000..42dfb6aaca --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/asIndexedPairs/asIndexedPairs.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let iter = [1, 2, 3].values().asIndexedPairs(); + +for (const v of [[0, 1], [1, 2], [2, 3]]) { + let result = iter.next(); + assertEq(result.done, false); + assertEq(result.value[0], v[0]); + assertEq(result.value[1], v[1]); +} + +assertEq(iter.next().done, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/asIndexedPairs/length.js b/js/src/tests/non262/Iterator/prototype/asIndexedPairs/length.js new file mode 100644 index 0000000000..4ec7ce238c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/asIndexedPairs/length.js @@ -0,0 +1,21 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.asIndexedPairs length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.asIndexedPairs.length, 0); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.asIndexedPairs, 'length'); +assertEq(propertyDescriptor.value, 0); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/asIndexedPairs/name.js b/js/src/tests/non262/Iterator/prototype/asIndexedPairs/name.js new file mode 100644 index 0000000000..74d31d7905 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/asIndexedPairs/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +/*--- +esid: pending +description: %Iterator.prototype%.asIndexedPairs.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.asIndexedPairs.name, 'asIndexedPairs'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.asIndexedPairs, 'name'); +assertEq(propertyDescriptor.value, 'asIndexedPairs'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/drop/drop-more-than-available.js b/js/src/tests/non262/Iterator/prototype/drop/drop-more-than-available.js new file mode 100644 index 0000000000..230ebb8faf --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/drop/drop-more-than-available.js @@ -0,0 +1,37 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.drop returns if the iterator is done. +info: > + Iterator Helpers proposal 2.1.5.5 + 1. Repeat, while remaining > 0, + ... + b. Let next be ? IteratorStep(iterated). + c. If next is false, return undefined. +features: [iterator-helpers] +---*/ + +let iter = [1, 2].values().drop(3); +let result = iter.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); + +class TestIterator extends Iterator { + counter = 0; + next() { + return {done: ++this.counter >= 2, value: undefined}; + } +} + +iter = new TestIterator(); +let dropped = iter.drop(10); +result = dropped.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); +assertEq(iter.counter, 2); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/drop/drop.js b/js/src/tests/non262/Iterator/prototype/drop/drop.js new file mode 100644 index 0000000000..2bacd45196 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/drop/drop.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +/*--- +esid: pending +description: Smoketest of %Iterator.prototype%.drop. +info: > + Iterator Helpers proposal 2.1.5.5 +features: [iterator-helpers] +---*/ + +let iter = [1, 2, 3].values().drop(1); + +for (const v of [2, 3]) { + let result = iter.next(); + assertEq(result.done, false); + assertEq(result.value, v); +} + +assertEq(iter.next().done, true); + +// `drop`, when called without arguments, has a limit of undefined, +// which converts to 0. +assertEq(['test'].values().drop().next().value, 'test'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/drop/length.js b/js/src/tests/non262/Iterator/prototype/drop/length.js new file mode 100644 index 0000000000..d7dbcefcc5 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/drop/length.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.drop length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [Symbol.iterator] +---*/ + +assertEq(Iterator.prototype.drop.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.drop, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/drop/name.js b/js/src/tests/non262/Iterator/prototype/drop/name.js new file mode 100644 index 0000000000..06c904a21c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/drop/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +/*--- +esid: pending +description: %Iterator.prototype%.drop.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.drop.name, 'drop'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.drop, 'name'); +assertEq(propertyDescriptor.value, 'drop'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/check-fn-after-getting-iterator.js b/js/src/tests/non262/Iterator/prototype/every/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..4c7305ad5c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/check-fn-after-getting-iterator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends Iterator { + next() { + return {done: true}; + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +assertThrowsInstanceOf(() => iter.every(1), TypeError); + +assertEq( + log.join('\n'), + `get: every +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/coerce-result-to-boolean.js b/js/src/tests/non262/Iterator/prototype/every/coerce-result-to-boolean.js new file mode 100644 index 0000000000..4af9650ab8 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/coerce-result-to-boolean.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = (value) => value; +assertEq([true].values().every(fn), true); +assertEq([1].values().every(fn), true); +assertEq([[]].values().every(fn), true); +assertEq([{}].values().every(fn), true); +assertEq(['test'].values().every(fn), true); + +assertEq([false].values().every(fn), false); +assertEq([0].values().every(fn), false); +assertEq([''].values().every(fn), false); +assertEq([null].values().every(fn), false); +assertEq([undefined].values().every(fn), false); +assertEq([NaN].values().every(fn), false); +assertEq([-0].values().every(fn), false); +assertEq([0n].values().every(fn), false); + +const htmlDDA = createIsHTMLDDA(); +assertEq([htmlDDA].values().every(fn), false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/descriptor.js b/js/src/tests/non262/Iterator/prototype/every/descriptor.js new file mode 100644 index 0000000000..e11eca28f3 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.prototype.every +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'every'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/error-from-correct-realm.js b/js/src/tests/non262/Iterator/prototype/every/error-from-correct-realm.js new file mode 100644 index 0000000000..7b24a631bb --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/error-from-correct-realm.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.every(), TypeError); +assertThrowsInstanceOf( + otherGlobal.Iterator.prototype.every.bind(iter), + otherGlobal.TypeError, + 'TypeError comes from the realm of the method.', +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/fn-not-callable-throws.js b/js/src/tests/non262/Iterator/prototype/every/fn-not-callable-throws.js new file mode 100644 index 0000000000..6b4328180c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/fn-not-callable-throws.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.every(), TypeError); +assertThrowsInstanceOf(() => iter.every(undefined), TypeError); +assertThrowsInstanceOf(() => iter.every(null), TypeError); +assertThrowsInstanceOf(() => iter.every(0), TypeError); +assertThrowsInstanceOf(() => iter.every(false), TypeError); +assertThrowsInstanceOf(() => iter.every(''), TypeError); +assertThrowsInstanceOf(() => iter.every(Symbol('')), TypeError); +assertThrowsInstanceOf(() => iter.every({}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/fn-throws-close-iterator.js b/js/src/tests/non262/Iterator/prototype/every/fn-throws-close-iterator.js new file mode 100644 index 0000000000..e3df75e2df --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/fn-throws-close-iterator.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + return { done: this.closed }; + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.every(fn), Error); +assertEq(iter.closed, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/length.js b/js/src/tests/non262/Iterator/prototype/every/length.js new file mode 100644 index 0000000000..8ef8ecbc7f --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.prototype.every. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.every, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/name.js b/js/src/tests/non262/Iterator/prototype/every/name.js new file mode 100644 index 0000000000..7a75da9771 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.prototype.every. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.every, 'name'); +assertEq(propDesc.value, 'every'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/next-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/every/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..56a3dae169 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/next-throws-iterator-not-closed.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => {}; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.every(fn), Error); +assertEq(iter.closed, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/proxy.js b/js/src/tests/non262/Iterator/prototype/every/proxy.js new file mode 100644 index 0000000000..c3a5b27770 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// This test checks that %Iterator.prototype%.every only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends Iterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return {done: false, value}; + } + return {done: true}; + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +assertEq(iter.every(x => x % 2 == 0), false); + +assertEq( + log.join('\n'), + `get: every +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: return` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/return-true-if-all-match.js b/js/src/tests/non262/Iterator/prototype/every/return-true-if-all-match.js new file mode 100644 index 0000000000..894219d29e --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/return-true-if-all-match.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 3, 5].values(); +const fn = (value) => value % 2 == 1; + +assertEq(iter.every(fn), true); + +assertEq([].values().every(x => x), true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/short-circuit-on-false.js b/js/src/tests/non262/Iterator/prototype/every/short-circuit-on-false.js new file mode 100644 index 0000000000..15b144a4a4 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/short-circuit-on-false.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 2, 3].values(); +const log = []; +const fn = (value) => { + log.push(value.toString()); + return value % 2 == 1; +}; + +assertEq(iter.every(fn), false); +assertEq(log.join(','), '1,2'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/this-not-iterator-throws.js b/js/src/tests/non262/Iterator/prototype/every/this-not-iterator-throws.js new file mode 100644 index 0000000000..5796c8524e --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/this-not-iterator-throws.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = x => x; +assertThrowsInstanceOf(Iterator.prototype.every.bind(undefined, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.every.bind({}, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.every.bind({next: 0}, fn), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/every/value-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/every/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..61fe29b7a1 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/every/value-throws-iterator-not-closed.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return new Proxy({done: false}, {get: (target, key, receiver) => { + if (key === 'value') + throw new TestError(); + return 0; + }}); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +assertThrowsInstanceOf(() => iterator.every(x => x), TestError); +assertEq(iterator.closed, false, 'iterator remains unclosed'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/filter/coerce-result-to-boolean.js b/js/src/tests/non262/Iterator/prototype/filter/coerce-result-to-boolean.js new file mode 100644 index 0000000000..a3d16bcdfa --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/filter/coerce-result-to-boolean.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +// All truthy values are kept. +const truthyValues = [true, 1, [], {}, 'test']; +for (const value of [...truthyValues].values().filter(x => x)) { + assertEq(truthyValues.shift(), value); +} + +// All falsy values are filtered out. +const falsyValues = [false, 0, '', null, undefined, NaN, -0, 0n, createIsHTMLDDA()]; +const result = falsyValues.values().filter(x => x).next(); +assertEq(result.done, true); +assertEq(result.value, undefined); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/filter/filter.js b/js/src/tests/non262/Iterator/prototype/filter/filter.js new file mode 100644 index 0000000000..1d148cb82b --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/filter/filter.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let iter = [1, 2, 3].values().filter(x => x % 2); + +for (const v of [1, 3]) { + let result = iter.next(); + assertEq(result.done, false); + assertEq(result.value, v); +} + +assertEq(iter.next().done, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/filter/length.js b/js/src/tests/non262/Iterator/prototype/filter/length.js new file mode 100644 index 0000000000..c1fad91b1d --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/filter/length.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.filter length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [Symbol.iterator] +---*/ + +assertEq(Iterator.prototype.filter.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.filter, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/filter/name.js b/js/src/tests/non262/Iterator/prototype/filter/name.js new file mode 100644 index 0000000000..b278220225 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/filter/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +/*--- +esid: pending +description: %Iterator.prototype%.filter.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.filter.name, 'filter'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.filter, 'name'); +assertEq(propertyDescriptor.value, 'filter'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/check-fn-after-getting-iterator.js b/js/src/tests/non262/Iterator/prototype/find/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..0417360cc9 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/check-fn-after-getting-iterator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends Iterator { + next() { + return {done: true}; + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +assertThrowsInstanceOf(() => iter.find(1), TypeError); + +assertEq( + log.join('\n'), + `get: find +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/coerce-result-to-boolean.js b/js/src/tests/non262/Iterator/prototype/find/coerce-result-to-boolean.js new file mode 100644 index 0000000000..7818e5a58d --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/coerce-result-to-boolean.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = (value) => value; +assertEq([true].values().find(fn), true); +assertEq([1].values().find(fn), 1); +assertEq(['test'].values().find(fn), 'test'); + +assertEq([false].values().find(fn), undefined); +assertEq([0].values().find(fn), undefined); +assertEq([''].values().find(fn), undefined); +assertEq([null].values().find(fn), undefined); +assertEq([undefined].values().find(fn), undefined); +assertEq([NaN].values().find(fn), undefined); +assertEq([-0].values().find(fn), undefined); +assertEq([0n].values().find(fn), undefined); + +let array = []; +assertEq([array].values().find(fn), array); + +let object = {}; +assertEq([object].values().find(fn), object); + +const htmlDDA = createIsHTMLDDA(); +assertEq([htmlDDA].values().find(fn), undefined); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/descriptor.js b/js/src/tests/non262/Iterator/prototype/find/descriptor.js new file mode 100644 index 0000000000..45699dff85 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.prototype.find +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'find'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/error-from-correct-realm.js b/js/src/tests/non262/Iterator/prototype/find/error-from-correct-realm.js new file mode 100644 index 0000000000..b34408e853 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/error-from-correct-realm.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.find(), TypeError); +assertThrowsInstanceOf( + otherGlobal.Iterator.prototype.find.bind(iter), + otherGlobal.TypeError, + 'TypeError comes from the realm of the method.', +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/fn-not-callable-throws.js b/js/src/tests/non262/Iterator/prototype/find/fn-not-callable-throws.js new file mode 100644 index 0000000000..33d18b49cc --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/fn-not-callable-throws.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.find(), TypeError); +assertThrowsInstanceOf(() => iter.find(undefined), TypeError); +assertThrowsInstanceOf(() => iter.find(null), TypeError); +assertThrowsInstanceOf(() => iter.find(0), TypeError); +assertThrowsInstanceOf(() => iter.find(false), TypeError); +assertThrowsInstanceOf(() => iter.find(''), TypeError); +assertThrowsInstanceOf(() => iter.find(Symbol('')), TypeError); +assertThrowsInstanceOf(() => iter.find({}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/fn-throws-close-iterator.js b/js/src/tests/non262/Iterator/prototype/find/fn-throws-close-iterator.js new file mode 100644 index 0000000000..1393bee356 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/fn-throws-close-iterator.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + return { done: this.closed }; + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = (value) => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.find(fn), Error); +assertEq(iter.closed, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/length.js b/js/src/tests/non262/Iterator/prototype/find/length.js new file mode 100644 index 0000000000..b182ca0e78 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.prototype.find. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.find, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/name.js b/js/src/tests/non262/Iterator/prototype/find/name.js new file mode 100644 index 0000000000..36d903b44c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.prototype.find. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.find, 'name'); +assertEq(propDesc.value, 'find'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/next-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/find/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..13c3195654 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/next-throws-iterator-not-closed.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = x => x; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.find(fn), Error); +assertEq(iter.closed, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/proxy.js b/js/src/tests/non262/Iterator/prototype/find/proxy.js new file mode 100644 index 0000000000..c24d787710 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// This test checks that %Iterator.prototype%.find only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends Iterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return {done: false, value}; + } + return {done: true}; + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +assertEq(iter.find(x => x % 2 == 1), 1); + +assertEq( + log.join('\n'), + `get: find +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: return` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/return-undefined-if-none-match.js b/js/src/tests/non262/Iterator/prototype/find/return-undefined-if-none-match.js new file mode 100644 index 0000000000..3e7364fa95 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/return-undefined-if-none-match.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 3, 5].values(); +const fn = (value) => value % 2 == 0; + +assertEq(iter.find(fn), undefined); + +assertEq([].values().find(x => x), undefined); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/short-circuit-on-match.js b/js/src/tests/non262/Iterator/prototype/find/short-circuit-on-match.js new file mode 100644 index 0000000000..8ce7c5a354 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/short-circuit-on-match.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 2, 3].values(); +const log = []; +const fn = (value) => { + log.push(value.toString()); + return value % 2 == 0; +}; + +assertEq(iter.find(fn), 2); +assertEq(log.join(','), '1,2'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/this-not-iterator-throws.js b/js/src/tests/non262/Iterator/prototype/find/this-not-iterator-throws.js new file mode 100644 index 0000000000..8e1d446705 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/this-not-iterator-throws.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = x => x; +assertThrowsInstanceOf(Iterator.prototype.find.bind(undefined, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.find.bind({}, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.find.bind({next: 0}, fn), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/find/value-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/find/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..23873c618a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/find/value-throws-iterator-not-closed.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return new Proxy({done: false}, {get: (target, key, receiver) => { + if (key === 'value') + throw new TestError(); + return 0; + }}); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +assertThrowsInstanceOf(() => iterator.find(x => x), TestError); +assertEq(iterator.closed, false, 'iterator remains unclosed'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-complete-throws.js b/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-complete-throws.js new file mode 100644 index 0000000000..3a0bfcf167 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-complete-throws.js @@ -0,0 +1,49 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.flatMap closes the iterator when innerComplete throws. +info: > + Iterator Helpers proposal 2.1.5.7 + 1. Repeat, + ... + i. Repeat, while innerAlive is true, + iii. Let innerComplete be IteratorComplete(innerNext). + iv. IfAbruptCloseIterator(innerComplete, iterated). +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next() { + return {done: false, value: 0}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +class TestError extends Error {} +class InnerIterator extends Iterator { + next() { + return { + get done() { + throw new TestError(); + } + }; + } +} + +const iter = new TestIterator(); +const mapped = iter.flatMap(x => new InnerIterator()); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => mapped.next(), TestError); +assertEq(iter.closed, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-next-throws.js b/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-next-throws.js new file mode 100644 index 0000000000..a8f70497f7 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-next-throws.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.flatMap closes the iterator when innerNext throws. +info: > + Iterator Helpers proposal 2.1.5.7 + 1. Repeat, + ... + i. Repeat, while innerAlive is true, + i. Let innerNext be IteratorNext(innerIterator). + ii. IfAbruptCloseIterator(innerNext, iterated). +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next() { + return {done: false, value: 0}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +class TestError extends Error {} +class InnerIterator extends Iterator { + next() { + throw new TestError(); + } +} + +const iter = new TestIterator(); +const mapped = iter.flatMap(x => new InnerIterator()); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => mapped.next(), TestError); +assertEq(iter.closed, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-value-throws.js b/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-value-throws.js new file mode 100644 index 0000000000..3b2f26b401 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/close-iterator-when-inner-value-throws.js @@ -0,0 +1,52 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.flatMap closes the iterator when innerValue throws. +info: > + Iterator Helpers proposal 2.1.5.7 + 1. Repeat, + ... + i. Repeat, while innerAlive is true, + ... + vi. Else, + 1. Let innerValue be IteratorValue(innerNext). + 2. IfAbruptCloseIterator(innerValue, iterated). +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next() { + return {done: false, value: 0}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +class TestError extends Error {} +class InnerIterator extends Iterator { + next() { + return { + done: false, + get value() { + throw new TestError(); + }, + }; + } +} + +const iter = new TestIterator(); +const mapped = iter.flatMap(x => new InnerIterator()); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => mapped.next(), TestError); +assertEq(iter.closed, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/flatMap.js b/js/src/tests/non262/Iterator/prototype/flatMap/flatMap.js new file mode 100644 index 0000000000..6acce22593 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/flatMap.js @@ -0,0 +1,12 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +let iter = [1, 2, 3].values().flatMap(x => [x, x + 1]); +for (const v of [1, 2, 2, 3, 3, 4]) { + let result = iter.next(); + assertEq(result.done, false); + assertEq(result.value, v); +} +assertEq(iter.next().done, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/inner-empty-iterable.js b/js/src/tests/non262/Iterator/prototype/flatMap/inner-empty-iterable.js new file mode 100644 index 0000000000..24fcc85c2c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/inner-empty-iterable.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.flatMap skips empty inner iterables. +info: > + Iterator Helpers proposal 2.1.5.7 + 1. Repeat, + ... + i. Repeat, while innerAlive is true, + ... + iii. Let innerComplete be IteratorComplete(innerNext). + ... + v. If innerComplete is true, set innerAlive to false. +features: [iterator-helpers] +---*/ + +let iter = [0, 1, 2, 3].values().flatMap(x => x % 2 ? [] : [x]); + +for (const expected of [0, 2]) { + const result = iter.next(); + assertEq(result.value, expected); + assertEq(result.done, false); +} + +let result = iter.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); + +iter = [0, 1, 2, 3].values().flatMap(x => []); +result = iter.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/inner-generator.js b/js/src/tests/non262/Iterator/prototype/flatMap/inner-generator.js new file mode 100644 index 0000000000..9589027b4b --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/inner-generator.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.flatMap innerIterator can be a generator. +info: > + Iterator Helpers proposal 2.1.5.7 +features: [iterator-helpers] +---*/ + +const iter = [1, 2].values().flatMap(function*(x) { + yield x; + yield* [x + 1, x + 2]; +}); + +for (const expected of [1, 2, 3, 2, 3, 4]) { + const result = iter.next(); + assertEq(result.value, expected); + assertEq(result.done, false); +} + +const result = iter.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/length.js b/js/src/tests/non262/Iterator/prototype/flatMap/length.js new file mode 100644 index 0000000000..d5f8a5021f --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/length.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.flatMap length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [Symbol.iterator] +---*/ + +assertEq(Iterator.prototype.flatMap.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.flatMap, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/name.js b/js/src/tests/non262/Iterator/prototype/flatMap/name.js new file mode 100644 index 0000000000..6cc6b98ef0 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +/*--- +esid: pending +description: %Iterator.prototype%.flatMap.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.flatMap.name, 'flatMap'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.flatMap, 'name'); +assertEq(propertyDescriptor.value, 'flatMap'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/flatMap/throw-when-inner-not-iterable.js b/js/src/tests/non262/Iterator/prototype/flatMap/throw-when-inner-not-iterable.js new file mode 100644 index 0000000000..eb581936e1 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/flatMap/throw-when-inner-not-iterable.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.flatMap closes the iterator and throws when mapped isn't iterable. +info: > + Iterator Helpers proposal 2.1.5.7 + 1. Repeat, + ... + f. Let innerIterator be GetIterator(mapped, sync). + g. IfAbruptCloseIterator(innerIterator, iterated). +features: [iterator-helpers] +---*/ + +class NotIterable { + next() { + return {done: true}; + } +} + +class InvalidIterable { + [Symbol.iterator]() { + return {}; + } +} + +class TestIterator extends Iterator { + next() { + return {done: false, value: 0}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const nonIterables = [ + new NotIterable(), + new InvalidIterable(), + undefined, + null, + 0, + false, + Symbol(''), + 0n, + {}, +]; + +for (const value of nonIterables) { + const iter = new TestIterator(); + const mapped = iter.flatMap(x => value); + + assertEq(iter.closed, false); + assertThrowsInstanceOf(() => mapped.next(), TypeError); + assertEq(iter.closed, true); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/Iterator/prototype/forEach/check-fn-after-getting-iterator.js b/js/src/tests/non262/Iterator/prototype/forEach/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..2291c04664 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/check-fn-after-getting-iterator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends Iterator { + next() { + return {done: true}; + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +assertThrowsInstanceOf(() => iter.forEach(1), TypeError); + +assertEq( + log.join('\n'), + `get: forEach +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/descriptor.js b/js/src/tests/non262/Iterator/prototype/forEach/descriptor.js new file mode 100644 index 0000000000..b33be1e435 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.prototype.forEach +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'forEach'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/error-from-correct-realm.js b/js/src/tests/non262/Iterator/prototype/forEach/error-from-correct-realm.js new file mode 100644 index 0000000000..38c8e9bb5c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/error-from-correct-realm.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.forEach(), TypeError); +assertThrowsInstanceOf( + otherGlobal.Iterator.prototype.forEach.bind(iter), + otherGlobal.TypeError, + 'TypeError comes from the realm of the method.', +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/fn-not-callable-throws.js b/js/src/tests/non262/Iterator/prototype/forEach/fn-not-callable-throws.js new file mode 100644 index 0000000000..473789a6a2 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/fn-not-callable-throws.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.forEach(), TypeError); +assertThrowsInstanceOf(() => iter.forEach(undefined), TypeError); +assertThrowsInstanceOf(() => iter.forEach(null), TypeError); +assertThrowsInstanceOf(() => iter.forEach(0), TypeError); +assertThrowsInstanceOf(() => iter.forEach(false), TypeError); +assertThrowsInstanceOf(() => iter.forEach(''), TypeError); +assertThrowsInstanceOf(() => iter.forEach(Symbol('')), TypeError); +assertThrowsInstanceOf(() => iter.forEach({}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/fn-throws-close-iterator.js b/js/src/tests/non262/Iterator/prototype/forEach/fn-throws-close-iterator.js new file mode 100644 index 0000000000..1703673f0d --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/fn-throws-close-iterator.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + return { done: this.closed }; + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.forEach(fn), Error); +assertEq(iter.closed, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/forEach.js b/js/src/tests/non262/Iterator/prototype/forEach/forEach.js new file mode 100644 index 0000000000..62ddce6eb7 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/forEach.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const log = []; +const fn = (value) => log.push(value); +const iter = [1, 2, 3].values(); + +assertEq(iter.forEach(fn), undefined); +assertEq(log.join(','), '1,2,3'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/length.js b/js/src/tests/non262/Iterator/prototype/forEach/length.js new file mode 100644 index 0000000000..9f2f9134f8 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.prototype.forEach. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.forEach, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/name.js b/js/src/tests/non262/Iterator/prototype/forEach/name.js new file mode 100644 index 0000000000..7c4964e696 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.prototype.forEach. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.forEach, 'name'); +assertEq(propDesc.value, 'forEach'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/next-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/forEach/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..042b6d660c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/next-throws-iterator-not-closed.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => {}; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.forEach(fn), Error); +assertEq(iter.closed, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/proxy.js b/js/src/tests/non262/Iterator/prototype/forEach/proxy.js new file mode 100644 index 0000000000..9d50aed9fe --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// This test checks that %Iterator.prototype%.forEach only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends Iterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return {done: false, value}; + } + return {done: true}; + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.forEach(x => x); + +assertEq( + log.join('\n'), + `get: forEach +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/this-not-iterator-throws.js b/js/src/tests/non262/Iterator/prototype/forEach/this-not-iterator-throws.js new file mode 100644 index 0000000000..d47ba38960 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/this-not-iterator-throws.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = x => x; +assertThrowsInstanceOf(Iterator.prototype.forEach.bind(undefined, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.forEach.bind({}, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.forEach.bind({next: 0}, fn), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/forEach/value-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/forEach/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..28f1d64113 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/forEach/value-throws-iterator-not-closed.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return new Proxy({done: false}, {get: (target, key, receiver) => { + if (key === 'value') + throw new TestError(); + return 0; + }}); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +assertThrowsInstanceOf(() => iterator.forEach(x => x), TestError); +assertEq(iterator.closed, false, 'iterator remains unclosed'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/generator-methods-throw-on-iterator-helpers.js b/js/src/tests/non262/Iterator/prototype/generator-methods-throw-on-iterator-helpers.js new file mode 100644 index 0000000000..f684be43fb --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/generator-methods-throw-on-iterator-helpers.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +const generatorProto = Object.getPrototypeOf( + Object.getPrototypeOf( + (function *() {})() + ) +); + +const iteratorHelper = [0].values().map(x => x); + +assertThrowsInstanceOf(() => generatorProto.next.call(iteratorHelper), TypeError); +assertThrowsInstanceOf(() => generatorProto.return.call(iteratorHelper), TypeError); +assertThrowsInstanceOf(() => generatorProto.throw.call(iteratorHelper), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/iterator-helper-methods-throw-on-generators.js b/js/src/tests/non262/Iterator/prototype/iterator-helper-methods-throw-on-generators.js new file mode 100644 index 0000000000..e074d7e580 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/iterator-helper-methods-throw-on-generators.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +const iteratorHelperProto = Object.getPrototypeOf([].values().map(x => x)); + +function *gen() { + yield 1; +} + +assertThrowsInstanceOf(() => iteratorHelperProto.next.call(gen()), TypeError); +assertThrowsInstanceOf(() => iteratorHelperProto.return.call(gen()), TypeError); +assertThrowsInstanceOf(() => iteratorHelperProto.throw.call(gen()), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/iterator-helpers-from-other-global.js b/js/src/tests/non262/Iterator/prototype/iterator-helpers-from-other-global.js new file mode 100644 index 0000000000..d18bd8f00f --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/iterator-helpers-from-other-global.js @@ -0,0 +1,64 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +class TestError extends Error {} + +class TestIterator extends Iterator { + next() { + return {done: false, value: 'value'}; + } + + closed = false; + return(value) { + this.closed = true; + return {done: true, value}; + } +} + +function checkIterResult({done, value}, expectedDone, expectedValue) { + assertEq(done, expectedDone); + assertEq(Array.isArray(value) ? value[1] : value, expectedValue); +} + +const otherGlobal = newGlobal({newCompartment: true}); + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", x => [x]], +]; + +for (const [method, arg] of methods) { + const {next: otherNext} = Object.getPrototypeOf( + new otherGlobal.Array().values()[method](arg) + ); + const iterator = new TestIterator(); + const helper = iterator[method](arg); + checkIterResult(otherNext.call(helper), false, 'value'); +} + +for (const [method, arg] of methods) { + const {return: otherReturn} = Object.getPrototypeOf( + new otherGlobal.Array().values()[method](arg) + ); + const iterator = new TestIterator(); + const helper = iterator[method](arg); + assertEq(iterator.closed, false); + checkIterResult(otherReturn.call(helper), true, undefined); + assertEq(iterator.closed, true); +} + +for (const [method, arg] of methods) { + const {throw: otherThrow} = Object.getPrototypeOf( + new otherGlobal.Array().values()[method](arg) + ); + const iterator = new TestIterator(); + const helper = iterator[method](arg); + assertThrowsInstanceOf(otherThrow.bind(helper, new TestError()), TestError); + checkIterResult(helper.next(), true, undefined); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-from-other-global.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-from-other-global.js new file mode 100644 index 0000000000..e6235ed171 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-from-other-global.js @@ -0,0 +1,31 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +const otherIteratorProto = newGlobal({newCompartment: true}).Iterator.prototype; + +const methods = [ + ["map", x => x], + ["filter", x => true], + ["take", Infinity], + ["drop", 0], + ["asIndexedPairs", undefined], + ["flatMap", x => [x]], +]; + +// Use the lazy Iterator methods from another global on an iterator from this global. +for (const [method, arg] of methods) { + const iterator = [1, 2, 3].values(); + const helper = otherIteratorProto[method].call(iterator, arg); + + for (const expected of [1, 2, 3]) { + const {done, value} = helper.next(); + assertEq(done, false); + assertEq(Array.isArray(value) ? value[1] : value, expected); + } + + const {done, value} = helper.next(); + assertEq(done, true); + assertEq(value, undefined); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-handle-empty-iterators.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-handle-empty-iterators.js new file mode 100644 index 0000000000..e6df2758ad --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-handle-empty-iterators.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods handle empty iterators. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class EmptyIterator extends Iterator { + next() { + return {done: true}; + } +} + +const emptyIterator1 = new EmptyIterator(); +const emptyIterator2 = [].values(); + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + for (const iterator of [emptyIterator1, emptyIterator2]) { + const result = method(iterator).next(); + assertEq(result.done, true); + assertEq(result.value, undefined); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-interleaved.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-interleaved.js new file mode 100644 index 0000000000..0edc3cd467 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-interleaved.js @@ -0,0 +1,62 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% method calls can be interleaved. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + value = 0; + next() { + return {done: false, value: this.value++}; + } +} + +function unwrapResult(result) { + // Unwrap the asIndexedPair return values. + while (Array.isArray(result.value)) { + result.value = result.value[1]; + } + return result; +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(2), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const firstMethod of methods) { + for (const secondMethod of methods) { + const iterator = new TestIterator(); + const firstHelper = firstMethod(iterator); + const secondHelper = secondMethod(iterator); + + let firstResult = unwrapResult(firstHelper.next()); + assertEq(firstResult.done, false); + assertEq(firstResult.value, 0); + + let secondResult = unwrapResult(secondHelper.next()); + assertEq(secondResult.done, false); + assertEq(secondResult.value, 1); + + firstResult = unwrapResult(firstHelper.next()); + assertEq(firstResult.done, false); + assertEq(firstResult.value, 2); + + secondResult = unwrapResult(secondHelper.next()); + assertEq(secondResult.done, false); + assertEq(secondResult.value, 3); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-closed-on-call-throws.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-closed-on-call-throws.js new file mode 100644 index 0000000000..f81e9280ce --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-closed-on-call-throws.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods close the iterator if callback throws. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +function fn() { + throw new TestError(); +} +const methods = [ + iter => iter.map(fn), + iter => iter.filter(fn), + iter => iter.flatMap(fn), +]; + +for (const method of methods) { + const iter = new TestIterator(); + assertEq(iter.closed, false); + assertThrowsInstanceOf(() => method(iter).next(), TestError); + assertEq(iter.closed, true); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-closed-on-yield-throws.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-closed-on-yield-throws.js new file mode 100644 index 0000000000..361b800404 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-closed-on-yield-throws.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods close the iterator if `yield` throws. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + const iteratorHelper = method(iterator); + iteratorHelper.next(); + assertThrowsInstanceOf(() => iteratorHelper.throw(new TestError()), TestError); + assertEq(iterator.closed, true); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-not-closed-on-next-throws.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-not-closed-on-next-throws.js new file mode 100644 index 0000000000..4aceacebb6 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-not-closed-on-next-throws.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods don't close the iterator if `.next` call throws. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + throw new TestError(); + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const iterator = new TestIterator(); + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + assertEq(iterator.closed, false); + assertThrowsInstanceOf(() => method(iterator).next(), TestError); + assertEq(iterator.closed, false); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-not-closed-on-value-throws.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-not-closed-on-value-throws.js new file mode 100644 index 0000000000..1ca88ca9b5 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-not-closed-on-value-throws.js @@ -0,0 +1,48 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods don't close the iterator if `value` throws. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return { + get value() { + throw new TestError(); + } + }; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const iterator = new TestIterator(); + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + assertEq(iterator.closed, false); + assertThrowsInstanceOf(() => method(iterator).next(), TestError); + assertEq(iterator.closed, false); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-returns-done-generator-finishes.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-returns-done-generator-finishes.js new file mode 100644 index 0000000000..d6b3f14277 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-iterator-returns-done-generator-finishes.js @@ -0,0 +1,27 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +class TestIterator extends Iterator { + next() { + return {done: true, value: 'value'}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + const iterator = method(new TestIterator()); + const result = iterator.next(); + assertEq(result.done, true); + assertEq(result.value, undefined); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-multiple-return-close-iterator-once.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-multiple-return-close-iterator-once.js new file mode 100644 index 0000000000..34c165b86d --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-multiple-return-close-iterator-once.js @@ -0,0 +1,60 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Calling `.return()` on a lazy %Iterator.prototype% method multiple times closes the source iterator once. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closeCount = 0; + return(value) { + this.closeCount++; + return {done: true, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +// Call `return` after stepping the iterator once: +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + iterHelper.next(); + + assertEq(iter.closeCount, 0); + iterHelper.return(); + assertEq(iter.closeCount, 1); + iterHelper.return(); + assertEq(iter.closeCount, 1); +} + +// Call `return` before stepping the iterator: +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closeCount, 0); + iterHelper.return(); + assertEq(iter.closeCount, 1); + iterHelper.return(); + assertEq(iter.closeCount, 1); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-multiple-throw-close-iterator-once.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-multiple-throw-close-iterator-once.js new file mode 100644 index 0000000000..6c044cbd89 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-multiple-throw-close-iterator-once.js @@ -0,0 +1,62 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Calling `.throw()` on a lazy %Iterator.prototype% method multiple times closes the source iterator once. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closeCount = 0; + return(value) { + this.closeCount++; + return {done: true, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +// Call `throw` after stepping the iterator once: +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + iterHelper.next(); + + assertEq(iter.closeCount, 0); + assertThrowsInstanceOf(() => iterHelper.throw(new TestError()), TestError); + assertEq(iter.closeCount, 1); + assertThrowsInstanceOf(() => iterHelper.throw(new TestError()), TestError); + assertEq(iter.closeCount, 1); +} + +// Call `throw` before stepping the iterator: +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closeCount, 0); + assertThrowsInstanceOf(() => iterHelper.throw(new TestError()), TestError); + assertEq(iter.closeCount, 1); + assertThrowsInstanceOf(() => iterHelper.throw(new TestError()), TestError); + assertEq(iter.closeCount, 1); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-pass-through-lastValue.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-pass-through-lastValue.js new file mode 100644 index 0000000000..a7195b38e4 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-pass-through-lastValue.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +class TestIterator extends Iterator { + next(value) { + return {done: false, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(2), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), +]; + +for (const method of methods) { + const iterator = new TestIterator(); + const iteratorHelper = method(iterator); + iteratorHelper.next(); + let result = iteratorHelper.next('last value'); + assertEq(result.done, false); + // Array.isArray is used to make sure we unwrap asIndexedPairs results. + assertEq(Array.isArray(result.value) ? result.value[1] : result.value, 'last value'); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-pass-value-through-chain.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-pass-value-through-chain.js new file mode 100644 index 0000000000..553e6934f7 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-pass-value-through-chain.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +class TestIterator extends Iterator { + next(value) { + return {done: false, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(2), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), +]; + +for (const outerMethod of methods) { + for (const innerMethod of methods) { + const iterator = new TestIterator(); + const iteratorChain = outerMethod(innerMethod(iterator)); + iteratorChain.next(); + let result = iteratorChain.next('last value'); + assertEq(result.done, false); + // Unwrap the asIndexedPair return values. + while (Array.isArray(result.value)) { + result.value = result.value[1]; + } + assertEq(result.value, 'last value'); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-proxy-accesses.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-proxy-accesses.js new file mode 100644 index 0000000000..d391a4f199 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-proxy-accesses.js @@ -0,0 +1,70 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods access specified properties only. +info: > +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + value = 0; + next() { + if (this.value < 2) + return { done: false, value: this.value++ }; + return { done: true, value: undefined }; + } +} + +const handlerProxy = log => new Proxy({}, { + get: (target, key, receiver) => (...args) => { + const target = args[0]; + const item = Reflect[key](...args); + + log.push(`${key}: ${args.filter(x => typeof x != 'object').map(x => x.toString())}`); + + return item; + }, +}); + +const methods = [ + [iter => iter.map(x => x), 'map'], + [iter => iter.filter(x => true), 'filter'], + [iter => iter.take(4), 'take'], + [iter => iter.drop(0), 'drop'], + [iter => iter.asIndexedPairs(), 'asIndexedPairs'], + [iter => iter.flatMap(x => [x]), 'flatMap'], +]; + +for (const method of methods) { + const log = []; + const iteratorProxy = new Proxy(new TestIterator(), handlerProxy(log)); + const iteratorHelper = method[0](iteratorProxy); + const methodName = method[1]; + + iteratorHelper.next(); + iteratorHelper.next(); + iteratorHelper.next(); + assertEq(iteratorHelper.next().done, true); + + assertEq( + log.join('\n'), + `get: ${methodName} +get: next +get: value +get: value +getOwnPropertyDescriptor: value +defineProperty: value +set: value,1 +get: value +get: value +getOwnPropertyDescriptor: value +defineProperty: value +set: value,2 +get: value` + ) +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-reentry-not-close-iterator.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-reentry-not-close-iterator.js new file mode 100644 index 0000000000..da1e4c8c29 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-reentry-not-close-iterator.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +const methods = [ + [iter => iter.map, x => x], + [iter => iter.filter, x => true], + [iter => iter.flatMap, x => [x]], +]; + +for (const method of methods) { + const iter = [1, 2, 3].values(); + const iterMethod = method[0](iter); + let iterHelper; + let reentered = false; + iterHelper = iterMethod.call(iter, x => { + if (x == 2) { + // Reenter the currently running generator. + reentered = true; + assertThrowsInstanceOf(() => iterHelper.next(), TypeError); + } + return method[1](x); + }); + + let result = iterHelper.next(); + assertEq(result.value, 1); + assertEq(result.done, false); + + assertEq(reentered, false); + result = iterHelper.next(); + assertEq(reentered, true); + assertEq(result.value, 2); + assertEq(result.done, false); + + result = iterHelper.next(); + assertEq(result.value, 3); + assertEq(result.done, false); + + result = iterHelper.next(); + assertEq(result.value, undefined); + assertEq(result.done, true); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-return-closes-iterator.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-return-closes-iterator.js new file mode 100644 index 0000000000..45fa3105df --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-return-closes-iterator.js @@ -0,0 +1,58 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Calling `.return()` on a lazy %Iterator.prototype% method closes the source iterator. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closed = false; + return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + iterHelper.next(); + + assertEq(iter.closed, false); + const result = iterHelper.return(0); + assertEq(iter.closed, true); + assertEq(result.done, true); + assertEq(result.value, 0); +} + +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closed, false); + const result = iterHelper.return(0); + assertEq(iter.closed, true); + assertEq(result.done, true); + assertEq(result.value, 0); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-return-new-iterator-result.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-return-new-iterator-result.js new file mode 100644 index 0000000000..407782fae5 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-return-new-iterator-result.js @@ -0,0 +1,39 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy Iterator Helper methods return new iterator result objects. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +const iterResult = {done: false, value: 1, testProperty: 'test'}; +class TestIterator extends Iterator { + next() { + return iterResult; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => true), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +// Call `return` before stepping the iterator: +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + const result = iterHelper.next(); + assertEq(result == iterResult, false); + assertEq(result.testProperty, undefined); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-closes-iterator-before-next.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-closes-iterator-before-next.js new file mode 100644 index 0000000000..d3197fc107 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-closes-iterator-before-next.js @@ -0,0 +1,50 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Calling `.throw()` on a lazy %Iterator.prototype% method closes the source iterator. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closed = false; + return(value) { + this.closed = true; + return {done: true, value}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + const iter = new TestIterator(); + const iterHelper = method(iter); + + assertEq(iter.closed, false); + assertThrowsInstanceOf(() => iterHelper.throw(new TestError()), TestError); + assertEq(iter.closed, true); + + const result = iterHelper.next(); + assertEq(result.done, true); + assertEq(result.value, undefined); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-next-non-callable.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-next-non-callable.js new file mode 100644 index 0000000000..22d9bbd48a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-next-non-callable.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods throw eagerly when `next` is non-callable. +info: > + Iterator Helpers proposal 1.1.1 +features: [iterator-helpers] +---*/ + +const methods = [ + next => Iterator.prototype.map.bind({next}, x => x), + next => Iterator.prototype.filter.bind({next}, x => x), + next => Iterator.prototype.take.bind({next}, 1), + next => Iterator.prototype.drop.bind({next}, 0), + next => Iterator.prototype.asIndexedPairs.bind({next}), + next => Iterator.prototype.flatMap.bind({next}, x => [x]), +]; + +for (const method of methods) { + assertThrowsInstanceOf(method(0), TypeError); + assertThrowsInstanceOf(method(false), TypeError); + assertThrowsInstanceOf(method(undefined), TypeError); + assertThrowsInstanceOf(method(null), TypeError); + assertThrowsInstanceOf(method(''), TypeError); + assertThrowsInstanceOf(method(Symbol('')), TypeError); + assertThrowsInstanceOf(method({}), TypeError); + assertThrowsInstanceOf(method([]), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-non-callable.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-non-callable.js new file mode 100644 index 0000000000..7c31a21a38 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-non-callable.js @@ -0,0 +1,38 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods throw eagerly when passed non-callables. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +const methods = [ + (iter, fn) => iter.map(fn), + (iter, fn) => iter.filter(fn), + (iter, fn) => iter.flatMap(fn), +]; + +for (const method of methods) { + assertThrowsInstanceOf(() => method(Iterator.prototype, 0), TypeError); + assertThrowsInstanceOf(() => method(Iterator.prototype, false), TypeError); + assertThrowsInstanceOf(() => method(Iterator.prototype, undefined), TypeError); + assertThrowsInstanceOf(() => method(Iterator.prototype, null), TypeError); + assertThrowsInstanceOf(() => method(Iterator.prototype, ''), TypeError); + assertThrowsInstanceOf(() => method(Iterator.prototype, Symbol('')), TypeError); + assertThrowsInstanceOf(() => method(Iterator.prototype, {}), TypeError); + + assertThrowsInstanceOf(() => method([].values(), 0), TypeError); + assertThrowsInstanceOf(() => method([].values(), false), TypeError); + assertThrowsInstanceOf(() => method([].values(), undefined), TypeError); + assertThrowsInstanceOf(() => method([].values(), null), TypeError); + assertThrowsInstanceOf(() => method([].values(), ''), TypeError); + assertThrowsInstanceOf(() => method([].values(), Symbol('')), TypeError); + assertThrowsInstanceOf(() => method([].values(), {}), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-non-iterator.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-non-iterator.js new file mode 100644 index 0000000000..dad38237f1 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-eagerly-on-non-iterator.js @@ -0,0 +1,34 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods throw eagerly when called on non-iterators. +info: > + Iterator Helpers proposal 1.1.1 +features: [iterator-helpers] +---*/ + +const methods = [ + iter => Iterator.prototype.map.bind(iter, x => x), + iter => Iterator.prototype.filter.bind(iter, x => x), + iter => Iterator.prototype.take.bind(iter, 1), + iter => Iterator.prototype.drop.bind(iter, 0), + iter => Iterator.prototype.asIndexedPairs.bind(iter), + iter => Iterator.prototype.flatMap.bind(iter, x => [x]), +]; + +for (const method of methods) { + assertThrowsInstanceOf(method(undefined), TypeError); + assertThrowsInstanceOf(method(null), TypeError); + assertThrowsInstanceOf(method(0), TypeError); + assertThrowsInstanceOf(method(false), TypeError); + assertThrowsInstanceOf(method(''), TypeError); + assertThrowsInstanceOf(method(Symbol('')), TypeError); + assertThrowsInstanceOf(method({}), TypeError); + assertThrowsInstanceOf(method([]), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-next-done-throws.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-next-done-throws.js new file mode 100644 index 0000000000..72669beabc --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-next-done-throws.js @@ -0,0 +1,47 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods throw if `next.done` throws. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return { + get done() { + throw new TestError(); + } + }; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + assertThrowsInstanceOf(() => method(iterator).next(), TestError); + assertEq(iterator.closed, false); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-next-not-object.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-next-not-object.js new file mode 100644 index 0000000000..1d70ed1d62 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-next-not-object.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: Lazy %Iterator.prototype% methods throw if `next` call returns a non-object. +info: > + Iterator Helpers proposal 2.1.5 +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next(value) { + return value; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const methods = [ + iter => iter.map(x => x), + iter => iter.filter(x => x), + iter => iter.take(1), + iter => iter.drop(0), + iter => iter.asIndexedPairs(), + iter => iter.flatMap(x => [x]), +]; + +for (const method of methods) { + for (const value of [undefined, null, 0, false, '', Symbol('')]) { + const iterator = new TestIterator(); + assertEq(iterator.closed, false); + assertThrowsInstanceOf(() => method(iterator).next(value), TypeError); + assertEq(iterator.closed, false); + } +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-on-reentry.js b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-on-reentry.js new file mode 100644 index 0000000000..1095842f55 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/lazy-methods-throw-on-reentry.js @@ -0,0 +1,18 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +const methods = [ + iter => iter.map, + iter => iter.filter, + iter => iter.flatMap, +]; + +for (const method of methods) { + const iter = [1].values(); + const iterMethod = method(iter); + let iterHelper; + iterHelper = iterMethod.call(iter, x => iterHelper.next()); + assertThrowsInstanceOf(() => iterHelper.next(), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/call-next-on-iterator-while-iterating.js b/js/src/tests/non262/Iterator/prototype/map/call-next-on-iterator-while-iterating.js new file mode 100644 index 0000000000..a5a10120a0 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/call-next-on-iterator-while-iterating.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +/*--- +esid: pending +description: Call next on an iterator that is being iterated over. +info: +features: [iterator-helpers] +---*/ + +const iterator = [1, 2, 3].values() +const items = []; + +for (const item of iterator.map(x => x ** 2)) { + const nextItem = iterator.next(); + items.push(item, nextItem.value); +} + +assertEq(items[0], 1); +assertEq(items[1], 2); +assertEq(items[2], 9); +assertEq(items[3], undefined); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/clobber-symbol.js b/js/src/tests/non262/Iterator/prototype/map/clobber-symbol.js new file mode 100644 index 0000000000..8d5b2721e1 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/clobber-symbol.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// + +/*--- +esid: pending +description: %Iterator.prototype%.map works even if the global Symbol has been clobbered.. +info: +features: [iterator-helpers, Symbol, Symbol.iterator] +---*/ + +Symbol = undefined; +assertThrowsInstanceOf(() => Symbol.iterator, TypeError); + +const iterator = [0].values(); +assertEq( + iterator.map(x => x + 1).next().value, 1, + '`%Iterator.prototype%.map` still works after Symbol has been clobbered' +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/interleaved-map-calls.js b/js/src/tests/non262/Iterator/prototype/map/interleaved-map-calls.js new file mode 100644 index 0000000000..3253e06192 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/interleaved-map-calls.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Interleaved %Iterator.prototype%.map calls on the same iterator. +info: +features: [iterator-helpers] +---*/ + +const iterator = [1, 2, 3].values(); +const mapped1 = iterator.map(x => x); +const mapped2 = iterator.map(x => 0); + +assertEq(mapped1.next().value, 1); +assertEq(mapped2.next().value, 0); +assertEq(mapped1.next().value, 3); + +assertEq(mapped1.next().done, true); +assertEq(mapped2.next().done, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/length.js b/js/src/tests/non262/Iterator/prototype/map/length.js new file mode 100644 index 0000000000..d6bc1ca774 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/length.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [Symbol.iterator] +---*/ + +assertEq(Iterator.prototype.map.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.map, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/map.js b/js/src/tests/non262/Iterator/prototype/map/map.js new file mode 100644 index 0000000000..2cf5267be7 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/map.js @@ -0,0 +1,28 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +/*--- +esid: pending +description: %Iterator.prototype%.map value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +const map = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'map'); + +assertEq( + Iterator.prototype.map, map.value, + 'The value of `%Iterator.prototype%.map` is the same as the value in the property descriptor.' +); + +assertEq( + typeof map.value, 'function', + '%Iterator.prototype%.map is a function.' +); + +assertEq(map.enumerable, false); +assertEq(map.writable, true); +assertEq(map.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/mapper-not-callable-throw.js b/js/src/tests/non262/Iterator/prototype/map/mapper-not-callable-throw.js new file mode 100644 index 0000000000..334e0fd8f9 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/mapper-not-callable-throw.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Eagerly throw TypeError when `mapper` is not callable. +info: +features: [iterator-helpers] +---*/ + +assertThrowsInstanceOf(() => Iterator.prototype.map(undefined), TypeError); +assertThrowsInstanceOf(() => [].values().map(undefined), TypeError); + +assertThrowsInstanceOf(() => Iterator.prototype.map(null), TypeError); +assertThrowsInstanceOf(() => [].values().map(null), TypeError); + +assertThrowsInstanceOf(() => Iterator.prototype.map(0), TypeError); +assertThrowsInstanceOf(() => [].values().map(0), TypeError); + +assertThrowsInstanceOf(() => Iterator.prototype.map(false), TypeError); +assertThrowsInstanceOf(() => [].values().map(false), TypeError); + +assertThrowsInstanceOf(() => Iterator.prototype.map({}), TypeError); +assertThrowsInstanceOf(() => [].values().map({}), TypeError); + +assertThrowsInstanceOf(() => Iterator.prototype.map(''), TypeError); +assertThrowsInstanceOf(() => [].values().map(''), TypeError); + +assertThrowsInstanceOf(() => Iterator.prototype.map(Symbol('')), TypeError); +assertThrowsInstanceOf(() => [].values().map(Symbol('')), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/mutate-iterator-after-done.js b/js/src/tests/non262/Iterator/prototype/map/mutate-iterator-after-done.js new file mode 100644 index 0000000000..b38158d73a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/mutate-iterator-after-done.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Mutate an iterator after it has been mapped and returned done. +info: +features: [iterator-helpers] +---*/ + +const array = [1, 2, 3]; +const iterator = [1, 2, 3].values().map(x => x * 2); + +assertEq(iterator.next().value, 2); +assertEq(iterator.next().value, 4); +assertEq(iterator.next().value, 6); +assertEq(iterator.next().done, true); + +array.push(4); +assertEq(iterator.next().done, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/mutate-iterator.js b/js/src/tests/non262/Iterator/prototype/map/mutate-iterator.js new file mode 100644 index 0000000000..b1cc52fae5 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/mutate-iterator.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Mutate an iterator after it has been mapped. +info: +features: [iterator-helpers] +---*/ + +const array = [1, 2, 3]; +const iterator = array.values().map(x => x * 2); +array.push(4); + +assertEq(iterator.next().value, 2); +assertEq(iterator.next().value, 4); +assertEq(iterator.next().value, 6); +assertEq(iterator.next().value, 8); +assertEq(iterator.next().done, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/name.js b/js/src/tests/non262/Iterator/prototype/map/name.js new file mode 100644 index 0000000000..3ba024d6e7 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +/*--- +esid: pending +description: %Iterator.prototype%.map.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.map.name, 'map'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.map, 'name'); +assertEq(propertyDescriptor.value, 'map'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/output-at-generator-end.js b/js/src/tests/non262/Iterator/prototype/map/output-at-generator-end.js new file mode 100644 index 0000000000..69ef04a7b2 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/output-at-generator-end.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map outputs correct value at end of iterator. +info: +features: [iterator-helpers] +---*/ + +const iterator = [0].values().map(x => x); + +const iterRecord = iterator.next(); +assertEq(iterRecord.done, false); +assertEq(iterRecord.value, 0); + +let endRecord = iterator.next(); +assertEq(endRecord.done, true); +assertEq(endRecord.value, undefined); + +endRecord = iterator.next(); +assertEq(endRecord.done, true); +assertEq(endRecord.value, undefined); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/pass-lastValue-to-next.js b/js/src/tests/non262/Iterator/prototype/map/pass-lastValue-to-next.js new file mode 100644 index 0000000000..deb31e2aa2 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/pass-lastValue-to-next.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map passes lastValue to the `next` call. +info: > + Iterator Helpers Proposal 2.1.5.2 +features: [iterator-helpers] +---*/ + +let nextWasPassed; + +const iteratorWhereNextTakesValue = Object.setPrototypeOf({ + next: function(value) { + nextWasPassed = value; + + if (this.value < 3) + return { done: false, value: this.value++ }; + return { done: true, value: undefined }; + }, + value: 0, +}, Iterator.prototype); + +const mappedIterator = iteratorWhereNextTakesValue.map(x => x); + +assertEq(mappedIterator.next(1).value, 0); +assertEq(nextWasPassed, undefined); + +assertEq(mappedIterator.next(2).value, 1); +assertEq(nextWasPassed, 2); + +assertEq(mappedIterator.next(3).value, 2); +assertEq(nextWasPassed, 3); + +assertEq(mappedIterator.next(4).done, true); +assertEq(nextWasPassed, 4); + +// assertEq(mappedIterator.next(5).done, true); +// assertEq(nextWasPassed, 4); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion-in-iteratorValue.js b/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion-in-iteratorValue.js new file mode 100644 index 0000000000..f50bd0da10 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion-in-iteratorValue.js @@ -0,0 +1,55 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map does not call return when IteratorValue returns an abrupt completion. +info: > +features: [iterator-helpers] +---*/ + +const handlerProxy = log => new Proxy({}, { + get: (target, key, receiver) => (...args) => { + const target = args[0]; + const item = Reflect[key](...args); + + log.push(`${key}: ${args.filter(x => typeof x != 'object').map(x => x.toString())}`); + + switch (typeof item) { + case 'function': return item.bind(new Proxy(target, handlerProxy(log))); + case 'object': return new Proxy(item, handlerProxy(log)); + default: return item; + } + }, +}); + +const log = []; +const iterator = Object.setPrototypeOf({ + next: function() { + throw 'error'; + return { done: false, value: 0 }; + }, + return: function(value) { + log.push('close iterator'); + return { done: true, value }; + }, +}, Iterator.prototype); +const iteratorProxy = new Proxy(iterator, handlerProxy(log)); +const mappedProxy = iteratorProxy.map(x => x); + +try { + mappedProxy.next(); +} catch (exc) { + assertEq(exc, 'error'); +} + +console.log(log.join('\n')); + +assertEq( + log.join('\n'), +`get: map +get: next` +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion-in-yield.js b/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion-in-yield.js new file mode 100644 index 0000000000..4d4854da36 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion-in-yield.js @@ -0,0 +1,62 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map calls return when yield throws. +info: > +features: [iterator-helpers] +---*/ + +class TestError extends Error {} + +class TestIterator extends Iterator { + constructor(log) { + super(); + this.log = log; + } + + next() { + return {done: false, value: 0}; + } + + return(value) { + log.push('close iterator'); + return {done: true, value}; + } +} + +const handlerProxy = log => new Proxy({}, { + get: (target, key, receiver) => (...args) => { + const target = args[0]; + const item = Reflect[key](...args); + + log.push(`${key}: ${args.filter(x => typeof x != 'object').map(x => x.toString())}`); + + switch (typeof item) { + case 'function': return item.bind(new Proxy(target, handlerProxy(log))); + case 'object': return new Proxy(item, handlerProxy(log)); + default: return item; + } + }, +}); + +const log = []; +const iterator = new TestIterator(log); +const iteratorProxy = new Proxy(iterator, handlerProxy(log)); +const mappedProxy = iteratorProxy.map(x => x); + +mappedProxy.next(); +assertThrowsInstanceOf(() => mappedProxy.throw(new TestError()), TestError); +mappedProxy.next(); + +assertEq( + log.join('\n'), +`get: map +get: next +get: return +close iterator` +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion.js b/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion.js new file mode 100644 index 0000000000..ea0e10bdb4 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/proxy-abrupt-completion.js @@ -0,0 +1,54 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map accesses specified properties only. +info: > +features: [iterator-helpers] +---*/ + +const handlerProxy = log => new Proxy({}, { + get: (target, key, receiver) => (...args) => { + const target = args[0]; + const item = Reflect[key](...args); + + log.push(`${key}: ${args.filter(x => typeof x != 'object').map(x => x.toString())}`); + + switch (typeof item) { + case 'function': return item.bind(new Proxy(target, handlerProxy(log))); + case 'object': return new Proxy(item, handlerProxy(log)); + default: return item; + } + }, +}); + +const log = []; +const iterator = Object.setPrototypeOf({ + next: function() { + return { done: false, value: 0 }; + }, + return: function(value) { + log.push('close iterator'); + return { done: true, value }; + }, +}, Iterator.prototype); +const iteratorProxy = new Proxy(iterator, handlerProxy(log)); +const mappedProxy = iteratorProxy.map(x => { throw 'error'; }); + +try { + mappedProxy.next(); +} catch (exc) { + assertEq(exc, 'error'); +} + +assertEq( + log.join('\n'), +`get: map +get: next +get: return +close iterator` +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/proxy-accesses.js b/js/src/tests/non262/Iterator/prototype/map/proxy-accesses.js new file mode 100644 index 0000000000..ba6dfd7ece --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/proxy-accesses.js @@ -0,0 +1,94 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.map accesses specified properties only. +info: > +features: [iterator-helpers] +---*/ + +const handlerProxy = log => new Proxy({}, { + get: (target, key, receiver) => (...args) => { + const target = args[0]; + const item = Reflect[key](...args); + + log.push(`${key}: ${args.filter(x => typeof x != 'object').map(x => x.toString())}`); + + switch (typeof item) { + case 'function': return item.bind(new Proxy(target, handlerProxy(log))); + case 'object': return new Proxy(item, handlerProxy(log)); + default: return item; + } + }, +}); + +const log = []; +const iterator = Object.setPrototypeOf({ + next: function() { + if (this.value < 3) + return { done: false, value: this.value++ }; + return { done: true, value: undefined }; + }, + value: 0, +}, Iterator.prototype); +const iteratorProxy = new Proxy(iterator, handlerProxy(log)); +const mappedProxy = iteratorProxy.map(x => x); + +for (const item of mappedProxy) { +} + +assertEq( + log.join('\n'), +`get: map +get: next +get: value +get: value +getOwnPropertyDescriptor: value +has: enumerable +get: enumerable +has: configurable +get: configurable +has: value +get: value +has: writable +get: writable +has: get +has: set +defineProperty: value +set: value,1 +get: value +get: value +getOwnPropertyDescriptor: value +has: enumerable +get: enumerable +has: configurable +get: configurable +has: value +get: value +has: writable +get: writable +has: get +has: set +defineProperty: value +set: value,2 +get: value +get: value +getOwnPropertyDescriptor: value +has: enumerable +get: enumerable +has: configurable +get: configurable +has: value +get: value +has: writable +get: writable +has: get +has: set +defineProperty: value +set: value,3 +get: value` +); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/reenter-map-generator-from-mapper.js b/js/src/tests/non262/Iterator/prototype/map/reenter-map-generator-from-mapper.js new file mode 100644 index 0000000000..b7a0d56d5c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/reenter-map-generator-from-mapper.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// Re-entering the map() generator from the called mapper fails. + +let iterator; +function mapper(x) { + let n = iterator.next(); + return x; +} +iterator = [0].values().map(mapper); + +assertThrowsInstanceOf(iterator.next, TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/this-not-iterator-throw.js b/js/src/tests/non262/Iterator/prototype/map/this-not-iterator-throw.js new file mode 100644 index 0000000000..aaed1f2617 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/this-not-iterator-throw.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Eagerly throw TypeError when `this` is not an iterator. +info: +features: [iterator-helpers] +---*/ + +const mapper = (x) => x; + +assertThrowsInstanceOf(() => Iterator.prototype.map.call(undefined, mapper), TypeError); +assertThrowsInstanceOf(() => Iterator.prototype.map.call(null, mapper), TypeError); +assertThrowsInstanceOf(() => Iterator.prototype.map.call(0, mapper), TypeError); +assertThrowsInstanceOf(() => Iterator.prototype.map.call(false, mapper), TypeError); +assertThrowsInstanceOf(() => Iterator.prototype.map.call({}, mapper), TypeError); +assertThrowsInstanceOf(() => Iterator.prototype.map.call('', mapper), TypeError); +assertThrowsInstanceOf(() => Iterator.prototype.map.call(new Symbol(''), mapper), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/this-value-array-throws.js b/js/src/tests/non262/Iterator/prototype/map/this-value-array-throws.js new file mode 100644 index 0000000000..d8573bb3ce --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/this-value-array-throws.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: TypeError is thrown if `this` is an Array. +info: +features: [Symbol.iterator] +---*/ + +assertThrowsInstanceOf(() => Iterator.prototype.map.call([], x => x), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/throw-when-iterator-returns-non-object.js b/js/src/tests/non262/Iterator/prototype/map/throw-when-iterator-returns-non-object.js new file mode 100644 index 0000000000..287507a2f6 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/throw-when-iterator-returns-non-object.js @@ -0,0 +1,24 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Throw TypeError if `next` call returns non-object. +info: +features: [iterator-helpers] +---*/ + +const iterator = returnValue => Object.setPrototypeOf({ + next: () => returnValue, +}, Iterator.prototype); +const mapper = x => x; + +assertThrowsInstanceOf(() => iterator(undefined).map(mapper).next(), TypeError); +assertThrowsInstanceOf(() => iterator(null).map(mapper).next(), TypeError); +assertThrowsInstanceOf(() => iterator(0).map(mapper).next(), TypeError); +assertThrowsInstanceOf(() => iterator(false).map(mapper).next(), TypeError); +assertThrowsInstanceOf(() => iterator('').map(mapper).next(), TypeError); +assertThrowsInstanceOf(() => iterator(Symbol()).map(mapper).next(), TypeError); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/map/values-pass-through-chained-maps-to-next.js b/js/src/tests/non262/Iterator/prototype/map/values-pass-through-chained-maps-to-next.js new file mode 100644 index 0000000000..d49daebb87 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/map/values-pass-through-chained-maps-to-next.js @@ -0,0 +1,43 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: Multiple chained %Iterator.prototype%.map calls pass `lastValue` to the iterator's `next` call. +info: > + Iterator Helpers Proposal 2.1.5.2 +features: [iterator-helpers] +---*/ + +let nextWasPassed; + +const iteratorWhereNextTakesValue = Object.setPrototypeOf({ + next: function(value) { + nextWasPassed = value; + + if (this.value < 3) + return { done: false, value: this.value++ }; + return { done: true, value: undefined }; + }, + value: 0, +}, Iterator.prototype); + +const mappedIterator = iteratorWhereNextTakesValue.map(x => 2 * x).map(x => 1 + x); + +assertEq(mappedIterator.next(1).value, 1); +assertEq(nextWasPassed, undefined); + +assertEq(mappedIterator.next(2).value, 3); +assertEq(nextWasPassed, 2); + +assertEq(mappedIterator.next(3).value, 5); +assertEq(nextWasPassed, 3); + +assertEq(mappedIterator.next(4).done, true); +assertEq(nextWasPassed, 4); + +assertEq(mappedIterator.next(5).done, true); +assertEq(nextWasPassed, 4); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/accumulator-set-to-initial-value.js b/js/src/tests/non262/Iterator/prototype/reduce/accumulator-set-to-initial-value.js new file mode 100644 index 0000000000..4395e1c7d4 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/accumulator-set-to-initial-value.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const reducer = (acc, value) => acc; +const iterator = [1, 2, 3].values(); + +assertEq(iterator.reduce(reducer, 0), 0); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/check-fn-after-getting-iterator.js b/js/src/tests/non262/Iterator/prototype/reduce/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..4e4852326a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/check-fn-after-getting-iterator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends Iterator { + next() { + return {done: true}; + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +assertThrowsInstanceOf(() => iter.reduce(1), TypeError); + +assertEq( + log.join('\n'), + `get: reduce +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/descriptor.js b/js/src/tests/non262/Iterator/prototype/reduce/descriptor.js new file mode 100644 index 0000000000..0fb0ff01b3 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.prototype.reduce +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'reduce'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/empty-iterator-without-initial-value-throws.js b/js/src/tests/non262/Iterator/prototype/reduce/empty-iterator-without-initial-value-throws.js new file mode 100644 index 0000000000..d8e0a595cf --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/empty-iterator-without-initial-value-throws.js @@ -0,0 +1,7 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [].values(); +assertThrowsInstanceOf(() => iter.reduce((x, y) => x + y), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/error-from-correct-realm.js b/js/src/tests/non262/Iterator/prototype/reduce/error-from-correct-realm.js new file mode 100644 index 0000000000..96634ba62b --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/error-from-correct-realm.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.reduce(), TypeError); +assertThrowsInstanceOf( + otherGlobal.Iterator.prototype.reduce.bind(iter), + otherGlobal.TypeError, + 'TypeError comes from the realm of the method.', +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/iterator-empty-return-initial-value.js b/js/src/tests/non262/Iterator/prototype/reduce/iterator-empty-return-initial-value.js new file mode 100644 index 0000000000..30c15a48fe --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/iterator-empty-return-initial-value.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const reducer = (x, y) => 0; +const iterator = [].values(); + +assertEq(iterator.reduce(reducer, 1), 1); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/iterator-next-return-non-object-throws.js b/js/src/tests/non262/Iterator/prototype/reduce/iterator-next-return-non-object-throws.js new file mode 100644 index 0000000000..d77789f04a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/iterator-next-return-non-object-throws.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + constructor(value) { + super(); + this.value = value; + } + + next() { + return this.value; + } +} + +const sum = (x, y) => x + y; + +let iter = new TestIterator(undefined); +assertThrowsInstanceOf(() => iter.reduce(sum), TypeError); +iter = new TestIterator(null); +assertThrowsInstanceOf(() => iter.reduce(sum), TypeError); +iter = new TestIterator(0); +assertThrowsInstanceOf(() => iter.reduce(sum), TypeError); +iter = new TestIterator(false); +assertThrowsInstanceOf(() => iter.reduce(sum), TypeError); +iter = new TestIterator(''); +assertThrowsInstanceOf(() => iter.reduce(sum), TypeError); +iter = new TestIterator(Symbol('')); +assertThrowsInstanceOf(() => iter.reduce(sum), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/left-associative.js b/js/src/tests/non262/Iterator/prototype/reduce/left-associative.js new file mode 100644 index 0000000000..a6208504b6 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/left-associative.js @@ -0,0 +1,7 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +assertEq([1, 2, 3].values().reduce((x, y) => `(${x}+${y})`, 0), '(((0+1)+2)+3)'); +assertEq([1, 2, 3].values().reduce((x, y) => `(${x}+${y})`), '((1+2)+3)'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/length.js b/js/src/tests/non262/Iterator/prototype/reduce/length.js new file mode 100644 index 0000000000..0780e684a8 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.prototype.reduce. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.reduce, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/name.js b/js/src/tests/non262/Iterator/prototype/reduce/name.js new file mode 100644 index 0000000000..1269bb4b52 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.prototype.reduce. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.reduce, 'name'); +assertEq(propDesc.value, 'reduce'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/next-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/reduce/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..0fbeb995f1 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/next-throws-iterator-not-closed.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const sum = (x, y) => x + y; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.reduce(sum), Error); +assertEq(iter.closed, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/no-initial-value-set-accumulator-to-first-value.js b/js/src/tests/non262/Iterator/prototype/reduce/no-initial-value-set-accumulator-to-first-value.js new file mode 100644 index 0000000000..a2b0008e6a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/no-initial-value-set-accumulator-to-first-value.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const reducer = (acc, value) => acc; +const iterator = [1, 2, 3].values(); + +assertEq(iterator.reduce(reducer), 1); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/proxy.js b/js/src/tests/non262/Iterator/prototype/reduce/proxy.js new file mode 100644 index 0000000000..34569cf044 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// This test checks that %Iterator.prototype%.reduce only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends Iterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return {done: false, value}; + } + return {done: true}; + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +iter.reduce((x, y) => x + y); + +assertEq( + log.join('\n'), + `get: reduce +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/reduce.js b/js/src/tests/non262/Iterator/prototype/reduce/reduce.js new file mode 100644 index 0000000000..d824da211d --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/reduce.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const reducer = (acc, value) => acc + value; +const iterator = [1, 2, 3].values(); + +assertEq(iterator.reduce(reducer, 0), 6); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/reducer-not-callable-throws.js b/js/src/tests/non262/Iterator/prototype/reduce/reducer-not-callable-throws.js new file mode 100644 index 0000000000..c33e5e4f9c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/reducer-not-callable-throws.js @@ -0,0 +1,20 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + return { done: false, value: 0 }; + } +} + +const iter = new TestIterator(); +assertThrowsInstanceOf(() => iter.reduce(), TypeError); +assertThrowsInstanceOf(() => iter.reduce(undefined), TypeError); +assertThrowsInstanceOf(() => iter.reduce(null), TypeError); +assertThrowsInstanceOf(() => iter.reduce(0), TypeError); +assertThrowsInstanceOf(() => iter.reduce(false), TypeError); +assertThrowsInstanceOf(() => iter.reduce(''), TypeError); +assertThrowsInstanceOf(() => iter.reduce(Symbol('')), TypeError); +assertThrowsInstanceOf(() => iter.reduce({}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/reducer-throws-iterator-closed.js b/js/src/tests/non262/Iterator/prototype/reduce/reducer-throws-iterator-closed.js new file mode 100644 index 0000000000..592fa80d17 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/reducer-throws-iterator-closed.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + return { done: this.closed, value: undefined }; + } + + closed = false; + return() { + this.closed = true; + } +} + +const reducer = (x, y) => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.reduce(reducer), Error); +assertEq(iter.closed, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/this-not-iterator-throws.js b/js/src/tests/non262/Iterator/prototype/reduce/this-not-iterator-throws.js new file mode 100644 index 0000000000..d4804c51e2 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/this-not-iterator-throws.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const sum = (x, y) => x + y; +assertThrowsInstanceOf(Iterator.prototype.reduce.bind(undefined, sum), TypeError); +assertThrowsInstanceOf(Iterator.prototype.reduce.bind({}, sum), TypeError); +assertThrowsInstanceOf(Iterator.prototype.reduce.bind({next: 0}, sum), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/reduce/value-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/reduce/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..7b43eedcc6 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/reduce/value-throws-iterator-not-closed.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return new Proxy({done: false}, {get: (target, key, receiver) => { + if (key === 'value') + throw new TestError(); + return 0; + }}); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +assertThrowsInstanceOf(() => iterator.reduce((x, y) => x + y, 0), TestError); +assertEq(iterator.closed, false, 'iterator remains unclosed'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/check-fn-after-getting-iterator.js b/js/src/tests/non262/Iterator/prototype/some/check-fn-after-getting-iterator.js new file mode 100644 index 0000000000..f81f660916 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/check-fn-after-getting-iterator.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class TestIterator extends Iterator { + next() { + return {done: true}; + } +} + +const iter = new Proxy(new TestIterator(), handlerProxy); +assertThrowsInstanceOf(() => iter.some(1), TypeError); + +assertEq( + log.join('\n'), + `get: some +get: next` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/coerce-result-to-boolean.js b/js/src/tests/non262/Iterator/prototype/some/coerce-result-to-boolean.js new file mode 100644 index 0000000000..5bebdb06ea --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/coerce-result-to-boolean.js @@ -0,0 +1,23 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = (value) => value; +assertEq([true].values().some(fn), true); +assertEq([1].values().some(fn), true); +assertEq([[]].values().some(fn), true); +assertEq([{}].values().some(fn), true); +assertEq(['test'].values().some(fn), true); + +assertEq([false].values().some(fn), false); +assertEq([0].values().some(fn), false); +assertEq([''].values().some(fn), false); +assertEq([null].values().some(fn), false); +assertEq([undefined].values().some(fn), false); +assertEq([NaN].values().some(fn), false); +assertEq([-0].values().some(fn), false); +assertEq([0n].values().some(fn), false); + +const htmlDDA = createIsHTMLDDA(); +assertEq([htmlDDA].values().some(fn), false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/descriptor.js b/js/src/tests/non262/Iterator/prototype/some/descriptor.js new file mode 100644 index 0000000000..dfafc4aa5f --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.prototype.some +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'some'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/error-from-correct-realm.js b/js/src/tests/non262/Iterator/prototype/some/error-from-correct-realm.js new file mode 100644 index 0000000000..ba68c6e7c1 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/error-from-correct-realm.js @@ -0,0 +1,16 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const otherGlobal = newGlobal({newCompartment: true}); +assertEq(TypeError !== otherGlobal.TypeError, true); + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.some(), TypeError); +assertThrowsInstanceOf( + otherGlobal.Iterator.prototype.some.bind(iter), + otherGlobal.TypeError, + 'TypeError comes from the realm of the method.', +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/fn-not-callable-throws.js b/js/src/tests/non262/Iterator/prototype/some/fn-not-callable-throws.js new file mode 100644 index 0000000000..52be15391f --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/fn-not-callable-throws.js @@ -0,0 +1,15 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [].values(); + +assertThrowsInstanceOf(() => iter.some(), TypeError); +assertThrowsInstanceOf(() => iter.some(undefined), TypeError); +assertThrowsInstanceOf(() => iter.some(null), TypeError); +assertThrowsInstanceOf(() => iter.some(0), TypeError); +assertThrowsInstanceOf(() => iter.some(false), TypeError); +assertThrowsInstanceOf(() => iter.some(''), TypeError); +assertThrowsInstanceOf(() => iter.some(Symbol('')), TypeError); +assertThrowsInstanceOf(() => iter.some({}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/fn-throws-close-iterator.js b/js/src/tests/non262/Iterator/prototype/some/fn-throws-close-iterator.js new file mode 100644 index 0000000000..bbe751e175 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/fn-throws-close-iterator.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + return { done: this.closed }; + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => { throw new Error(); }; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.some(fn), Error); +assertEq(iter.closed, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/length.js b/js/src/tests/non262/Iterator/prototype/some/length.js new file mode 100644 index 0000000000..c9d1f986f6 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.prototype.some. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.some, 'length'); +assertEq(propDesc.value, 1); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/name.js b/js/src/tests/non262/Iterator/prototype/some/name.js new file mode 100644 index 0000000000..679e75460c --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.prototype.some. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.some, 'name'); +assertEq(propDesc.value, 'some'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/next-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/some/next-throws-iterator-not-closed.js new file mode 100644 index 0000000000..4bdf372908 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/next-throws-iterator-not-closed.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + throw new Error(); + } + + closed = false; + return() { + this.closed = true; + } +} + +const fn = () => {}; +const iter = new TestIterator(); + +assertEq(iter.closed, false); +assertThrowsInstanceOf(() => iter.some(fn), Error); +assertEq(iter.closed, false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/proxy.js b/js/src/tests/non262/Iterator/prototype/some/proxy.js new file mode 100644 index 0000000000..a444a2d5cb --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// This test checks that %Iterator.prototype%.some only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends Iterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return {done: false, value}; + } + return {done: true}; + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +assertEq(iter.some(x => x % 2 == 1), true); + +assertEq( + log.join('\n'), + `get: some +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: return` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/return-false-if-none-match.js b/js/src/tests/non262/Iterator/prototype/some/return-false-if-none-match.js new file mode 100644 index 0000000000..00fab94245 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/return-false-if-none-match.js @@ -0,0 +1,11 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 3, 5].values(); +const fn = (value) => value % 2 == 0; + +assertEq(iter.some(fn), false); + +assertEq([].values().some(x => x), false); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/short-circuit-on-true.js b/js/src/tests/non262/Iterator/prototype/some/short-circuit-on-true.js new file mode 100644 index 0000000000..4f0d67bf09 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/short-circuit-on-true.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 2, 3].values(); +const log = []; +const fn = (value) => { + log.push(value.toString()); + return value % 2 == 0; +}; + +assertEq(iter.some(fn), true); +assertEq(log.join(','), '1,2'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/this-not-iterator-throws.js b/js/src/tests/non262/Iterator/prototype/some/this-not-iterator-throws.js new file mode 100644 index 0000000000..0659ec037e --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/this-not-iterator-throws.js @@ -0,0 +1,9 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const fn = x => x; +assertThrowsInstanceOf(Iterator.prototype.some.bind(undefined, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.some.bind({}, fn), TypeError); +assertThrowsInstanceOf(Iterator.prototype.some.bind({next: 0}, fn), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/some/value-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/some/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..87bf5df033 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/some/value-throws-iterator-not-closed.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return new Proxy({done: false}, {get: (target, key, receiver) => { + if (key === 'value') + throw new TestError(); + return 0; + }}); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +assertThrowsInstanceOf(() => iterator.some(x => x), TestError); +assertEq(iterator.closed, false, 'iterator remains unclosed'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/take-drop-throw-eagerly-on-negative.js b/js/src/tests/non262/Iterator/prototype/take-drop-throw-eagerly-on-negative.js new file mode 100644 index 0000000000..b870e31e72 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take-drop-throw-eagerly-on-negative.js @@ -0,0 +1,30 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: `take` and `drop` throw eagerly when passed negative numbers, after rounding towards 0. +info: > + Iterator Helpers proposal 2.1.5.4 and 2.1.5.5 +features: [iterator-helpers] +---*/ + +const iter = [].values(); +const methods = [ + value => iter.take(value), + value => iter.drop(value), +]; + +for (const method of methods) { + assertThrowsInstanceOf(() => method(-1), RangeError); + assertThrowsInstanceOf(() => method(-Infinity), RangeError); + + method(NaN); + method(-NaN); + method(-0); + method(-0.9); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/take-drop-throw-eagerly-on-non-integer.js b/js/src/tests/non262/Iterator/prototype/take-drop-throw-eagerly-on-non-integer.js new file mode 100644 index 0000000000..3e1333ab37 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take-drop-throw-eagerly-on-non-integer.js @@ -0,0 +1,32 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: `take` and `drop` throw eagerly when passed values that can't be converted to numbers. +info: > + Iterator Helpers proposal 2.1.5.4 and 2.1.5.5 +features: [iterator-helpers] +---*/ + +const iter = [].values(); +const methods = [ + value => iter.take(value), + value => iter.drop(value), +]; + +const objectWithToPrimitive = { + [Symbol.toPrimitive]() { + return {}; + } +}; + +for (const method of methods) { + assertThrowsInstanceOf(() => method(0n), TypeError); + assertThrowsInstanceOf(() => method(Symbol('')), TypeError); + assertThrowsInstanceOf(() => method(objectWithToPrimitive), TypeError); +} + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/take/close-iterator-when-none-remaining.js b/js/src/tests/non262/Iterator/prototype/take/close-iterator-when-none-remaining.js new file mode 100644 index 0000000000..1c78db378b --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take/close-iterator-when-none-remaining.js @@ -0,0 +1,40 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.take closes the iterator when remaining is 0. +info: > + Iterator Helpers proposal 2.1.5.4 +features: [iterator-helpers] +---*/ + +class TestIterator extends Iterator { + next() { + return {done: false, value: 1}; + } + + closed = false; + return() { + this.closed = true; + return {done: true}; + } +} + +const iter = new TestIterator(); +const iterTake = iter.take(1); + +let result = iterTake.next(); +assertEq(result.done, false); +assertEq(result.value, 1); +assertEq(iter.closed, false); + +result = iterTake.next(); +assertEq(result.done, true); +assertEq(result.value, undefined); +assertEq(iter.closed, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); + diff --git a/js/src/tests/non262/Iterator/prototype/take/length.js b/js/src/tests/non262/Iterator/prototype/take/length.js new file mode 100644 index 0000000000..4e5efa6cea --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take/length.js @@ -0,0 +1,22 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +// + +/*--- +esid: pending +description: %Iterator.prototype%.take length value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +includes: [propertyHelper.js] +features: [Symbol.iterator] +---*/ + +assertEq(Iterator.prototype.take.length, 1); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.take, 'length'); +assertEq(propertyDescriptor.value, 1); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/take/name.js b/js/src/tests/non262/Iterator/prototype/take/name.js new file mode 100644 index 0000000000..87f75b63dd --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take/name.js @@ -0,0 +1,19 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) +/*--- +esid: pending +description: %Iterator.prototype%.take.name value and descriptor. +info: > + 17 ECMAScript Standard Built-in Objects +features: [iterator-helpers] +---*/ + +assertEq(Iterator.prototype.take.name, 'take'); + +const propertyDescriptor = Reflect.getOwnPropertyDescriptor(Iterator.prototype.take, 'name'); +assertEq(propertyDescriptor.value, 'take'); +assertEq(propertyDescriptor.enumerable, false); +assertEq(propertyDescriptor.writable, false); +assertEq(propertyDescriptor.configurable, true); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/take/take-more-than-available.js b/js/src/tests/non262/Iterator/prototype/take/take-more-than-available.js new file mode 100644 index 0000000000..b9d07b6122 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take/take-more-than-available.js @@ -0,0 +1,52 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) + +// +// +/*--- +esid: pending +description: %Iterator.prototype%.take returns if the iterator is done. +info: > + Iterator Helpers proposal 2.1.5.4 + 2. Repeat, + ... + c. Let next be ? IteratorStep(iterated, lastValue). + d. If next is false, return undefined. +features: [iterator-helpers] +---*/ + +let iter = [1, 2].values().take(3); +for (const expected of [1, 2]) { + const result = iter.next(); + assertEq(result.value, expected); + assertEq(result.done, false); +} +let result = iter.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); + +class TestIterator extends Iterator { + counter = 0; + next() { + return {done: ++this.counter >= 2, value: undefined}; + } + + closed = false; + return(value) { + this.closed = true; + return {done: true, value}; + } +} + +iter = new TestIterator(); +let taken = iter.take(10); +for (const value of taken) { + assertEq(value, undefined); +} +result = taken.next(); +assertEq(result.value, undefined); +assertEq(result.done, true); +assertEq(iter.counter, 2); +assertEq(iter.closed, false); + +if (typeof reportCompare == 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/take/take.js b/js/src/tests/non262/Iterator/prototype/take/take.js new file mode 100644 index 0000000000..d94671c664 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/take/take.js @@ -0,0 +1,26 @@ +// |reftest| skip-if(!this.hasOwnProperty('AsyncIterator')) + +/*--- +esid: pending +description: Smoketest of %Iterator.prototype%.take. +info: > + Iterator Helpers proposal 2.1.5.4 +features: [iterator-helpers] +---*/ + +let iter = [1, 2, 3].values().take(2); + +for (const v of [1, 2]) { + let result = iter.next(); + assertEq(result.done, false); + assertEq(result.value, v); +} + +assertEq(iter.next().done, true); + +// `take`, when called without arguments, has a limit of undefined, +// which converts to 0. +assertEq(['test'].values().take().next().done, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/create-in-current-realm.js b/js/src/tests/non262/Iterator/prototype/toArray/create-in-current-realm.js new file mode 100644 index 0000000000..850729e5e3 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/create-in-current-realm.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const otherGlobal = newGlobal({newCompartment: true}); + +let array = [1, 2, 3].values().toArray(); +assertEq(array instanceof Array, true); +assertEq(array instanceof otherGlobal.Array, false); + +array = otherGlobal.Iterator.prototype.toArray.call([1, 2, 3].values()); +assertEq(array instanceof Array, false); +assertEq(array instanceof otherGlobal.Array, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/descriptor.js b/js/src/tests/non262/Iterator/prototype/toArray/descriptor.js new file mode 100644 index 0000000000..8daa56fd4a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/descriptor.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + Descriptor property of Iterator.prototype.toArray +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype, 'toArray'); +assertEq(typeof propDesc.value, 'function'); +assertEq(propDesc.writable, true); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/iterator-empty.js b/js/src/tests/non262/Iterator/prototype/toArray/iterator-empty.js new file mode 100644 index 0000000000..97c14f0d06 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/iterator-empty.js @@ -0,0 +1,10 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [].values(); +const array = iter.toArray(); + +assertEq(Array.isArray(array), true); +assertEq(array.length, 0); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/length.js b/js/src/tests/non262/Iterator/prototype/toArray/length.js new file mode 100644 index 0000000000..493a225ce7 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/length.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + The `length` property of Iterator.prototype.toArray. +info: | + ES7 section 17: Unless otherwise specified, the length property of a built-in + Function object has the attributes { [[Writable]]: false, [[Enumerable]]: + false, [[Configurable]]: true }. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.toArray, 'length'); +assertEq(propDesc.value, 0); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/name.js b/js/src/tests/non262/Iterator/prototype/toArray/name.js new file mode 100644 index 0000000000..b382c3a3a6 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/name.js @@ -0,0 +1,13 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +/*--- + `name` property of Iterator.prototype.toArray. +---*/ + +const propDesc = Reflect.getOwnPropertyDescriptor(Iterator.prototype.toArray, 'name'); +assertEq(propDesc.value, 'toArray'); +assertEq(propDesc.writable, false); +assertEq(propDesc.enumerable, false); +assertEq(propDesc.configurable, true); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/next-throws.js b/js/src/tests/non262/Iterator/prototype/toArray/next-throws.js new file mode 100644 index 0000000000..c93ecdd324 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/next-throws.js @@ -0,0 +1,14 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestIterator extends Iterator { + next() { + throw new Error(); + } +} + +const iter = new TestIterator(); + +assertThrowsInstanceOf(() => iter.toArray(), Error); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/proxy.js b/js/src/tests/non262/Iterator/prototype/toArray/proxy.js new file mode 100644 index 0000000000..83024eeb8a --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/proxy.js @@ -0,0 +1,44 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally +// +// This test checks that %Iterator.prototype%.toArray only gets the `next` method off of the +// iterator once, and never accesses the @@iterator property. +const log = []; +const handlerProxy = new Proxy({}, { + get: (target, key, receiver) => (...args) => { + log.push(`${key}: ${args[1]?.toString()}`); + return Reflect[key](...args); + }, +}); + +class Counter extends Iterator { + value = 0; + next() { + const value = this.value; + if (value < 2) { + this.value = value + 1; + return {done: false, value}; + } + return {done: true}; + } +} + +const iter = new Proxy(new Counter(), handlerProxy); +const array = iter.toArray(); + +assertEq( + log.join('\n'), + `get: toArray +get: next +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value +set: value +getOwnPropertyDescriptor: value +defineProperty: value +get: value` +); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/this-not-iterator-throws.js b/js/src/tests/non262/Iterator/prototype/toArray/this-not-iterator-throws.js new file mode 100644 index 0000000000..1442d22557 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/this-not-iterator-throws.js @@ -0,0 +1,8 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +assertThrowsInstanceOf(Iterator.prototype.toArray.bind(undefined), TypeError); +assertThrowsInstanceOf(Iterator.prototype.toArray.bind({}), TypeError); +assertThrowsInstanceOf(Iterator.prototype.toArray.bind({next: 0}), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/toArray.js b/js/src/tests/non262/Iterator/prototype/toArray/toArray.js new file mode 100644 index 0000000000..29c99525fb --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/toArray.js @@ -0,0 +1,17 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +const iter = [1, 2, 3].values(); +assertEq(Array.isArray(iter), false); + +const array = iter.toArray(); +assertEq(Array.isArray(array), true); +assertEq(array.length, 3); + +const expected = [1, 2, 3]; +for (const item of array) { + const expect = expected.shift(); + assertEq(item, expect); +} + +if (typeof reportCompare === 'function') + reportCompare(0, 0); diff --git a/js/src/tests/non262/Iterator/prototype/toArray/value-throws-iterator-not-closed.js b/js/src/tests/non262/Iterator/prototype/toArray/value-throws-iterator-not-closed.js new file mode 100644 index 0000000000..ca56de8a66 --- /dev/null +++ b/js/src/tests/non262/Iterator/prototype/toArray/value-throws-iterator-not-closed.js @@ -0,0 +1,25 @@ +// |reftest| skip-if(!this.hasOwnProperty('Iterator')) -- Iterator is not enabled unconditionally + +class TestError extends Error {} +class TestIterator extends Iterator { + next() { + return new Proxy({done: false}, {get: (target, key, receiver) => { + if (key === 'value') + throw new TestError(); + return 0; + }}); + } + + closed = false; + return() { + closed = true; + } +} + +const iterator = new TestIterator(); +assertEq(iterator.closed, false, 'iterator starts unclosed'); +assertThrowsInstanceOf(() => iterator.toArray(), TestError); +assertEq(iterator.closed, false, 'iterator remains unclosed'); + +if (typeof reportCompare === 'function') + reportCompare(0, 0); |