Skip to content

Commit 8310ec8

Browse files
tetsuharuohzekiAhmad Saleem
authored andcommitted
[JSC] Update iterator built-ins to reflect the change of github:tc39/ecma262#3776
https://bugs.webkit.org/show_bug.cgi?id=315085 Reviewed by Yusuke Suzuki. The change of tc39/ecma262#3776 for iterator helper builtins are categorized into: - Our implementations should be changed due to they should throw a `RangeError` corresponds to the user input value. - `Iterator.prototype.drop` - `Iterator.prototype.take` - The semantics has been a bit changed but it's not related to our implementation actually. - `Iterator.prototype.filter` - `Iterator.prototype.find` - `Iterator.prototype.flatMap` - `Iterator.prototype.forEach` - `Iterator.prototype.map` - `Iterator.prototype.reduce` - `Iterator.prototype.some` Tests: JSTests/stress/iterator-prototype-drop-bug315085.js JSTests/stress/iterator-prototype-take-bug315085.js * JSTests/stress/iterator-prototype-drop-bug315085.js: Added. https://commits.webkit.org/284597@main implements `Iterator.prototype.drop` but no stress tests. This change adds a minimum testcase. * JSTests/stress/iterator-prototype-take-bug315085.js: Added. https://commits.webkit.org/284597@main implements `Iterator.prototype.take` but no stress tests. This change adds a minimum testcase. * Source/JavaScriptCore/builtins/JSIteratorPrototype.js: Canonical link: https://commits.webkit.org/315276@main
1 parent 660e785 commit 8310ec8

3 files changed

Lines changed: 240 additions & 2 deletions

