From 1ba41f208430bdf382b75b56e9aad7436ca522ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 16 Mar 2020 16:34:33 +0100 Subject: [PATCH] Throw better errors for non-iterables when Symbol doesn't exist (#11264) --- packages/babel-helpers/src/helpers.js | 27 ++++++++++++------- .../array-symbol-unsupported/exec.js | 13 +++++++++ .../array-symbol-unsupported/input.js | 6 +++++ .../array-symbol-unsupported/output.js | 8 ++++++ .../destructuring/non-iterable/exec.js | 25 ++++++++++++----- .../destructuring/non-iterable/options.json | 5 ++++ .../test/fixtures/regression/T7199/output.js | 4 +-- .../spread/array-symbol-unsupported/exec.js | 10 +++++++ .../spread/array-symbol-unsupported/input.js | 6 +++++ .../spread/array-symbol-unsupported/output.js | 6 +++++ .../test/fixtures/spread/non-iterable/exec.js | 10 +++++++ .../browserslist-defaults-not-ie/output.js | 4 +-- .../sanity/block-scoping-for-of/output.js | 4 +-- .../helpers/esm/iterableToArray.js | 3 ++- .../helpers/iterableToArray.js | 4 ++- .../helpers/esm/iterableToArray.js | 2 +- .../babel-runtime/helpers/iterableToArray.js | 2 +- 17 files changed, 114 insertions(+), 25 deletions(-) create mode 100644 packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js create mode 100644 packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/input.js create mode 100644 packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/output.js create mode 100644 packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/options.json create mode 100644 packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js create mode 100644 packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/input.js create mode 100644 packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/output.js create mode 100644 packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index f8ad7731c3c1..3290407184d0 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -936,7 +936,7 @@ helpers.iterableToArray = helper("7.0.0-beta.0")` if ( typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" - || Symbol.iterator in Object(iter) + || (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) ) return Array.from(iter); } `; @@ -952,9 +952,12 @@ helpers.iterableToArrayLimit = helper("7.0.0-beta.0")` // _e = _iteratorError // _i = _iterator // _s = _step - if (!( - Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]" - )) { return } + + 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; @@ -980,9 +983,11 @@ helpers.iterableToArrayLimit = helper("7.0.0-beta.0")` helpers.iterableToArrayLimitLoose = helper("7.0.0-beta.0")` export default function _iterableToArrayLimitLoose(arr, i) { - if (!( - Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]" - )) { return } + if ( + (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) && + Object.prototype.toString.call(arr) !== "[object Arguments]" + ) return; + var _arr = []; for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { _arr.push(_step.value); @@ -994,13 +999,17 @@ helpers.iterableToArrayLimitLoose = helper("7.0.0-beta.0")` helpers.nonIterableSpread = helper("7.0.0-beta.0")` export default function _nonIterableSpread() { - throw new TypeError("Invalid attempt to spread non-iterable instance"); + throw new TypeError( + "Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." + ); } `; helpers.nonIterableRest = helper("7.0.0-beta.0")` export default function _nonIterableRest() { - throw new TypeError("Invalid attempt to destructure non-iterable instance"); + throw new TypeError( + "Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method." + ); } `; diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js new file mode 100644 index 000000000000..ea73919662e2 --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/exec.js @@ -0,0 +1,13 @@ +var a = (() => [1, 2, 3])(); + +// Simulate old environment +let _Symbol = Symbol; +Symbol = void 0; +try { + var [first, ...rest] = a; + + expect(first).toBe(1); + expect(rest).toEqual([2, 3]); +} finally { + Symbol = _Symbol; +} diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/input.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/input.js new file mode 100644 index 000000000000..ea501d37a051 --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/input.js @@ -0,0 +1,6 @@ +var a = (() => [1, 2, 3])(); + +// !!! In order to run this test, this shouldn't be optimized using type inference +// If it's optimized and doesn't call toArray, please modify this test +// and exec.js +var [first, ...rest] = a; diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/output.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/output.js new file mode 100644 index 000000000000..f9d095f2d823 --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/array-symbol-unsupported/output.js @@ -0,0 +1,8 @@ +var a = (() => [1, 2, 3])(); // !!! In order to run this test, this shouldn't be optimized using type inference +// If it's optimized and doesn't call toArray, please modify this test +// and exec.js + + +var _a = babelHelpers.toArray(a), + first = _a[0], + rest = _a.slice(1); diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js index 83099c46c4f4..e0bac050d60a 100644 --- a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/exec.js @@ -1,9 +1,22 @@ +var foo, bar; + expect( - () => { - var [foo, bar] = undefined; - }).toThrow("Invalid attempt to destructure non-iterable instance"); + () => [foo, bar] = undefined +).toThrow(/destructure non-iterable/); expect( - () => { - var foo = [ ...undefined ]; - }).toThrow("Invalid attempt to spread non-iterable instance"); + () => [foo, bar] = {} +).toThrow(/destructure non-iterable/); + +// Simulate old browser +let _Symbol = Symbol; +Symbol = void 0; +try { + + expect( + () => [foo, bar] = {} + ).toThrow(/destructure non-iterable/); + +} finally { + Symbol = _Symbol; +} diff --git a/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/options.json b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/options.json new file mode 100644 index 000000000000..23c0b75d2acd --- /dev/null +++ b/packages/babel-plugin-transform-destructuring/test/fixtures/destructuring/non-iterable/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "transform-destructuring" + ] +} diff --git a/packages/babel-plugin-transform-modules-commonjs/test/fixtures/regression/T7199/output.js b/packages/babel-plugin-transform-modules-commonjs/test/fixtures/regression/T7199/output.js index cbe323d57d81..c41719ea46e4 100644 --- a/packages/babel-plugin-transform-modules-commonjs/test/fixtures/regression/T7199/output.js +++ b/packages/babel-plugin-transform-modules-commonjs/test/fixtures/regression/T7199/output.js @@ -6,9 +6,9 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "d function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } -function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } +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 (!(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 _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 _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js new file mode 100644 index 000000000000..93ad0b7a1e33 --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/exec.js @@ -0,0 +1,10 @@ +var a = (() => [2, 3])(); + +// Simulate old environment +let _Symbol = Symbol; +Symbol = void 0; +try { + expect([1, ...a]).toEqual([1, 2, 3]); +} finally { + Symbol = _Symbol; +} diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/input.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/input.js new file mode 100644 index 000000000000..50d65a58a087 --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/input.js @@ -0,0 +1,6 @@ +var a = (() => [2, 3])(); + +// !!! In order to run this test, this shouldn't be optimized using type inference +// If it's optimized and doesn't call toConsumableArray, please modify this test +// and exec.js +[1, ...a]; diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/output.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/output.js new file mode 100644 index 000000000000..b4c314291e02 --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/array-symbol-unsupported/output.js @@ -0,0 +1,6 @@ +var a = (() => [2, 3])(); // !!! In order to run this test, this shouldn't be optimized using type inference +// If it's optimized and doesn't call toConsumableArray, please modify this test +// and exec.js + + +[1].concat(babelHelpers.toConsumableArray(a)); diff --git a/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js b/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js new file mode 100644 index 000000000000..0ff7a4876673 --- /dev/null +++ b/packages/babel-plugin-transform-spread/test/fixtures/spread/non-iterable/exec.js @@ -0,0 +1,10 @@ +var o = {}; + +expect(() => [...undefined]).toThrow(/spread non-iterable/); + +expect(() => [...o]).toThrow(/spread non-iterable/); + +// Simulate old browser +Symbol = void 0; + +expect(() => [...o]).toThrow(/spread non-iterable/); diff --git a/packages/babel-preset-env/test/fixtures/preset-options/browserslist-defaults-not-ie/output.js b/packages/babel-preset-env/test/fixtures/preset-options/browserslist-defaults-not-ie/output.js index 27a1db8f8775..a013b75063ca 100644 --- a/packages/babel-preset-env/test/fixtures/preset-options/browserslist-defaults-not-ie/output.js +++ b/packages/babel-preset-env/test/fixtures/preset-options/browserslist-defaults-not-ie/output.js @@ -2,9 +2,9 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } -function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } +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 (!(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 _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 _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } diff --git a/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js b/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js index 6fc0a62bd42f..4e061ab31db5 100644 --- a/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js +++ b/packages/babel-preset-env/test/fixtures/sanity/block-scoping-for-of/output.js @@ -1,8 +1,8 @@ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); } -function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } +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 (!(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 _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 _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } diff --git a/packages/babel-runtime-corejs2/helpers/esm/iterableToArray.js b/packages/babel-runtime-corejs2/helpers/esm/iterableToArray.js index a3df2576ad58..502a84d50ffb 100644 --- a/packages/babel-runtime-corejs2/helpers/esm/iterableToArray.js +++ b/packages/babel-runtime-corejs2/helpers/esm/iterableToArray.js @@ -1,5 +1,6 @@ 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]" || _isIterable(Object(iter))) return _Array$from(iter); + if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof _Symbol !== "undefined" && _isIterable(Object(iter))) return _Array$from(iter); } \ No newline at end of file diff --git a/packages/babel-runtime-corejs2/helpers/iterableToArray.js b/packages/babel-runtime-corejs2/helpers/iterableToArray.js index 0e07f27b136a..1dd709656c23 100644 --- a/packages/babel-runtime-corejs2/helpers/iterableToArray.js +++ b/packages/babel-runtime-corejs2/helpers/iterableToArray.js @@ -2,8 +2,10 @@ var _Array$from = require("../core-js/array/from"); 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]" || _isIterable(Object(iter))) return _Array$from(iter); + if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof _Symbol !== "undefined" && _isIterable(Object(iter))) return _Array$from(iter); } module.exports = _iterableToArray; \ No newline at end of file diff --git a/packages/babel-runtime/helpers/esm/iterableToArray.js b/packages/babel-runtime/helpers/esm/iterableToArray.js index c4c541415481..a680535864bd 100644 --- a/packages/babel-runtime/helpers/esm/iterableToArray.js +++ b/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]" || Symbol.iterator in Object(iter)) return Array.from(iter); + if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } \ No newline at end of file diff --git a/packages/babel-runtime/helpers/iterableToArray.js b/packages/babel-runtime/helpers/iterableToArray.js index 1670d4c8e194..bd49473c9977 100644 --- a/packages/babel-runtime/helpers/iterableToArray.js +++ b/packages/babel-runtime/helpers/iterableToArray.js @@ -1,5 +1,5 @@ function _iterableToArray(iter) { - if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || Symbol.iterator in Object(iter)) return Array.from(iter); + if (typeof iter === 'string' || Object.prototype.toString.call(iter) === "[object Arguments]" || typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } module.exports = _iterableToArray; \ No newline at end of file