Skip to content

Commit 808d437

Browse files
Support iterating generators in browsers without Symbol (#13129)
1 parent 5d55055 commit 808d437

15 files changed

Lines changed: 124 additions & 53 deletions

File tree

packages/babel-helpers/src/helpers.js

Lines changed: 36 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,15 @@ helpers.jsx = helper("7.0.0-beta.0")`
8383

8484
helpers.asyncIterator = helper("7.0.0-beta.0")`
8585
export default function _asyncIterator(iterable) {
86-
var method
86+
var method;
8787
if (typeof Symbol !== "undefined") {
88-
if (Symbol.asyncIterator) {
89-
method = iterable[Symbol.asyncIterator]
90-
if (method != null) return method.call(iterable);
91-
}
92-
if (Symbol.iterator) {
93-
method = iterable[Symbol.iterator]
94-
if (method != null) return method.call(iterable);
95-
}
88+
if (Symbol.asyncIterator) method = iterable[Symbol.asyncIterator];
89+
if (method == null && Symbol.iterator) method = iterable[Symbol.iterator];
9690
}
97-
throw new TypeError("Object is not async iterable");
91+
if (method == null) method = iterable["@@asyncIterator"];
92+
if (method == null) method = iterable["@@iterator"]
93+
if (method == null) throw new TypeError("Object is not async iterable");
94+
return method.call(iterable);
9895
}
9996
`;
10097

@@ -179,9 +176,7 @@ helpers.AsyncGenerator = helper("7.0.0-beta.0")`
179176
}
180177
}
181178
182-
if (typeof Symbol === "function" && Symbol.asyncIterator) {
183-
AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; };
184-
}
179+
AsyncGenerator.prototype[typeof Symbol === "function" && Symbol.asyncIterator || "@@asyncIterator"] = function () { return this; };
185180
186181
AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); };
187182
AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); };
@@ -216,9 +211,7 @@ helpers.asyncGeneratorDelegate = helper("7.0.0-beta.0")`
216211
return { done: false, value: awaitWrap(value) };
217212
};
218213
219-
if (typeof Symbol === "function" && Symbol.iterator) {
220-
iter[Symbol.iterator] = function () { return this; };
221-
}
214+
iter[typeof Symbol !== "undefined" && Symbol.iterator || "@@iterator"] = function () { return this; };
222215
223216
iter.next = function (value) {
224217
if (waiting) {
@@ -1003,7 +996,7 @@ helpers.maybeArrayLike = helper("7.9.0")`
1003996

1004997
helpers.iterableToArray = helper("7.0.0-beta.0")`
1005998
export default function _iterableToArray(iter) {
1006-
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
999+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
10071000
}
10081001
`;
10091002

@@ -1019,14 +1012,15 @@ helpers.iterableToArrayLimit = helper("7.0.0-beta.0")`
10191012
// _i = _iterator
10201013
// _s = _step
10211014
1022-
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
1015+
var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]);
1016+
if (_i == null) return;
10231017
10241018
var _arr = [];
10251019
var _n = true;
10261020
var _d = false;
10271021
var _e = undefined;
10281022
try {
1029-
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
1023+
for (_i = _i.call(arr), _s; !(_n = (_s = _i.next()).done); _n = true) {
10301024
_arr.push(_s.value);
10311025
if (i && _arr.length === i) break;
10321026
}
@@ -1046,10 +1040,11 @@ helpers.iterableToArrayLimit = helper("7.0.0-beta.0")`
10461040