File tree

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
function shouldThrow(caseName, fn, expectedErrorCtor, expectedErrorMessage) {
2+
if (!caseName)
3+
throw new Error(`must specify test case name`);
4+
5+
const expected = `${expectedErrorCtor.name}(${expectedErrorMessage})`;
6+
try {
7+
fn();
8+
throw new Error(`${caseName}: Expected to throw ${expected}, but succeeded`);
9+
} catch (e) {
10+
const actual = `${e.name}(${e.message})`;
11+
if (!(e instanceof expectedErrorCtor) || e.message !== expectedErrorMessage)
12+
throw new Error(`${caseName}: Expected ${expected} but got ${actual}`);
13+
}
14+
}
15+
16+
function shouldNotThrow(fn, caseName) {
17+
if (!caseName)
18+
throw new Error(`must specify message`);
19+
20+
try {
21+
fn();
22+
} catch (e) {
23+
const actual = `${e.name}(${e.message})`;
24+
throw new Error(`${caseName}: Expected not thrown but got ${actual}`);
25+
}
26+
}
27+
28+
function shouldBe(a, b, testname) {
29+
if (a !== b)
30+
throw new Error(`${testname}: Expected ${b} but got ${a}`);
31+
}
32+
33+
class TestIterator extends Iterator {
34+
value = 0;
35+
isClosed = false;
36+
isDone = false;
37+
constructor(max = 3) {
38+
super();
39+
if (max < 0) {
40+
throw new RangeError('max must be >= 0');
41+
}
42+
this.max = +max;
43+
}
44+
next() {
45+
const value = this.value;
46+
if (value > this.max) {
47+
this.isDone = true;
48+
return {
49+
done: true,
50+
value: undefined,
51+
};
52+
}
53+
54+
this.value += 1;
55+
return {
56+
done: false,
57+
value,
58+
};
59+
}
60+
return() {
61+
this.isClosed = true;
62+
return {
63+
done: true,
64+
value: undefined,
65+
};
66+
}
67+
}
68+
69+
function test(target, limit) {
70+
const newIter = Iterator.prototype.drop.call(target, limit);
71+
return newIter;
72+
}
73+
74+
{
75+
const validUpperLowerBoundArgumentTestCases = [
76+
[Number.POSITIVE_INFINITY, ],
77+
[Number.MAX_SAFE_INTEGER, `Number.MAX_SAFE_INTEGER`],
78+
[0, ],
79+
[0.0, `0.0`],
80+
];
81+
82+
for (const [value, name] of validUpperLowerBoundArgumentTestCases) {
83+
const label = name ?? String(name);
84+
const iter = new TestIterator();
85+
86+
shouldNotThrow(() => {
87+
test(iter, value);
88+
}, `${label}: should be valid for the 1st argument`);
89+
shouldBe(iter.isClosed, false, `${label}: the iterator should not be closed`);
90+
}
91+
}
92+
93+
{
94+
const invalidOutOfRangeArgumentTestCases = [
95+
[Number.MAX_VALUE, `Number.MAX_VALUE`],
96+
[Number.MAX_SAFE_INTEGER + 1, `Number.MAX_SAFE_INTEGER + 1`],
97+
[-1, ],
98+
[Number.NEGATIVE_INFINITY, ],
99+
];
100+
101+
for (const [value, name] of invalidOutOfRangeArgumentTestCases) {
102+
const label = name ?? String(name);
103+
const iter = new TestIterator();
104+
105+
shouldThrow(`${label}: the 1st arg is out of range`, () => {
106+
test(iter, value);
107+
}, RangeError, `Iterator.prototype.drop argument must be non-negative safe integer or +Infinity`);
108+
109+
shouldBe(iter.isClosed, true, `${label}: the iterator should be closed`);
110+
}
111+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
function shouldThrow(caseName, fn, expectedErrorCtor, expectedErrorMessage) {
2+
if (!caseName)
3+
throw new Error(`must specify test case name`);
4+
5+
const expected = `${expectedErrorCtor.name}(${expectedErrorMessage})`;
6+
try {
7+
fn();
8+
throw new Error(`${caseName}: Expected to throw ${expected}, but succeeded`);
9+
} catch (e) {
10+
const actual = `${e.name}(${e.message})`;
11+
if (!(e instanceof expectedErrorCtor) || e.message !== expectedErrorMessage)
12+
throw new Error(`${caseName}: Expected ${expected} but got ${actual}`);
13+
}
14+
}
15+
16+
function shouldNotThrow(fn, caseName) {
17+
if (!caseName)
18+
throw new Error(`must specify message`);
19+
20+
try {
21+
fn();
22+
} catch (e) {
23+
const actual = `${e.name}(${e.message})`;
24+
throw new Error(`${caseName}: Expected not thrown but got ${actual}`);
25+
}
26+
}
27+
28+
function shouldBe(a, b, testname) {
29+
if (a !== b)
30+
throw new Error(`${testname}: Expected ${b} but got ${a}`);
31+
}
32+
33+
class TestIterator extends Iterator {
34+
value = 0;
35+
isClosed = false;
36+
isDone = false;
37+
constructor(max = 3) {
38+
super();
39+
if (max < 0) {
40+
throw new RangeError('max must be >= 0');
41+
}
42+
this.max = +max;
43+
}
44+
next() {
45+
const value = this.value;
46+
if (value > this.max) {
47+
this.isDone = true;
48+
return {
49+
done: true,
50+
value: undefined,
51+
};
52+
}
53+
54+
this.value += 1;
55+
return {
56+
done: false,
57+
value,
58+
};
59+
}
60+
return() {
61+
this.isClosed = true;
62+
return {
63+
done: true,
64+
value: undefined,
65+
};
66+
}
67+
}
68+
69+
function test(target, limit) {
70+
const newIter = Iterator.prototype.take.call(target, limit);
71+
return newIter;
72+
}
73+
74+
{
75+
const validUpperLowerBoundArgumentTestCases = [
76+
[Number.POSITIVE_INFINITY, ],
77+
[Number.MAX_SAFE_INTEGER, `Number.MAX_SAFE_INTEGER`],
78+
[0, ],
79+
[0.0, `0.0`],
80+
];
81+
82+
for (const [value, name] of validUpperLowerBoundArgumentTestCases) {
83+
const label = name ?? String(name);
84+
const iter = new TestIterator();
85+
86+
shouldNotThrow(() => {
87+
test(iter, value);
88+
}, `${label}: should be valid for the 1st argument`);
89+
shouldBe(iter.isClosed, false, `${label}: the iterator should not be closed`);
90+
}
91+
}
92+
93+
{
94+
const invalidOutOfRangeArgumentTestCases = [
95+
[Number.MAX_VALUE, `Number.MAX_VALUE`],
96+
[Number.MAX_SAFE_INTEGER + 1, `Number.MAX_SAFE_INTEGER + 1`],
97+
[-1, ],
98+
[Number.NEGATIVE_INFINITY, ],
99+
];
100+
101+
for (const [value, name] of invalidOutOfRangeArgumentTestCases) {
102+
const label = name ?? String(name);
103+
const iter = new TestIterator();
104+
105+
shouldThrow(`${label}: the 1st arg is out of range`, () => {
106+
test(iter, value);
107+
}, RangeError, `Iterator.prototype.take argument must be non-negative safe integer or +Infinity`);
108+
109+
shouldBe(iter.isClosed, true, `${label}: the iterator should be closed`);
110+
}
111+
}

‎Source/JavaScriptCore/builtins/JSIteratorPrototype.js‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,20 @@ function take(limit)
114114
}
115115
}
116116

117+
if (@isFinite(numLimit) && numLimit > @MAX_SAFE_INTEGER) {
118+
try {
119+
@iteratorGenericClose(this);
120+
} finally {
121+
@throwRangeError("Iterator.prototype.take argument must be non-negative safe integer or +Infinity");
122+
}
123+
}
124+
117125
var intLimit = @toIntegerOrInfinity(numLimit);
118126
if (intLimit < 0) {
119127
try {
120128
@iteratorGenericClose(this);
121129
} finally {
122-
@throwRangeError("Iterator.prototype.take argument must be non-negative.");
130+
@throwRangeError("Iterator.prototype.take argument must be non-negative safe integer or +Infinity");
123131
}
124132
}
125133

@@ -169,12 +177,20 @@ function drop(limit)
169177
}
170178
}
171179

180+
if (@isFinite(numLimit) && numLimit > @MAX_SAFE_INTEGER) {
181+
try {
182+
@iteratorGenericClose(this);
183+
} finally {
184+
@throwRangeError("Iterator.prototype.drop argument must be non-negative safe integer or +Infinity");
185+
}
186+
}
187+
172188
var intLimit = @toIntegerOrInfinity(numLimit);
173189
if (intLimit < 0) {
174190
try {
175191
@iteratorGenericClose(this);
176192
} finally {
177-
@throwRangeError("Iterator.prototype.drop argument must be non-negative.");
193+
@throwRangeError("Iterator.prototype.drop argument must be non-negative safe integer or +Infinity");
178194
}
179195
}
180196

0 commit comments

Comments
 (0)