Skip to content

Commit

Permalink
Allow rest/spread on polyfilled or builtin iterables without Symbol
Browse files Browse the repository at this point in the history
… support (#11268)

* Allow rest/spread on polyfilled or builtin iterables when `Symbol` unsupported

Currently, when `Symbol` is not supported, we allow using rest/spread with:
- arrays
- strings
- arguments

With this PR, it will be also possible to use it with
- maps
- sets
- binary arrays

While in old browsers es6 builtins would still need to be polyfilled, it's way
easier to polyfill them because `Symbol` cannot be reliably polyfilled.

I didn't use `instanceof` becase:
- it doesn't work with polyfills not attatched to the global scope
- when using Babel to load polyfills, it would force the inclusion of `Map` and `Set` polyfills even if they are not used

Downside: the current approach of relying on `toString || construcor.name` doesn't work with subclasses.

* Don't use Array.from for array-like objects
  • Loading branch information
nicolo-ribaudo committed Mar 17, 2020
1 parent 841f442 commit 10aa97b
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 35 deletions.
77 changes: 56 additions & 21 deletions packages/babel-helpers/src/helpers.js
Expand Up @@ -902,49 +902,72 @@ helpers.temporalRef = helper("7.0.0-beta.0")`
helpers.slicedToArray = helper("7.0.0-beta.0")`
import arrayWithHoles from "arrayWithHoles";
import iterableToArrayLimit from "iterableToArrayLimit";
import unsupportedIterableToArray from "unsupportedIterableToArray";
import nonIterableRest from "nonIterableRest";
export default function _slicedToArray(arr, i) {
return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || nonIterableRest();
return (
arrayWithHoles(arr) ||
iterableToArrayLimit(arr, i) ||
unsupportedIterableToArray(arr, i) ||
nonIterableRest()
);
}
`;

helpers.slicedToArrayLoose = helper("7.0.0-beta.0")`
import arrayWithHoles from "arrayWithHoles";
import iterableToArrayLimitLoose from "iterableToArrayLimitLoose";
import unsupportedIterableToArray from "unsupportedIterableToArray";
import nonIterableRest from "nonIterableRest";
export default function _slicedToArrayLoose(arr, i) {
return arrayWithHoles(arr) || iterableToArrayLimitLoose(arr, i) || nonIterableRest();
return (
arrayWithHoles(arr) ||
iterableToArrayLimitLoose(arr, i) ||
unsupportedIterableToArray(arr, i) ||
nonIterableRest()
);
}
`;

helpers.toArray = helper("7.0.0-beta.0")`
import arrayWithHoles from "arrayWithHoles";
import iterableToArray from "iterableToArray";
import unsupportedIterableToArray from "unsupportedIterableToArray";
import nonIterableRest from "nonIterableRest";
export default function _toArray(arr) {
return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();
return (
arrayWithHoles(arr) ||
iterableToArray(arr) ||
unsupportedIterableToArray(arr) ||
nonIterableRest()
);
}
`;

helpers.toConsumableArray = helper("7.0.0-beta.0")`
import arrayWithoutHoles from "arrayWithoutHoles";
import iterableToArray from "iterableToArray";
import unsupportedIterableToArray from "unsupportedIterableToArray";
import nonIterableSpread from "nonIterableSpread";
export default function _toConsumableArray(arr) {
return arrayWithoutHoles(arr) || iterableToArray(arr) || nonIterableSpread();
return (
arrayWithoutHoles(arr) ||
iterableToArray(arr) ||
unsupportedIterableToArray(arr) ||
nonIterableSpread()
);
}
`;

helpers.arrayWithoutHoles = helper("7.0.0-beta.0")`
import arrayLikeToArray from "arrayLikeToArray";
export default function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
}
if (Array.isArray(arr)) return arrayLikeToArray(arr);
}
`;

Expand All @@ -956,11 +979,7 @@ helpers.arrayWithHoles = helper("7.0.0-beta.0")`

helpers.iterableToArray = helper("7.0.0-beta.0")`
export default function _iterableToArray(iter) {
if (
typeof iter === 'string'
|| Object.prototype.toString.call(iter) === "[object Arguments]"
|| (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter))
) return Array.from(iter);
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
`;

Expand All @@ -976,10 +995,7 @@ helpers.iterableToArrayLimit = helper("7.0.0-beta.0")`
// _i = _iterator
// _s = _step
if (
(typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) &&
Object.prototype.toString.call(arr) !== "[object Arguments]"
) return;
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
var _n = true;
Expand All @@ -1006,10 +1022,7 @@ helpers.iterableToArrayLimit = helper("7.0.0-beta.0")`

