Skip to content

Commit 8167400

Browse files
[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 NOBODY (OOPS!). 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:
1 parent 1f2316e commit 8167400

3 files changed

Lines changed: 234 additions & 2 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
function test(target, limit) {
34+
const newIter = Iterator.prototype.drop.call(target, limit);
35+
return newIter;
36+
}
37+
38+
class TestIterator extends Iterator {
39+
value = 0;
40+
isClosed = false;
41+
isDone = false;
42+
constructor(max = 3) {
43+
super();
44+
if (max < 0) {
45+
throw new RangeError('max must be >= 0');
46+
}
47+
this.max = max;
48+
}
49+
next() {
50+
const value = this.value;
51+
if (value > this.max) {
52+
this.isDone = true;
53+
return {
54+
done: true,
55+
value: undefined,
56+
};
57+
}
58+
59+
this.value += 1;
60+
return {
61+
done: false,
62+
value,
63+
};
64+
}
65+
return() {
66+
this.isClosed = true;
67+
return {
68+
done: true,
69+
value: undefined,
70+
};
71+
}
72+
}
73+
74+
{
75+
const TEST_CASES = [
76+
[0, ],
77+
[Number.MAX_SAFE_INTEGER, `Number.MAX_SAFE_INTEGER`],
78+
];
79+
80+
for (const [value, name] of TEST_CASES) {
81+
const label = name ?? String(name);
82+
const iter = new TestIterator();
83+
84+
shouldNotThrow(() => {
85+
test(iter, value);
86+
}, `MAX_SAFE_INTEGER should be valid for the 1st argument`);
87+
shouldBe(iter.isClosed, false, `${label}: the iterator should not be closed`);
88+
}
89+
}
90+
91+
{
92+
const TEST_CASES = [
93+
[-1, ],
94+
[Number.MAX_SAFE_INTEGER + 1, `2 ** 53`],
95+
[Number.POSITIVE_INFINITY, `+Infinity`],
96+
];
97+
98+
for (const [value, name] of TEST_CASES) {
99+
const label = name ?? String(name);
100+
const iter = new TestIterator();
101+
102+
shouldThrow(`${label}: the 1st arg is out of range`, () => {
103+
test(iter, value);
104+
}, RangeError, 'Iterator.prototype.drop argument must be non-negative safe integer');
105+
106+
shouldBe(iter.isClosed, true, `${label}: the iterator should be closed`);
107+
}
108+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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+
function test(target, limit) {
34+
const newIter = Iterator.prototype.take.call(target, limit);
35+
return newIter;
36+
}
37+
38+
class TestIterator extends Iterator {
39+
value = 0;
40+
isClosed = false;
41+
isDone = false;
42+
constructor(max) {
43+
super();
44+
if (max < 0) {
45+
throw new RangeError('max must be >= 0');
46+
}
47+
this.max = max;
48+
}
49+
next() {
50+
const value = this.value;
51+
if (value > this.max) {
52+
this.isDone = true;
53+
return {
54+
done: true,
55+
value: undefined,
56+
};
57+
}
58+
59+
this.value += 1;
60+
return {
61+
done: false,
62+
value,
63+
};
64+
}
65+
return() {
66+
this.isClosed = true;
67+
return {
68+
done: true,
69+
value: undefined,
70+
};
71+
}
72+
}
73+
74+
{
75+
const TEST_CASES = [
76+
[0, ],
77+
[Number.MAX_SAFE_INTEGER, `Number.MAX_SAFE_INTEGER`],
78+
];
79+
80+
for (const [value, name] of TEST_CASES) {
81+
const label = name ?? String(name);
82+
const iter = new TestIterator();
83+
84+
shouldNotThrow(() => {
85+
test(iter, value);
86+
}, `MAX_SAFE_INTEGER should be valid for the 1st argument`);
87+
shouldBe(iter.isClosed, false, `${label}: the iterator should not be closed`);
88+
}
89+
}
90+
91+
{
92+
const TEST_CASES = [
93+
[-1, ],
94+
[Number.MAX_SAFE_INTEGER + 1, `2 ** 53`],
95+
[Number.POSITIVE_INFINITY, `+Infinity`],
96+
];
97+
98+
for (const [value, name] of TEST_CASES) {
99+
const label = name ?? String(name);
100+
const iter = new TestIterator();
101+
102+
shouldThrow(`${label}: the 1st arg is out of range`, () => {
103+
test(iter, value);
104+
}, RangeError, 'Iterator.prototype.take argument must be non-negative safe integer');
105+
106+
shouldBe(iter.isClosed, true, `${label}: the iterator should be closed`);
107+
}
108+
}

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 (numLimit > @MAX_SAFE_INTEGER) {
118+
try {
119+
@iteratorGenericClose(this);
120+
} finally {
121+
@throwRangeError("Iterator.prototype.take argument must be non-negative safe integer");
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");
123131
}
124132
}
125133

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

180+
if (numLimit > @MAX_SAFE_INTEGER) {
181+
try {
182+
@iteratorGenericClose(this);
183+
} finally {
184+
@throwRangeError("Iterator.prototype.drop argument must be non-negative safe integer");
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");
178194
}
179195
}
180196

0 commit comments

Comments
 (0)