10471041
helpers.iterableToArrayLimitLoose = helper("7.0.0-beta.0")`
10481042
export default function _iterableToArrayLimitLoose(arr, i) {
1049-
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
1043+
var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]);
1044+
if (_i == null) return;
10501045
10511046
var _arr = [];
1052-
for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) {
1047+
for (_i = _i.call(arr), _step; !(_step = _i.next()).done;) {
10531048
_arr.push(_step.value);
10541049
if (i && _arr.length === i) break;
10551050
}
@@ -1104,8 +1099,9 @@ helpers.createForOfIteratorHelper = helper("7.9.0")`
11041099
// f: finish (always called at the end)
11051100
11061101
export default function _createForOfIteratorHelper(o, allowArrayLike) {
1107-
var it;
1108-
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
1102+
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
1103+
1104+
if (!it) {
11091105
// Fallback for engines without symbol support
11101106
if (
11111107
Array.isArray(o) ||
@@ -1133,7 +1129,7 @@ helpers.createForOfIteratorHelper = helper("7.9.0")`
11331129
11341130
return {
11351131
s: function() {
1136-
it = o[Symbol.iterator]();
1132+
it = it.call(o);
11371133
},
11381134
n: function() {
11391135
var step = it.next();
@@ -1159,28 +1155,25 @@ helpers.createForOfIteratorHelperLoose = helper("7.9.0")`
11591155
import unsupportedIterableToArray from "unsupportedIterableToArray";
11601156
11611157
export default function _createForOfIteratorHelperLoose(o, allowArrayLike) {
1162-
var it;
1163-
1164-
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
1165-
// Fallback for engines without symbol support
1166-
if (
1167-
Array.isArray(o) ||
1168-
(it = unsupportedIterableToArray(o)) ||
1169-
(allowArrayLike && o && typeof o.length === "number")
1170-
) {
1171-
if (it) o = it;
1172-
var i = 0;
1173-
return function() {
1174-
if (i >= o.length) return { done: true };
1175-
return { done: false, value: o[i++] };
1176-
}
1158+
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
1159+
1160+
if (it) return (it = it.call(o)).next.bind(it);
1161+
1162+
// Fallback for engines without symbol support
1163+
if (
1164+
Array.isArray(o) ||
1165+
(it = unsupportedIterableToArray(o)) ||
1166+
(allowArrayLike && o && typeof o.length === "number")
1167+
) {
1168+
if (it) o = it;
1169+
var i = 0;
1170+
return function() {
1171+
if (i >= o.length) return { done: true };
1172+
return { done: false, value: o[i++] };
11771173
}
1178-
1179-
throw new TypeError("Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
11801174
}
11811175
1182-
it = o[Symbol.iterator]();
1183-
return it.next.bind(it);
1176+
throw new TypeError("Invalid attempt to iterate non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
11841177
}
11851178
`;
11861179

packages/babel-plugin-transform-modules-commonjs/test/fixtures/regression/T7199/output.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o =
1212

1313
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
1414

15-
function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
15+
function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (_i = _i.call(arr), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
1616

1717
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
1818

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
let iterable = function* fn() {
2+
yield 0;
3+
yield 1;
4+
yield 2;
5+
}();
6+
7+
let iteratorMethod = iterable[Symbol.iterator];
8+
iterable[Symbol.iterator] = undefined;
9+
10+
expect(() => {
11+
let [_, ...res] = iterable;
12+
}).toThrow(/non-iterable/);
13+
14+
// In older envs, regenerator uses @@iterator.
15+
// In those browser Babel must delegate to Array.from, which
16+
// needs to be polyfilled by the user and support @@iterator.
17+
iterable["@@iterator"] = iteratorMethod;
18+
Array.from = (it) => {
19+
var i = it["@@iterator"](), arr = [], r;
20+
while (!(r = it.next()).done) arr.push(r.value);
21+
return arr;
22+
};
23+
24+
let [_, ...res] = iterable;
25+
expect(res).toEqual([1, 2]);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["transform-regenerator", "transform-destructuring"]
3+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
let iterable = function* fn() {
2+
yield 1;
3+
yield 2;
4+
}();
5+
6+
let iteratorMethod = iterable[Symbol.iterator];
7+
iterable[Symbol.iterator] = undefined;
8+
9+
expect(() => {
10+
[...iterable];
11+
}).toThrow(/non-iterable/);
12+
13+
// In older envs, regenerator uses @@iterator.
14+
// In those browser Babel must delegate to Array.from, which
15+
// needs to be polyfilled by the user and support @@iterator.
16+
iterable["@@iterator"] = iteratorMethod;
17+
Array.from = (it) => {
18+
var i = it["@@iterator"](), arr = [], r;
19+
while (!(r = it.next()).done) arr.push(r.value);
20+
return arr;
21+
};
22+
23+
let res = [...iterable];
24+
25+
expect(res).toEqual([1, 2]);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["transform-regenerator", "transform-spread"]
3+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
let iterable = function* fn() {
2+
yield 1;
3+
yield 2;
4+
}();
5+
6+
let iteratorMethod = iterable[Symbol.iterator];
7+
iterable[Symbol.iterator] = undefined;
8+
9+
expect(() => {
10+
for (let x of iterable);
11+
}).toThrow(/non-iterable/);
12+
13+
// In older envs, regenerator uses @@iterator.
14+
iterable["@@iterator"] = iteratorMethod;
15+
16+
let res = [];
17+
for (let x of iterable) res.push(x);
18+
19+
expect(res).toEqual([1, 2]);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["transform-regenerator", "transform-for-of"]
3+
}

packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var o = {};
22

3-
expect(() => [...undefined]).toThrow(/spread non-iterable/);
3+
expect(() => [...undefined]).toThrow(/undefined is not iterable|property 'Symbol\(Symbol\.iterator\)' of undefined/);
44

55
expect(() => [...o]).toThrow(/spread non-iterable/);
66

packages/babel-preset-env/test/fixtures/corejs2-babel-7/usage-shippedProposals/output.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function _wrapAsyncGenerator(fn) { return function () { return new _AsyncGenerat
4242

4343
function _AsyncGenerator(gen) { var front, back; function send(key, arg) { return new Promise(function (resolve, reject) { var request = { key: key, arg: arg, resolve: resolve, reject: reject, next: null }; if (back) { back = back.next = request; } else { front = back = request; resume(key, arg); } }); } function resume(key, arg) { try { var result = gen[key](arg); var value = result.value; var wrappedAwait = value instanceof _AwaitValue; Promise.resolve(wrappedAwait ? value.wrapped : value).then(function (arg) { if (wrappedAwait) { resume(key === "return" ? "return" : "next", arg); return; } settle(result.done ? "return" : "normal", arg); }, function (err) { resume("throw", err); }); } catch (err) { settle("throw", err); } } function settle(type, value) { switch (type) { case "return": front.resolve({ value: value, done: true }); break; case "throw": front.reject(value); break; default: front.resolve({ value: value, done: false }); break; } front = front.next; if (front) { resume(front.key, front.arg); } else { back = null; } } this._invoke = send; if (typeof gen["return"] !== "function") { this["return"] = undefined; } }
4444

45-
if (typeof Symbol === "function" && Symbol.asyncIterator) { _AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; }; }
45+
_AsyncGenerator.prototype[typeof Symbol === "function" && Symbol.asyncIterator || "@@asyncIterator"] = function () { return this; };
4646

4747
_AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); };
4848

0 commit comments

Comments
 (0)