helpers.iterableToArrayLimitLoose = helper("7.0.0-beta.0")`
export default function _iterableToArrayLimitLoose(arr, i) {
if (
(typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) &&
Object.prototype.toString.call(arr) !== "[object Arguments]"
) return;
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return;
var _arr = [];
for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) {
Expand All @@ -1020,6 +1033,28 @@ helpers.iterableToArrayLimitLoose = helper("7.0.0-beta.0")`
}
`;

helpers.unsupportedIterableToArray = helper("7.9.0")`
import arrayLikeToArray from "arrayLikeToArray";
export default function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
return arrayLikeToArray(o, minLen);
}
`;

helpers.arrayLikeToArray = helper("7.9.0")`
export default 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;
}
`;

helpers.nonIterableSpread = helper("7.0.0-beta.0")`
export default function _nonIterableSpread() {
throw new TypeError(
Expand Down
Expand Up @@ -4,11 +4,15 @@ var _foo = _interopRequireDefault(require("foo"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArrayLimit(arr, i) { if ((typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) && Object.prototype.toString.call(arr) !== "[object Arguments]") 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; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

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; }

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; }

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

Expand Down
@@ -1,10 +1,14 @@
"use strict";

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArrayLimit(arr, i) { if ((typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) && Object.prototype.toString.call(arr) !== "[object Arguments]") 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; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

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; }

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; }

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

Expand Down
@@ -1,8 +1,12 @@
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArrayLimit(arr, i) { if ((typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) && Object.prototype.toString.call(arr) !== "[object Arguments]") 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; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

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; }

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; }

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

Expand Down
Expand Up @@ -2,5 +2,5 @@ import _Array$from from "../../core-js/array/from";
import _isIterable from "../../core-js/is-iterable";
import _Symbol from "../../core-js/symbol";
export default function _iterableToArray(iter) {
if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof _Symbol !== "undefined" && _isIterable(Object(iter))) return _Array$from(iter);
if (typeof _Symbol !== "undefined" && _isIterable(Object(iter))) return _Array$from(iter);
}
3 changes: 2 additions & 1 deletion packages/babel-runtime-corejs2/helpers/esm/toArray.js
@@ -1,6 +1,7 @@
import arrayWithHoles from "./arrayWithHoles";
import iterableToArray from "./iterableToArray";
import unsupportedIterableToArray from "./unsupportedIterableToArray";
import nonIterableRest from "./nonIterableRest";
export default function _toArray(arr) {
return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();
return arrayWithHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableRest();
}
2 changes: 1 addition & 1 deletion packages/babel-runtime-corejs2/helpers/iterableToArray.js
Expand Up @@ -5,7 +5,7 @@ var _isIterable = require("../core-js/is-iterable");
var _Symbol = require("../core-js/symbol");

function _iterableToArray(iter) {
if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof _Symbol !== "undefined" && _isIterable(Object(iter))) return _Array$from(iter);
if (typeof _Symbol !== "undefined" && _isIterable(Object(iter))) return _Array$from(iter);
}

module.exports = _iterableToArray;
4 changes: 3 additions & 1 deletion packages/babel-runtime-corejs2/helpers/toArray.js
Expand Up @@ -2,10 +2,12 @@ var arrayWithHoles = require("./arrayWithHoles");

var iterableToArray = require("./iterableToArray");

var unsupportedIterableToArray = require("./unsupportedIterableToArray");

var nonIterableRest = require("./nonIterableRest");

function _toArray(arr) {
return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();
return arrayWithHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableRest();
}

module.exports = _toArray;
2 changes: 1 addition & 1 deletion packages/babel-runtime/helpers/esm/iterableToArray.js
@@ -1,3 +1,3 @@
export default function _iterableToArray(iter) {
if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}
3 changes: 2 additions & 1 deletion packages/babel-runtime/helpers/esm/toArray.js
@@ -1,6 +1,7 @@
import arrayWithHoles from "./arrayWithHoles";
import iterableToArray from "./iterableToArray";
import unsupportedIterableToArray from "./unsupportedIterableToArray";
import nonIterableRest from "./nonIterableRest";
export default function _toArray(arr) {
return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();
return arrayWithHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableRest();
}
2 changes: 1 addition & 1 deletion packages/babel-runtime/helpers/iterableToArray.js
@@ -1,5 +1,5 @@
function _iterableToArray(iter) {
if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter);
}

module.exports = _iterableToArray;
4 changes: 3 additions & 1 deletion packages/babel-runtime/helpers/toArray.js
Expand Up @@ -2,10 +2,12 @@ var arrayWithHoles = require("./arrayWithHoles");

var iterableToArray = require("./iterableToArray");

var unsupportedIterableToArray = require("./unsupportedIterableToArray");

var nonIterableRest = require("./nonIterableRest");

function _toArray(arr) {
return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();
return arrayWithHoles(arr) || iterableToArray(arr) || unsupportedIterableToArray(arr) || nonIterableRest();
}

module.exports = _toArray;

0 comments on commit 10aa97b

Please sign in to comment.