diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ca6d9e2eed8..1581eed8da0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,191 @@ _Note: Gaps between patch versions are faulty, broken or test releases._ See [CHANGELOG - 6to5](CHANGELOG-6to5.md) for the pre-4.0.0 version changelog. + +## v6.20.0 (2016-12-08) + +> If you missed it, please check out our latest blog post: [The State of Babel](http://babeljs.io/blog/2016/12/07/the-state-of-babel). Talks about where we can possibly move forward as a project and how you can help! + +- Maybe fix that crazy babel-generator deopt message you've all probably seen! +- Change to `babel-code-frame` for [facebookincubator/create-react-app#1101](https://github.com/facebookincubator/create-react-app/issues/1101) +- Change to `babel-generator` for [webpack/webpack#3413](https://github.com/webpack/webpack/pull/3413) +- Move implementation of Regenerator back to the original repo. + +--- + +You've probably seen this more than a few times and had no idea what it meant... + +``` +[BABEL] Note: The code generator has deoptimised the styling of "app.js" as it exceeds the max of "100KB". +``` + +Generating code used to get really slow as file size increased. We've mostly fixed that, but we still automatically fall back to compact output on large files. We're going to bump the limit to 500KB and if there aren't issues just remove it. + +--- + +[Ben Newman, @benjamn](https://github.com/benjamn): wrote [Regenerator](https://github.com/facebook/regenerator) while at Facebook. It used a bunch of other libraries such as `ast-types` but has now been rewritten as a standalone **Babel plugin** (also thanks to [Sebastian's](https://github.com/kittens) previous work in [facebook/regenerator#222](https://github.com/facebook/regenerator/pull/222)). We're also moving the implementation of Regenerator back into the original repository since Ben is the creator/maintainer. + +#### :rocket: New Feature +* `babel-traverse` + * [#4876](https://github.com/babel/babel/pull/4876) Add `getBindingIdentifierPaths`/`getOuterBindingIdentifierPaths`. ([@boopathi](https://github.com/boopathi)) + +Returns `Array` rather than `Array`. + +- `path.getBindingIdentifierPaths()` +- `path.getOuterBindingIdentifierPaths()` + +```js +traverse(parse(` + var a = 1, {b} = c, [d] = e, function f() {}; +`), { + VariableDeclaration(path) { + let nodes = path.getBindingIdentifiers(); // a, d, b + let paths = path.getBindingIdentifierPaths(); + }, + FunctionDeclaration(path) { + let outerNodes = path.getOuterBindingIdentifiers(); // f + let outerPaths = path.getOuterBindingIdentifierPaths(); + } +}); +``` + +* `babel-code-frame` + * [#4913](https://github.com/babel/babel/pull/4913) Add `forceColor` option to `babel-code-frame`. ([@Timer](https://github.com/Timer)) + +> Forcibly syntax highlight the code as JavaScript (for non-terminals); overrides `highlightCode`. For [facebookincubator/create-react-app#1101](https://github.com/facebookincubator/create-react-app/issues/1101) + +Usage + +```js +const result = codeFrame(rawLines, lineNumber, colNumber, { + forceColor: true +}); +``` + +#### :bug: Bug Fix +* `babel-plugin-transform-es2015-block-scoping` + * [#4880](https://github.com/babel/babel/pull/4880) Add (and fix) failing test of function parameter bindings in a catch block. ([@benjamn](https://github.com/benjamn)) + +**In** + +```js +try { + foo(); +} catch (x) { + function harmless(x) { + return x; + } +} +``` + +**Correct Out** + +```js +try { + foo(); +} catch (x) { + var harmless = function (x) { + return x; + }; +} +``` + +* `babel-helper-remap-async-to-generator`, `babel-plugin-transform-async-generator-functions`, `babel-plugin-transform-async-to-generator` + * [#4901](https://github.com/babel/babel/pull/4901) Only base async fn arity on non-default/non-rest params - Closes [#4891](https://github.com/babel/babel/issues/4891). ([@loganfsmyth](https://github.com/loganfsmyth)) + +```js +// both length's should be 0 +const foo = (...args) => { } +console.log(foo.length) // 0 +const asyncFoo = async (...args) => { } +console.log(asyncFoo.length) // 0 +``` + +* `babel-generator`, `babel-types` + * [#4945](https://github.com/babel/babel/pull/4945) Add `babel-generator` support for `Import`. ([@TheLarkInn](https://github.com/TheLarkInn)) + +> Relevant for webpack 2 support of `Import`. Just allows Babel to print it correctly. + +```js +import("module.js"); +``` + +* `babel-plugin-transform-object-rest-spread` + * [#4883](https://github.com/babel/babel/pull/4883) Fix for object-rest with parameters destructuring nested rest. ([@christophehurpeau](https://github.com/christophehurpeau)) + +```js +function a5({a3, b2: { ba1, ...ba2 }, ...c3}) {} +``` +* `babel-traverse` + * [#4875](https://github.com/babel/babel/pull/4875) Fix `path.evaluate` for references before declarations. ([@boopathi](https://github.com/boopathi)) + +```js +// should deopt if ids are referenced before the bindings +var a = b + 2; var b = 2 + 2; +``` + +* `babel-core`, `babel-generator`, `babel-helper-transform-fixture-test-runner`, `babel-plugin-transform-object-rest-spread` + * [#4858](https://github.com/babel/babel/pull/4858) Fix bug + Generate test fixtures if no expected.js. ([@hzoo](https://github.com/hzoo)) +* `babel-types` + * [#4853](https://github.com/babel/babel/pull/4853) Preserve null in `babel-types` `t.clone` and `t.deepClone` ([@NTillmann](https://github.com/NTillmann)) + +#### :nail_care: Polish +* `babel-generator` + * [#4862](https://github.com/babel/babel/pull/4862) Fix identation with empty leading `ObjectTypeProperty`. ([@existentialism](https://github.com/existentialism)) + +#### :memo: Documentation +* `Various Packages` + * [#4938](https://github.com/babel/babel/pull/4938) Update babel-core documentation. ([@xtuc](https://github.com/xtuc)) + * [#4939](https://github.com/babel/babel/pull/4939) Add example to transform-react-display-name docs. ([@existentialism](https://github.com/existentialism)) + * [#4931](https://github.com/babel/babel/pull/4931) Update plugins READMEs from babel.github.io [skip ci]. ([@raspo](https://github.com/raspo)) + * [#4926](https://github.com/babel/babel/pull/4926) Update transform-es2015 READMEs from babel.github.io [skip ci]. ([@existentialism](https://github.com/existentialism)) + * [#4930](https://github.com/babel/babel/pull/4930) Update transform-object-rest-spread's README from babel.github.io [skip ci]. ([@lukyth](https://github.com/lukyth)) + * [#4929](https://github.com/babel/babel/pull/4929) Update transform-object-assign's README from babel.github.io [skip ci]. ([@lukyth](https://github.com/lukyth)) + * [#4928](https://github.com/babel/babel/pull/4928) mention [skip ci] in PR template. ([@hzoo](https://github.com/hzoo)) + * [#4925](https://github.com/babel/babel/pull/4925) Tweak example in transform-jsx-source README [skip ci]. ([@existentialism](https://github.com/existentialism)) + * [#4919](https://github.com/babel/babel/pull/4919) Update async READMEs from babel.github.io [skip-ci]. ([@existentialism](https://github.com/existentialism)) + * [#4917](https://github.com/babel/babel/pull/4917) Fix some React transform README issues [skip-ci]. ([@existentialism](https://github.com/existentialism)) + * [#4903](https://github.com/babel/babel/pull/4903) Update React transform READMEs from babel.github.io [skip ci]. ([@existentialism](https://github.com/existentialism)) + * [#4884](https://github.com/babel/babel/pull/4884) Readme updates from babel.github.io [skip ci]. ([@hzoo](https://github.com/hzoo)) + +#### :house: Internal +* `babel-plugin-transform-regenerator` + * [#4881](https://github.com/babel/babel/pull/4881) Use `regenerator-transform` to implement `babel-plugin-transform-regenerator`. ([@benjamn](https://github.com/benjamn)) +* `babel-traverse` + * [#4934](https://github.com/babel/babel/pull/4934) Hoist `generateDeclaredUidIdentifier` helper function. ([@jridgewell](https://github.com/jridgewell)) +* `babel-polyfill` + * [#4966](https://github.com/babel/babel/pull/4966) update `regenerator-runtime` in `babel-polyfill`. ([@zloirock](https://github.com/zloirock)) +* `babel-runtime` + * [#4877](https://github.com/babel/babel/pull/4877) Upgrade `regenerator-runtime` to version 0.10.0. ([@benjamn](https://github.com/benjamn)) +* `babel-plugin-syntax-trailing-function-commas` + * [#4936](https://github.com/babel/babel/pull/4936) Add `test` to `babel-plugin-syntax-trailing-function-commas` `.npmignore` ([@wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg)) +* `babel-helper-fixtures` + * [#4907](https://github.com/babel/babel/pull/4907) Remove `shouldIgnore` check. ([@danez](https://github.com/danez)) +* `babel-core`, `babel-traverse` + * [#4897](https://github.com/babel/babel/pull/4897) Fix eslint. ([@danez](https://github.com/danez)) +* `babel-generator` + * [#4965](https://github.com/babel/babel/pull/4965) Raise limit on code size before compacting ([@existentialism](https://github.com/existentialism)) + +#### Committers: 17 +- Ben Newman ([benjamn](https://github.com/benjamn)) +- Benjamin E. Coe ([bcoe](https://github.com/bcoe)) +- Boopathi Rajaa ([boopathi](https://github.com/boopathi)) +- Brian Ng ([existentialism](https://github.com/existentialism)) +- Christophe Hurpeau ([christophehurpeau](https://github.com/christophehurpeau)) +- Daniel Tschinder ([danez](https://github.com/danez)) +- Denis Pushkarev ([zloirock](https://github.com/zloirock)) +- Henry Zhu ([hzoo](https://github.com/hzoo)) +- Joe Haddad ([Timer](https://github.com/Timer)) +- Justin Ridgewell ([jridgewell](https://github.com/jridgewell)) +- Kanitkorn Sujautra ([lukyth](https://github.com/lukyth)) +- Logan Smyth ([loganfsmyth](https://github.com/loganfsmyth)) +- Nikolai Tillmann ([NTillmann](https://github.com/NTillmann)) +- Sean Larkin ([TheLarkInn](https://github.com/TheLarkInn)) +- Sven SAULEAU ([xtuc](https://github.com/xtuc)) +- Tommaso ([raspo](https://github.com/raspo)) +- [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) + ## v6.19.0 (2016-11-16) #### :rocket: New Feature diff --git a/Makefile b/Makefile index 46bcc392dabc..c7e27bf712c1 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ build-dist: build node scripts/generate-babel-types-docs.js watch: clean + rm -rf packages/*/lib ./node_modules/.bin/gulp watch lint: @@ -27,7 +28,6 @@ fix: ./node_modules/.bin/eslint packages/ --format=codeframe --fix clean: test-clean - rm -rf packages/*/lib rm -rf packages/babel-polyfill/browser* rm -rf packages/babel-polyfill/dist rm -rf coverage diff --git a/lerna.json b/lerna.json index d9fb63b4f588..3236999da4d4 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "lerna": "2.0.0-beta.23", - "version": "6.19.0", + "version": "6.20.3", "changelog": { "repo": "babel/babel", "labels": { diff --git a/packages/babel-code-frame/package.json b/packages/babel-code-frame/package.json index 113ac80fb0cc..f48072728355 100644 --- a/packages/babel-code-frame/package.json +++ b/packages/babel-code-frame/package.json @@ -1,6 +1,6 @@ { "name": "babel-code-frame", - "version": "6.16.0", + "version": "6.20.0", "description": "Generate errors that contain a code frame that point to source locations.", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", diff --git a/packages/babel-core/README.md b/packages/babel-core/README.md index f73054b42357..3db09945ec2c 100644 --- a/packages/babel-core/README.md +++ b/packages/babel-core/README.md @@ -108,7 +108,7 @@ Following is a table of the options you can use: | `code` | `true` | Enable code generation | | `no-babelrc` | [CLI flag](http://babeljs.io/docs/usage/cli/#ignoring-babelrc) | Specify whether or not to use .babelrc and .babelignore files. Only available when using the CLI. | | `ast` | `true` | Include the AST in the returned object | -| `compact` | `"auto"` | Do not include superfluous whitespace characters and line terminators. When set to `"auto"` compact is set to `true` on input sizes of >100KB. | +| `compact` | `"auto"` | Do not include superfluous whitespace characters and line terminators. When set to `"auto"` compact is set to `true` on input sizes of >500KB. | | `minified` | `false` | Should the output be minified (not printing last semicolons in blocks, printing literal string values instead of escaped ones, stripping `()` from `new` when safe) | | `comments` | `true` | Output comments in generated output. | | `shouldPrintComment` | `null` | An optional callback that controls whether a comment should be output or not. Called as `shouldPrintComment(commentContents)`. **NOTE:** This overrides the `comment` option when used. | diff --git a/packages/babel-core/package.json b/packages/babel-core/package.json index 0f921c72bd3a..02f3cd66a085 100644 --- a/packages/babel-core/package.json +++ b/packages/babel-core/package.json @@ -1,6 +1,6 @@ { "name": "babel-core", - "version": "6.18.2", + "version": "6.20.0", "description": "Babel compiler core.", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", @@ -24,15 +24,15 @@ "test": "make test" }, "dependencies": { - "babel-code-frame": "^6.16.0", - "babel-generator": "^6.18.0", + "babel-code-frame": "^6.20.0", + "babel-generator": "^6.20.0", "babel-helpers": "^6.16.0", "babel-messages": "^6.8.0", "babel-template": "^6.16.0", - "babel-runtime": "^6.9.1", + "babel-runtime": "^6.20.0", "babel-register": "^6.18.0", - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", + "babel-traverse": "^6.20.0", + "babel-types": "^6.20.0", "babylon": "^6.11.0", "convert-source-map": "^1.1.0", "debug": "^2.1.1", @@ -45,8 +45,8 @@ "source-map": "^0.5.0" }, "devDependencies": { - "babel-helper-fixtures": "^6.18.2", - "babel-helper-transform-fixture-test-runner": "^6.18.2", - "babel-polyfill": "^6.16.0" + "babel-helper-fixtures": "^6.20.0", + "babel-helper-transform-fixture-test-runner": "^6.20.0", + "babel-polyfill": "^6.20.0" } } diff --git a/packages/babel-generator/package.json b/packages/babel-generator/package.json index 0d5fb8589ef7..84fd0c04d282 100644 --- a/packages/babel-generator/package.json +++ b/packages/babel-generator/package.json @@ -1,6 +1,6 @@ { "name": "babel-generator", - "version": "6.19.0", + "version": "6.20.0", "description": "Turns an AST into code.", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", @@ -12,15 +12,15 @@ ], "dependencies": { "babel-messages": "^6.8.0", - "babel-runtime": "^6.9.0", - "babel-types": "^6.19.0", + "babel-runtime": "^6.20.0", + "babel-types": "^6.20.0", "detect-indent": "^4.0.0", "jsesc": "^1.3.0", "lodash": "^4.2.0", "source-map": "^0.5.0" }, "devDependencies": { - "babel-helper-fixtures": "^6.18.0", + "babel-helper-fixtures": "^6.20.0", "babylon": "^6.11.0" } } diff --git a/packages/babel-generator/src/index.js b/packages/babel-generator/src/index.js index c730c6689ae3..c212cbdf7c05 100644 --- a/packages/babel-generator/src/index.js +++ b/packages/babel-generator/src/index.js @@ -38,7 +38,7 @@ class Generator extends Printer { * Normalize generator options, setting defaults. * * - Detects code indentation. - * - If `opts.compact = "auto"` and the code is over 100KB, `compact` will be set to `true`. + * - If `opts.compact = "auto"` and the code is over 500KB, `compact` will be set to `true`. */ function normalizeOptions(code, opts, tokens): Format { @@ -78,10 +78,10 @@ function normalizeOptions(code, opts, tokens): Format { } if (format.compact === "auto") { - format.compact = code.length > 100000; // 100KB + format.compact = code.length > 500000; // 500KB if (format.compact) { - console.error("[BABEL] " + messages.get("codeGeneratorDeopt", opts.filename, "100KB")); + console.error("[BABEL] " + messages.get("codeGeneratorDeopt", opts.filename, "500KB")); } } diff --git a/packages/babel-helper-fixtures/package.json b/packages/babel-helper-fixtures/package.json index ab7694f79012..974ea4321730 100644 --- a/packages/babel-helper-fixtures/package.json +++ b/packages/babel-helper-fixtures/package.json @@ -1,13 +1,13 @@ { "name": "babel-helper-fixtures", - "version": "6.18.2", + "version": "6.20.0", "description": "Helper function to support fixtures", "author": "Sebastian McKenzie ", "license": "MIT", "repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-fixtures", "main": "lib/index.js", "dependencies": { - "babel-runtime": "^6.9.0", + "babel-runtime": "^6.20.0", "lodash": "^4.2.0", "try-resolve": "^1.0.0" } diff --git a/packages/babel-helper-remap-async-to-generator/package.json b/packages/babel-helper-remap-async-to-generator/package.json index 4d3876a19ea8..0e633745d612 100644 --- a/packages/babel-helper-remap-async-to-generator/package.json +++ b/packages/babel-helper-remap-async-to-generator/package.json @@ -1,15 +1,15 @@ { "name": "babel-helper-remap-async-to-generator", - "version": "6.18.0", + "version": "6.20.3", "description": "Helper function to remap async functions to generators", "repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-remap-async-to-generator", "license": "MIT", "main": "lib/index.js", "dependencies": { - "babel-runtime": "^6.0.0", + "babel-runtime": "^6.20.0", "babel-template": "^6.16.0", - "babel-types": "^6.18.0", - "babel-traverse": "^6.18.0", + "babel-types": "^6.20.0", + "babel-traverse": "^6.20.0", "babel-helper-function-name": "^6.18.0" } } diff --git a/packages/babel-helper-remap-async-to-generator/src/index.js b/packages/babel-helper-remap-async-to-generator/src/index.js index a7600e413a7c..840a195d8afd 100644 --- a/packages/babel-helper-remap-async-to-generator/src/index.js +++ b/packages/babel-helper-remap-async-to-generator/src/index.js @@ -123,7 +123,7 @@ function plainFunction(path: NodePath, callId: Object) { REF: path.scope.generateUidIdentifier("ref"), FUNCTION: built, PARAMS: node.params.reduce((acc, param) => { - acc.done = acc.done || !t.isIdentifier(param); + acc.done = acc.done || t.isAssignmentPattern(param) || t.isRestElement(param); if (!acc.done) { acc.params.push(path.scope.generateUidIdentifier("x")); diff --git a/packages/babel-helper-transform-fixture-test-runner/package.json b/packages/babel-helper-transform-fixture-test-runner/package.json index 564226d7472c..4239b53e4772 100644 --- a/packages/babel-helper-transform-fixture-test-runner/package.json +++ b/packages/babel-helper-transform-fixture-test-runner/package.json @@ -1,6 +1,6 @@ { "name": "babel-helper-transform-fixture-test-runner", - "version": "6.18.2", + "version": "6.20.0", "description": "Transform test runner for babel-helper-fixtures module", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", @@ -8,12 +8,12 @@ "repository": "https://github.com/babel/babel/tree/master/packages/babel-helper-transform-fixture-test-runner", "main": "lib/index.js", "dependencies": { - "babel-runtime": "^6.9.0", - "babel-core": "^6.18.2", - "babel-polyfill": "^6.16.0", - "babel-helper-fixtures": "^6.18.2", + "babel-runtime": "^6.20.0", + "babel-core": "^6.20.0", + "babel-polyfill": "^6.20.0", + "babel-helper-fixtures": "^6.20.0", "source-map": "^0.5.0", - "babel-code-frame": "^6.16.0", + "babel-code-frame": "^6.20.0", "chai": "^3.0.0", "lodash": "^4.2.0" } diff --git a/packages/babel-plugin-syntax-trailing-function-commas/package.json b/packages/babel-plugin-syntax-trailing-function-commas/package.json index 076e23337e8a..74d448be81b3 100644 --- a/packages/babel-plugin-syntax-trailing-function-commas/package.json +++ b/packages/babel-plugin-syntax-trailing-function-commas/package.json @@ -1,6 +1,6 @@ { "name": "babel-plugin-syntax-trailing-function-commas", - "version": "6.13.0", + "version": "6.20.0", "description": "Compile trailing function commas to ES5", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-syntax-trailing-function-commas", "license": "MIT", diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/actual.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/actual.js index 492289af70d2..a08e67e7f33a 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/actual.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/actual.js @@ -1,4 +1,6 @@ async function one(a, b = 1) {} async function two(a, b, ...c) {} async function three(a, b = 1, c, d = 3) {} -async function four(a, b = 1, c, ...d) {} \ No newline at end of file +async function four(a, b = 1, c, ...d) {} +async function five(a, {b}){} +async function six(a, {b} = {}){} diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/expected.js index 1db5d28058ee..e74b618424d4 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/function-arity/expected.js @@ -28,4 +28,20 @@ let four = (() => { return function four(_x5) { return _ref4.apply(this, arguments); }; +})(); + +let five = (() => { + var _ref5 = babelHelpers.asyncToGenerator(function* (a, { b }) {}); + + return function five(_x6, _x7) { + return _ref5.apply(this, arguments); + }; +})(); + +let six = (() => { + var _ref6 = babelHelpers.asyncToGenerator(function* (a, { b } = {}) {}); + + return function six(_x8) { + return _ref6.apply(this, arguments); + }; })(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-block-scoping/package.json b/packages/babel-plugin-transform-es2015-block-scoping/package.json index 49f8144055cd..4df1c289cee3 100644 --- a/packages/babel-plugin-transform-es2015-block-scoping/package.json +++ b/packages/babel-plugin-transform-es2015-block-scoping/package.json @@ -1,16 +1,16 @@ { "name": "babel-plugin-transform-es2015-block-scoping", - "version": "6.18.0", + "version": "6.20.0", "description": "Compile ES2015 block scoping (const and let) to ES5", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-es2015-block-scoping", "license": "MIT", "main": "lib/index.js", "dependencies": { - "babel-traverse": "^6.18.0", - "babel-types": "^6.18.0", + "babel-traverse": "^6.20.0", + "babel-types": "^6.20.0", "babel-template": "^6.15.0", "lodash": "^4.2.0", - "babel-runtime": "^6.9.0" + "babel-runtime": "^6.20.0" }, "keywords": [ "babel-plugin" diff --git a/packages/babel-plugin-transform-es2015-unicode-regex/README.md b/packages/babel-plugin-transform-es2015-unicode-regex/README.md index a522e098040f..56b2035f6ce1 100644 --- a/packages/babel-plugin-transform-es2015-unicode-regex/README.md +++ b/packages/babel-plugin-transform-es2015-unicode-regex/README.md @@ -2,6 +2,22 @@ > Compile ES2015 unicode regex to ES5 +## Example + +**In** + +```js +var string = "foo💩bar"; +var match = string.match(/foo(.)bar/u); +``` + +**Out** + +```js +var string = "foo💩bar"; +var match = string.match(/foo((?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))bar/); +``` + ## Installation ```sh diff --git a/packages/babel-plugin-transform-object-rest-spread/package.json b/packages/babel-plugin-transform-object-rest-spread/package.json index 829aa747cd04..572fc9c51676 100644 --- a/packages/babel-plugin-transform-object-rest-spread/package.json +++ b/packages/babel-plugin-transform-object-rest-spread/package.json @@ -1,6 +1,6 @@ { "name": "babel-plugin-transform-object-rest-spread", - "version": "6.19.0", + "version": "6.20.2", "description": "Compile object rest and spread to ES5", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-object-rest-spread", "license": "MIT", @@ -10,7 +10,7 @@ ], "dependencies": { "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.0.0" + "babel-runtime": "^6.20.0" }, "devDependencies": { "babel-helper-plugin-test-runner": "^6.8.0" diff --git a/packages/babel-plugin-transform-object-rest-spread/src/index.js b/packages/babel-plugin-transform-object-rest-spread/src/index.js index 7d04ee311d2f..dbc357be07d9 100644 --- a/packages/babel-plugin-transform-object-rest-spread/src/index.js +++ b/packages/babel-plugin-transform-object-rest-spread/src/index.js @@ -4,19 +4,19 @@ export default function ({ types: t }) { path.traverse({ RestProperty() { foundRestProperty = true; + path.stop(); } }); return foundRestProperty; } - function hasSpread(path) { - let foundSpreadProperty = false; - path.traverse({ - SpreadProperty() { - foundSpreadProperty = true; + function hasSpread(node) { + for (let prop of (node.properties)) { + if (t.isSpreadProperty(prop)) { + return true; } - }); - return foundSpreadProperty; + } + return false; } function createObjectSpread(file, props, objRef) { @@ -223,7 +223,7 @@ export default function ({ types: t }) { }, // var a = { ...b, ...c } ObjectExpression(path, file) { - if (!hasSpread(path)) return; + if (!hasSpread(path.node)) return; let useBuiltIns = file.opts.useBuiltIns || false; if (typeof useBuiltIns !== "boolean") { diff --git a/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/actual.js b/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/actual.js index 8d610842d85f..6f0367fb7c70 100644 --- a/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/actual.js +++ b/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/actual.js @@ -1 +1,3 @@ z = { x, ...y }; + +z = { x, w: { ...y } }; diff --git a/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/expected.js b/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/expected.js index 5e42763fcec7..1b1a3defbabd 100644 --- a/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/expected.js +++ b/packages/babel-plugin-transform-object-rest-spread/test/fixtures/object-spread/assignment/expected.js @@ -1,3 +1,5 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; z = _extends({ x }, y); + +z = { x, w: _extends({}, y) }; diff --git a/packages/babel-plugin-transform-regenerator/.npmignore b/packages/babel-plugin-transform-regenerator/.npmignore index a3dd02aae49b..31852902b187 100644 --- a/packages/babel-plugin-transform-regenerator/.npmignore +++ b/packages/babel-plugin-transform-regenerator/.npmignore @@ -1,4 +1,4 @@ -/node_modules -/test -/.test -/src +node_modules +*.log +src +test diff --git a/packages/babel-plugin-transform-regenerator/.test/async.es6.js b/packages/babel-plugin-transform-regenerator/.test/async.es6.js deleted file mode 100644 index 587e9b5f0054..000000000000 --- a/packages/babel-plugin-transform-regenerator/.test/async.es6.js +++ /dev/null @@ -1,493 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); - -describe("async functions and await expressions", function() { - Promise = require("promise"); - - describe("regeneratorRuntime", function() { - it("should be defined globally", function() { - var global = Function("return this")(); - assert.ok("regeneratorRuntime" in global); - assert.strictEqual(global.regeneratorRuntime, regeneratorRuntime); - }); - - it("should have a .wrap method", function() { - assert.strictEqual(typeof regeneratorRuntime.wrap, "function"); - }); - }); - - describe("Promise", function() { - it("should be defined globally", function() { - var global = Function("return this")(); - assert.ok("Promise" in global); - assert.strictEqual(global.Promise, Promise); - }); - - it("should be a function", function() { - assert.strictEqual(typeof Promise, "function"); - }); - }); - - describe("no-await async function", function() { - it("should return a Promise", function(done) { - var called = false; - - async function noAwait(value) { - called = true; - return value; - } - - var promise = noAwait("asdf"); - assert.strictEqual(called, true); - - promise.then(function(value) { - assert.strictEqual(called, true); - assert.strictEqual(value, "asdf"); - done(); - }).catch(done); - }); - }); - - describe("one-await async function", function() { - it("should finish asynchronously", function(done) { - var flag1 = false; - var flag2 = false; - - async function oneAwait(value) { - flag1 = true; - var result = await value; - flag2 = true; - return result; - } - - var promise = oneAwait("asdf"); - assert.strictEqual(flag1, true); - assert.strictEqual(flag2, false); - - promise.then(function(value) { - assert.strictEqual(flag2, true); - assert.strictEqual(value, "asdf"); - done(); - }).catch(done); - }); - }); - - describe("nested async function calls", function() { - it("should evaluate in the right order", function(done) { - var markers = []; - - async function innerMost(marker) { - markers.push(marker); - return await marker; - } - - async function inner(marker) { - markers.push(marker); - - assert.strictEqual( - await innerMost(marker + 1), - marker + 1 - ); - - markers.push(marker + 2); - - assert.strictEqual( - await innerMost(marker + 3), - marker + 3 - ); - - markers.push(marker + 4); - } - - async function outer() { - markers.push(0); - await inner(1); - markers.push(6); - await inner(7); - markers.push(12); - } - - outer().then(function() { - var expected = []; - for (var i = 0; i <= 12; ++i) - expected.push(i); - assert.deepEqual(markers, expected); - done(); - }).catch(done); - }); - }); - - describe("dependent promises", function() { - it("should be awaitable out of order", function(done) { - async function outer(value) { - var resolved = false; - var p1 = new Promise(function(resolve) { - setTimeout(function() { - resolve(value + 1); - resolved = true; - }, 0); - }); - - assert.strictEqual(resolved, false); - - var v2 = await p1.then(function(value) { - return value + 1; - }); - - assert.strictEqual(resolved, true); - - var v1 = await p1; - - return [v1, v2]; - } - - outer(1).then(function(pair) { - assert.deepEqual(pair, [2, 3]); - done(); - }).catch(done); - }); - }); - - describe("rejected promises", function() { - it("should cause await expressions to throw", function(done) { - var error = new Error("rejected"); - - async function f(arg) { - try { - return await arg; - } catch (e) { - assert.strictEqual(e, error); - return "did throw"; - } - } - - Promise.all([ - f(Promise.reject(error)), - f(Promise.resolve("did not throw")) - ]).then(function(results) { - assert.deepEqual(results, [ - "did throw", - "did not throw" - ]); - done(); - }).catch(done); - }); - - it("should be returned by exceptional async functions", function(done) { - var error = new Error("rejected"); - - async function e(arg) { - if (arg) { - throw arg; - } - return "did not throw"; - } - - async function f(arg) { - return await e(arg); - } - - async function g(arg) { - return await f(arg); - } - - async function h(arg) { - return await Promise.all([ - g(arg), - Promise.resolve("dummy") - ]); - } - - Promise.all([ - h(error).then(function() { - done(new Error("should not have resolved")); - }, function(e) { - assert.strictEqual(e, error); - return "ok1"; - }), - h(null).then(function(result) { - assert.deepEqual(result, [ - "did not throw", - "dummy" - ]); - return "ok2"; - }) - ]).then(function(results) { - assert.deepEqual(results, ["ok1", "ok2"]); - done(); - }).catch(done); - }); - - it("should propagate failure when returned", function() { - var rejection = new Error("rejection"); - - async function f() { - return new Promise(function(resolve, reject) { - reject(rejection); - }); - } - - return f().then(function(result) { - assert.ok(false, "should have been rejected"); - }, function(error) { - assert.strictEqual(error, rejection); - }); - }); - }); - - describe("async function expressions", function() { - it("should be allowed", function(done) { - (async function(arg) { - return await arg; - })(Promise.resolve(1234)).then(function(value) { - assert.strictEqual(value, 1234); - done(); - }).catch(done); - }); - }); -}); - -describe("async generator functions", function() { - it("should return a working AsyncIterator", function() { - var markers = []; - - async function *gen(arg) { - markers.push(0); - var sent = yield arg; - markers.push(1); - var result = await sent; - markers.push(2); - assert.strictEqual(await (yield "second"), "sent after second"); - markers.push(3); - return result; - } - - var iter = gen("initial argument"); - assert.deepEqual(markers, []); - - var firstPromise = iter.next(); - assert.deepEqual(markers, [0]); - - return firstPromise.then(function(firstResult) { - assert.deepEqual(firstResult, { - value: "initial argument", - done: false - }); - - assert.deepEqual(markers, [0]); - - return iter.next(new Promise(function(resolve) { - setTimeout(resolve, 100); - }).then(function() { - assert.deepEqual(markers, [0, 1]); - return "will become final result"; - })); - - }).then(function(secondResult) { - assert.deepEqual(secondResult, { - value: "second", - done: false - }); - - assert.deepEqual(markers, [0, 1, 2]); - - return iter.next("sent after second"); - - }).then(function(finalResult) { - assert.deepEqual(markers, [0, 1, 2, 3]); - assert.deepEqual(finalResult, { - value: "will become final result", - done: true - }); - }); - }); - - it("should keep results in order", function() { - async function *range(limit) { - var before = []; - var after = []; - for (var i = 0; i < limit; ++i) { - before.push(i); - yield i; - after.push(i); - } - assert.deepEqual(before, after); - return before; - } - - var limit = 10; - var iter = range(limit); - var promises = []; - var results = []; - - for (var i = 0; i < limit; ++i) { - var promise = iter.next(); - promises.push(promise); - - promise.then(function(result) { - assert.strictEqual(result.done, false); - results.push(result); - }); - } - - assert.deepEqual(results, []); - - return Promise.all(promises).then(function(promiseResults) { - assert.deepEqual(results, promiseResults); - - return iter.next(); - - }).then(function(finalResult) { - assert.deepEqual(results.map(function(result) { - return result.value; - }), finalResult.value); - - assert.strictEqual(finalResult.done, true); - }); - }); - - it("should be able to handle many awaits", function() { - var awaitCount = 0; - - function countAwait(i) { - return Promise.resolve(i).then(function() { - ++awaitCount; - }); - } - - async function *gen(limit) { - await countAwait(0); - yield 1; - await countAwait(2); - await countAwait(3); - yield 4; - await countAwait(5); - await countAwait(6); - await countAwait(7); - yield 8; - for (var i = 0; i < limit; ++i) { - await countAwait(i); - } - return "done"; - } - - var iter = gen(100); - - return iter.next().then(function(result) { - assert.strictEqual(awaitCount, 1); - - assert.deepEqual(result, { - value: 1, - done: false - }); - - return iter.next(); - - }).then(function(result) { - assert.strictEqual(awaitCount, 3); - - assert.deepEqual(result, { - value: 4, - done: false - }); - - return iter.next(); - - }).then(function(result) { - assert.strictEqual(awaitCount, 6); - - assert.deepEqual(result, { - value: 8, - done: false - }); - - return iter.next(); - - }).then(function(result) { - assert.strictEqual(awaitCount, 6 + 100); - - assert.deepEqual(result, { - value: "done", - done: true - }); - - return iter.next(); - - }).then(function(result) { - assert.deepEqual(result, { - value: void 0, - done: true - }); - }); - }); - - it("should not propagate exceptions between iterations", function() { - async function *gen() { - yield 1; - yield 2; - } - - var iter = gen(); - - return iter.next().then(function(result) { - assert.deepEqual(result, { - value: 1, - done: false - }); - - return iter.throw(new Error("thrown from first yield")); - - }).then(function() { - throw new Error("should have thrown"); - - }, function(error) { - assert.strictEqual(error.message, "thrown from first yield"); - return iter.next(); - - }).then(function(result) { - assert.deepEqual(result, { - value: void 0, - done: true - }); - }); - }); - - it("should allow yielding a rejected Promise", function() { - var yielded = new Error("yielded rejection"); - var returned = new Error("returned rejection"); - - async function *gen() { - assert.strictEqual(yield Promise.reject(yielded), "first sent"); - assert.strictEqual(yield "middle", "second sent"); - return Promise.reject(returned); - } - - var iter = gen(); - - return iter.next().then(function(result) { - assert.ok(false, "should have yielded a rejected Promise"); - }, function(error) { - assert.strictEqual(error, yielded); - return iter.next("first sent"); - }).then(function(result) { - assert.deepEqual(result, { - value: "middle", - done: false - }); - return iter.next("second sent"); - }).then(function(result) { - assert.ok(false, "should have returned a rejected Promise"); - }, function(error) { - assert.strictEqual(error, returned); - }); - }); -}); diff --git a/packages/babel-plugin-transform-regenerator/.test/tests.es6.js b/packages/babel-plugin-transform-regenerator/.test/tests.es6.js deleted file mode 100644 index 3df59e76e9ab..000000000000 --- a/packages/babel-plugin-transform-regenerator/.test/tests.es6.js +++ /dev/null @@ -1,2560 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -var assert = require("assert"); -var runningInTranslation = /\.wrap\(/.test(function*(){}); -var iteratorSymbol = typeof Symbol === "function" - && Symbol.iterator - || "@@iterator"; - -function check(g, yields, returnValue) { - for (var i = 0; i < yields.length; ++i) { - var info = g.next(i); - assert.deepEqual(info.value, yields[i]); - assert.strictEqual(info.done, false); - } - - assert.deepEqual( - i > 0 ? g.next(i) : g.next(), - { value: returnValue, done: true } - ); -} - -// A version of `throw` whose behavior can't be statically analyzed. -// Useful for testing dynamic exception dispatching. -function raise(argument) { - throw argument; -} - -function assertAlreadyFinished(generator) { - assert.deepEqual(generator.next(), { - value: void 0, - done: true - }); -} - -describe("regeneratorRuntime", function() { - it("should be defined globally", function() { - var global = Function("return this")(); - assert.ok("regeneratorRuntime" in global); - assert.strictEqual(global.regeneratorRuntime, regeneratorRuntime); - }); - - it("should have a .wrap method", function() { - assert.strictEqual(typeof regeneratorRuntime.wrap, "function"); - }); - - it("should have a .mark method", function() { - assert.strictEqual(typeof regeneratorRuntime.mark, "function"); - }); - - it("should be the object name returned by util.runtimeProperty", function() { - assert.strictEqual( - require("../lib/util").runtimeProperty("foo").object.name, - "regeneratorRuntime" - ); - }); -}); - -(runningInTranslation ? describe : xdescribe)("@@iterator", function() { - it("is defined on Generator.prototype and returns this", function() { - function *gen(){} - var iterator = gen(); - assert.ok(!iterator.hasOwnProperty(iteratorSymbol)); - assert.ok(!Object.getPrototypeOf(iterator).hasOwnProperty(iteratorSymbol)); - assert.ok(Object.getPrototypeOf( - Object.getPrototypeOf(iterator) - ).hasOwnProperty(iteratorSymbol)); - assert.strictEqual(iterator[iteratorSymbol](), iterator); - }); -}); - -describe("simple argument yielder", function() { - it("should yield only its first argument", function() { - function *gen(x) { - yield x; - } - - check(gen("oyez"), ["oyez"]); - check(gen("foo", "bar"), ["foo"]); - }); - - it("should support multiple yields in expression", function() { - function *gen() { return (yield 0) + (yield 0); } - var itr = gen(); - itr.next(); - itr.next(1); - assert.equal(itr.next(2).value, 3); - }); -}); - -function *range(n) { - for (var i = 0; i < n; ++i) { - yield i; - } -} - -describe("range generator", function() { - it("should yield the empty range", function() { - check(range(0), []); - }) - - it("should yield the range 0..n-1", function() { - check(range(5), [0, 1, 2, 3, 4]); - }); -}); - -describe("collatz generator", function() { - function *gen(n) { - var count = 0; - - yield n; - - while (n !== 1) { - count += 1; - - if (n % 2) { - yield n = n * 3 + 1; - } else { - yield n >>= 1; - } - } - - return count; - } - - function collatz(n) { - var result = [n]; - - while (n !== 1) { - if (n % 2) { - n *= 3; - n += 1; - } else { - n >>= 1; - } - - result.push(n); - } - - return result; - } - - var seven = collatz(7); - var fiftyTwo = seven.slice(seven.indexOf(52)); - var eightyTwo = collatz(82); - - it("seven", function() { - check(gen(7), seven, 16); - }); - - it("fifty two", function() { - check(gen(52), fiftyTwo, 11); - }); - - it("eighty two", function() { - check(gen(82), eightyTwo, 110); - }); -}); - -describe("throw", function() { - (runningInTranslation ? it : xit)("should complete generator", function() { - function *gen(x) { - throw 1; - } - - var u = gen(); - - try { - u.next(); - } catch (err) { - assert.strictEqual(err, 1); - } - - assertAlreadyFinished(u); - }); -}); - -describe("try-catch generator", function() { - function *usingThrow(x) { - yield 0; - try { - yield 1; - if (x % 2 === 0) - throw 2; - yield x; - } catch (x) { - yield x; - } - yield 3; - } - - function *usingRaise(x) { - yield 0; - try { - yield 1; - if (x % 2 === 0) - raise(2); - yield x; - } catch (x) { - yield x; - } - yield 3; - } - - it("should catch static exceptions properly", function() { - check(usingThrow(4), [0, 1, 2, 3]); - check(usingThrow(5), [0, 1, 5, 3]); - }); - - it("should catch dynamic exceptions properly", function() { - check(usingRaise(4), [0, 1, 2, 3]); - check(usingRaise(5), [0, 1, 5, 3]); - }); -}); - -describe("nested generators in try-catch", function() { - function *gen() { - try { - nonExistent; - } catch (e) { - yield function* () { - yield e; - } - } - } - - it('should get a reference to the caught error', function () { - var genFun2 = gen().next().value; - assert.ok(regeneratorRuntime.isGeneratorFunction(genFun2)); - var gen2 = genFun2(); - var res = gen2.next(); - assert.ok(res.value instanceof ReferenceError); - // Note that we don't do strict equality over the message because it varies - // across browsers (if we ever want to run tests in browsers). - assert.ok(res.value.message.match(/nonExistent/)); - }); - -}); - -describe("try-finally generator", function() { - function *usingThrow(condition) { - yield 0; - try { - yield 1; - throw 2; - yield 3; - } finally { - if (condition) { - yield 4; - return 5; - } - yield 6; - return 7; - } - } - - function *usingRaise(condition) { - yield 0; - try { - yield 1; - raise(2); - yield 3; - } finally { - if (condition) { - yield 4; - return 5; - } - yield 6; - return 7; - } - } - - function *usingAbrupt(abruptType, finallyAbruptType) { - yield 0; - for (;;) { - try { - yield 1; - if (abruptType === "return") { - return 2; - } else if (abruptType === "break") { - break; - } else if (abruptType === "continue") { - abruptType = "return"; - continue; - } - } - finally { - yield 3; - if (finallyAbruptType === "return") { - return 4; - } else if (finallyAbruptType === "break") { - break; - } else if (finallyAbruptType === "continue") { - finallyAbruptType = null; - continue; - } - } - } - return 5; - } - - it("should honor return", function() { - check(usingAbrupt("return", null), [0, 1, 3], 2); - }); - - it("should honor break", function() { - check(usingAbrupt("break", null), [0, 1, 3], 5); - }); - - it("should honor continue", function() { - check(usingAbrupt("continue", null), [0, 1, 3, 1, 3], 2); - }); - - it("should override abrupt with return", function() { - check(usingAbrupt("return", "return"), [0, 1, 3], 4); - check(usingAbrupt("break", "return"), [0, 1, 3], 4); - check(usingAbrupt("continue", "return"), [0, 1, 3], 4); - }); - - it("should override abrupt with break", function() { - check(usingAbrupt("return", "break"), [0, 1, 3], 5); - check(usingAbrupt("break", "break"), [0, 1, 3], 5); - check(usingAbrupt("continue", "break"), [0, 1, 3], 5); - }); - - it("should override abrupt with continue", function() { - check(usingAbrupt("return", "continue"), [0, 1, 3, 1, 3], 2); - check(usingAbrupt("break", "continue"), [0, 1, 3, 1, 3], 5); - check(usingAbrupt("continue", "continue"), [0, 1, 3, 1, 3], 2); - }); - - it("should execute finally blocks statically", function() { - check(usingThrow(true), [0, 1, 4], 5); - check(usingThrow(false), [0, 1, 6], 7); - }); - - it("should execute finally blocks dynamically", function() { - check(usingRaise(true), [0, 1, 4], 5); - check(usingRaise(false), [0, 1, 6], 7); - }); - - it("should execute finally blocks before throwing", function() { - var uncaughtError = new Error("uncaught"); - - function *uncaught(condition) { - try { - yield 0; - if (condition) { - yield 1; - raise(uncaughtError); - } - yield 2; - } finally { - yield 3; - } - yield 4; - } - - check(uncaught(false), [0, 2, 3, 4]); - - var u = uncaught(true); - - assert.deepEqual(u.next(), { value: 0, done: false }); - assert.deepEqual(u.next(), { value: 1, done: false }); - assert.deepEqual(u.next(), { value: 3, done: false }); - - try { - u.next(); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, uncaughtError); - } - }); - - it("should throw correct error when finally contains catch", function() { - var right = new Error("right"); - var wrong = new Error("wrong"); - - function *gen() { - try { - yield 0; - raise(right); - } finally { - yield 1; - try { - raise(wrong); - } catch (err) { - assert.strictEqual(err, wrong); - yield 2; - } - } - } - - var g = gen(); - - assert.deepEqual(g.next(), { - value: 0, - done: false - }); - - assert.deepEqual(g.next(), { - value: 1, - done: false - }); - - assert.deepEqual(g.next(), { - value: 2, - done: false - }); - - try { - g.next(); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, right); - } - }); - - it("should run finally after break within try", function() { - function *gen() { - try { - yield 0; - while (true) { - yield 1; - break; - } - } finally { - yield 2; - } - yield 3; - } - - check(gen(), [0, 1, 2, 3]); - }); -}); - -describe("try-catch-finally generator", function() { - function *usingThrow() { - yield 0; - try { - try { - yield 1; - throw 2; - yield 3; - } catch (x) { - throw yield x; - } finally { - yield 5; - } - } catch (thrown) { - yield thrown; - } - yield 6; - } - - function *usingRaise() { - yield 0; - try { - try { - yield 1; - raise(2); - yield 3; - } catch (x) { - throw yield x; - } finally { - yield 5; - } - } catch (thrown) { - yield thrown; - } - yield 6; - } - - it("should statically catch and then finalize", function() { - check(usingThrow(), [0, 1, 2, 5, 3, 6]); - }); - - it("should dynamically catch and then finalize", function() { - check(usingRaise(), [0, 1, 2, 5, 3, 6]); - }); - - it("should execute catch and finally blocks at most once", function() { - var error = new Error(); - - function *gen() { - try { - switch (1) { - case 1: - yield "a"; - break; - default: - break; - } - throw error; - } catch (e) { - assert.strictEqual(e, error); - yield "b"; - do { - do { - yield "c"; - break; - } while (false); - yield "d"; - break; - } while (false); - yield "e"; - } finally { - yield "f"; - } - } - - check(gen(), ["a", "b", "c", "d", "e", "f"]); - }); - - it("should handle backwards jumps in labeled loops", function() { - function *gen() { - var firstTime = true; - outer: - while (true) { - yield 0; - try { - while (true) { - yield 1; - if (firstTime) { - firstTime = false; - yield 2; - continue outer; - } else { - yield 3; - break; - } - } - yield 4; - break; - } finally { - yield 5; - } - yield 6; - } - yield 7; - } - - check(gen(), [0, 1, 2, 5, 0, 1, 3, 4, 5, 7]); - }); - - it("should handle loop continue statements properly", function() { - var error = new Error("thrown"); - var markers = []; - - function *gen() { - var c = 2; - while (c > 0) { - try { - markers.push("try"); - yield c; - } catch (e) { - assert.strictEqual(e, error); - markers.push("catch"); - continue; - } finally { - markers.push("finally"); - } - markers.push("decrement"); - --c; - } - } - - var g = gen(); - - assert.deepEqual(g.next(), { value: 2, done: false }); - assert.deepEqual(g.throw(error), { value: 2, done: false }); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - - assert.deepEqual(markers, [ - "try", - "catch", - "finally", - "try", - "finally", - "decrement", - "try", - "finally", - "decrement" - ]); - }); -}); - -describe("dynamic exception", function() { - function *gen(x, fname) { - try { - return fns[fname](x); - } catch (thrown) { - yield thrown; - } - } - - var fns = { - f: function(x) { - throw x; - }, - - g: function(x) { - return x; - } - }; - - it("should be dispatched correctly", function() { - check(gen("asdf", "f"), ["asdf"]); - check(gen("asdf", "g"), [], "asdf"); - }); -}); - -describe("nested finally blocks", function() { - function *usingThrow() { - try { - try { - try { - throw "thrown"; - } finally { - yield 1; - } - } catch (thrown) { - yield thrown; - } finally { - yield 2; - } - } finally { - yield 3; - } - } - - function *usingRaise() { - try { - try { - try { - raise("thrown"); - } finally { - yield 1; - } - } catch (thrown) { - yield thrown; - } finally { - yield 2; - } - } finally { - yield 3; - } - } - - it("should statically execute in order", function() { - check(usingThrow(), [1, "thrown", 2, 3]); - }); - - it("should dynamically execute in order", function() { - check(usingRaise(), [1, "thrown", 2, 3]); - }); -}); - -describe("for-in loop generator", function() { - it("should handle the simple case", function() { - function *gen() { - var count = 0; - var obj = {foo: 1, bar: 2}; - for (var key in obj) { - assert(obj.hasOwnProperty(key), key + " must be own property"); - yield [key, obj[key]]; - count += 1; - } - return count; - } - - check(gen(), [["foo", 1], ["bar", 2]], 2); - }); - - it("should handle break in loop", function() { - function *gen(obj) { - var count = 0; - for (var key in (yield "why not", obj)) { - if (obj.hasOwnProperty(key)) { - if (key === "skip") { - break; - } - count += 1; - yield [key, obj[key]]; - } - } - return count; - } - - check( - gen({ a: 1, b: 2, skip: 3, c: 4 }), - ["why not", ["a", 1], ["b", 2]], - 2 - ); - }); - - it("should handle property deletion in loop", function() { - function *gen() { - var count = 0; - var obj = {foo: 1, bar: 2}; - for (var key in obj) { - assert(obj.hasOwnProperty(key), key + " must be own property"); - yield [key, obj[key]]; - delete obj.bar; - count += 1; - } - return count; - } - - check(gen(), [["foo", 1]], 1); - }); - - it("should loop over inherited properties", function() { - function *gen() { - var count = 0; - function Foo() { - this.baz = 1 - } - Foo.prototype.bar = 2; - - var foo = new Foo(); - for (var key in foo) { - yield [key, foo[key]]; - count += 1; - } - return count; - } - - check(gen(), [["baz", 1], ["bar", 2]], 2); - }); - - it("should handle risky object expressions", function() { - function a(sent) { - assert.strictEqual(sent, 1); - a.called = true; - } - - function b(sent) { - assert.strictEqual(sent, 2); - b.called = true; - return { callee: b }; - } - - function *gen() { - assert.ok(!a.called); - assert.ok(!b.called); - for (var key in a(yield 0), b(yield 1)) { - assert.ok(a.called); - assert.ok(b.called); - assert.strictEqual(yield key, 3); - } - - for (var key in a(1), { foo: "foo", bar: "bar" }) { - yield key; - } - } - - check(gen(), [0, 1, "callee", "foo", "bar"]); - }); - - it("should allow non-Identifier left-hand expressions", function() { - var obj = {}; - var baz = { a: 1, b: 2, c: 3 }; - var markers = []; - - function foo() { - markers.push("called foo"); - return obj; - } - - function *gen() { - for (foo().bar in baz) { - markers.push(obj.bar); - yield obj.bar; - } - } - - check(gen(), ["a", "b", "c"]); - - assert.deepEqual(markers, [ - "called foo", - "a", - "called foo", - "b", - "called foo", - "c" - ]); - }); -}); - -describe("yield chain", function() { - function *gen(n) { - return yield yield yield yield n; - } - - it("should have correct associativity", function() { - check(gen(5), [5, 1, 2, 3], 4); - check(gen("asdf"), ["asdf", 1, 2, 3], 4); - }); -}); - -describe("object literal generator", function() { - function *gen(a, b) { - yield { - a: a - (yield a), - b: yield b - }; - } - - it("should yield the correct object", function() { - check(gen(1, 2), [1, 2, { a: 0, b: 2 }]); - check(gen(4, 2), [4, 2, { a: 3, b: 2 }]); - }); -}); - -describe("switch statement generator", function() { - function *gen(a) { - switch (yield a) { - case (yield "x") - a: - return "first case"; - case (yield "y") - a: - return "second case"; - } - } - - it("should jump to the correct cases", function() { - check(gen(1), [1, "x"], "first case"); - check(gen(2), [2, "x", "y"], "second case"); - }); -}); - -describe("infinite sequence generator", function() { - function *gen(start, step) { - step = step || 1; - while (true) { - yield start; - start += step; - } - } - - function *limit(g, stop) { - while (true) { - var info = g.next(); - if (info.done) { - return; - } else if (info.value < stop) { - yield info.value; - } else { - return; - } - } - } - - it("should generate a lot of plausible values", function() { - var g = gen(10, 2); - - assert.deepEqual(g.next(), { value: 10, done: false }); - assert.deepEqual(g.next(), { value: 12, done: false }); - assert.deepEqual(g.next(), { value: 14, done: false }); - assert.deepEqual(g.next(), { value: 16, done: false }); - - var sum = 10 + 12 + 14 + 16; - - for (var n = 0; n < 1000; ++n) { - var info = g.next(); - sum += info.value; - assert.strictEqual(info.done, false); - } - - assert.strictEqual(sum, 1017052); - }); - - it("should allow limiting", function() { - check(limit(gen(10, 3), 20), [10, 13, 16, 19]); - }); -}); - -describe("generator function expression", function() { - it("should behave just like a declared generator", function() { - check(function *(x, y) { - yield x; - yield y; - yield x + y; - return x * y; - }(3, 7), [3, 7, 10], 21); - }) -}); - -describe("generator reentry attempt", function() { - function *gen(x) { - try { - (yield x).next(x); - } catch (err) { - yield err; - } - return x + 1; - } - - it("should complain with a TypeError", function() { - var g = gen(3); - assert.deepEqual(g.next(), { value: 3, done: false }); - var complaint = g.next(g); // Sending the generator to itself. - assert.ok(complaint.value instanceof Error); - assert.strictEqual( - complaint.value.message, - "Generator is already running" - ); - assert.deepEqual(g.next(), { value: 4, done: true }); - }); -}); - -describe("completed generator", function() { - function *gen() { - return "ALL DONE"; - } - - (runningInTranslation ? it : xit) - ("should refuse to resume", function() { - var g = gen(); - - assert.deepEqual(g.next(), { - value: "ALL DONE", done: true - }); - - assertAlreadyFinished(g); - }); -}); - -describe("delegated yield", function() { - it("should delegate correctly", function() { - function *gen(condition) { - yield 0; - if (condition) { - yield 1; - yield* gen(false); - yield 2; - } - yield 3; - } - - check(gen(true), [0, 1, 0, 3, 2, 3]); - check(gen(false), [0, 3]); - }); - - it("should cope with empty delegatees", function() { - function *gen(condition) { - if (condition) { - yield 0; - yield* gen(false); - yield 1; - } - } - - check(gen(true), [0, 1]); - check(gen(false), []); - }); - - it("should support deeper nesting", function() { - function *outer(n) { - yield n; - yield* middle(n - 1, inner(n + 10)); - yield n + 1; - } - - function *middle(n, plusTen) { - yield n; - yield* inner(n - 1); - yield n + 1; - yield* plusTen; - } - - function *inner(n) { - yield n; - } - - check(outer(5), [5, 4, 3, 5, 15, 6]); - }); - - it("should pass sent values through", function() { - function *outer(n) { - yield* inner(n << 1); - yield "zxcv"; - } - - function *inner(n) { - return yield yield yield n; - } - - var g = outer(3); - assert.deepEqual(g.next(), { value: 6, done: false }); - assert.deepEqual(g.next(1), { value: 1, done: false }); - assert.deepEqual(g.next(2), { value: 2, done: false }); - assert.deepEqual(g.next(4), { value: "zxcv", done: false }); - assert.deepEqual(g.next(5), { value: void 0, done: true }); - }); - - it("should be governed by enclosing try statements", function() { - var error = new Error("thrown"); - - function *outer(n) { - try { - yield 0; - yield* inner(n); - yield 1; - } catch (err) { - yield err.message; - } - yield 4; - } - - function *inner(n) { - while (n --> 0) { - try { - if (n === 3) { - raise(error); - } - } finally { - yield n; - } - } - } - - check(outer(3), [0, 2, 1, 0, 1, 4]); - check(outer(5), [0, 4, 3, "thrown", 4]); - }); - - it("should dispatch .thrown exceptions correctly", function() { - var count = 0; - - function *gen() { - yield* inner(); - try { - yield* inner(); - } catch (err) { - // pass - } - return yield* inner(); - } - - function *inner() { - return yield count++; - } - - var g = gen(); - - assert.deepEqual(g.next(), { - value: 0, - done: false - }); - - assert.deepEqual(g.next(), { - value: 1, - done: false - }); - - assert.deepEqual(g.throw(new Error("lol")), { - value: 2, - done: false, - }); - - assert.deepEqual(g.next("sent"), { - value: "sent", - done: true - }); - }); - - it("should call .return methods of delegate iterators", function() { - var throwee = new Error("argument to gen.throw"); - var thrownFromThrow = new Error("thrown from throw method"); - var thrownFromReturn = new Error("thrown from return method"); - - function *gen(delegate) { - try { - return yield* delegate; - } catch (err) { - return err; - } - } - - function check(throwMethod, returnMethod) { - var throwCalled = false; - var returnCalled = false; - var count = 0; - var iterator = { - next: function() { - return { value: count++, done: false }; - } - }; - - iterator[iteratorSymbol] = function() { - return this; - }; - - if (throwMethod) { - iterator["throw"] = function() { - throwCalled = true; - return throwMethod.apply(this, arguments); - }; - } - - if (returnMethod) { - iterator["return"] = function() { - returnCalled = true; - return returnMethod.apply(this, arguments); - }; - } - - var g = gen(iterator); - - assert.deepEqual(g.next(), { value: 0, done: false }); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 2, done: false }); - assert.deepEqual(g.next(), { value: 3, done: false }); - - assert.strictEqual(throwCalled, false); - assert.strictEqual(returnCalled, false); - - var result = {}; - - result.throwResult = g.throw(throwee); - result.throwCalled = throwCalled; - result.returnCalled = returnCalled; - - return result; - } - - var checkResult = check(undefined, function() { - throw thrownFromReturn; - }); - if (runningInTranslation) { - // BUG: Node v0.11 and v0.12 neglect to call .return here. - assert.strictEqual(checkResult.throwResult.value, thrownFromReturn); - } else { - // This is the TypeError that results from trying to call the - // undefined .throw method of the iterator. - assert.ok(checkResult.throwResult.value instanceof TypeError); - } - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, false); - // BUG: Node v0.11 and v0.12 neglect to call .return here. - assert.strictEqual(checkResult.returnCalled, runningInTranslation); - - checkResult = check(undefined, function() { - return { value: "from return", done: true }; - }); - assert.notStrictEqual(checkResult.throwResult.value, throwee); - // This is the TypeError that results from trying to call the - // undefined .throw method of the iterator. - assert.ok(checkResult.throwResult.value instanceof TypeError); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, false); - // BUG: Node v0.11 and v0.12 neglect to call .return here. - assert.strictEqual(checkResult.returnCalled, runningInTranslation); - - var checkResult = check(function(thrown) { - return { value: "from throw", done: true }; - }, function() { - throw thrownFromReturn; - }); - assert.strictEqual(checkResult.throwResult.value, "from throw"); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, true); - assert.strictEqual(checkResult.returnCalled, false); - - var checkResult = check(function(thrown) { - throw thrownFromThrow; - }, function() { - throw thrownFromReturn; - }); - assert.strictEqual(checkResult.throwResult.value, thrownFromThrow); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, true); - assert.strictEqual(checkResult.returnCalled, false); - - var checkResult = check(undefined, undefined); - assert.notStrictEqual(checkResult.throwResult.value, throwee); - // This is the TypeError that results from trying to call the - // undefined .throw method of the iterator. - assert.ok(checkResult.throwResult.value instanceof TypeError); - assert.strictEqual(checkResult.throwResult.done, true); - assert.strictEqual(checkResult.throwCalled, false); - assert.strictEqual(checkResult.returnCalled, false); - }); - - it("should not be required to have a .return method", function() { - function *gen(delegate) { - return yield* delegate; - } - - var inner = range(5); - var iterator = { next: inner.next.bind(inner) }; - iterator[iteratorSymbol] = function() { - return this; - }; - - var g = gen(iterator); - assert.deepEqual(g.next(), { value: 0, done: false }); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 2, done: false }); - - if (typeof g.return === "function") { - assert.deepEqual(g.return(-1), { value: -1, done: true }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - } - }); - - (runningInTranslation ? it : xit) - ("should support any iterable argument", function() { - function *gen() { - yield 0; - yield* [ - yield "one", - yield "two", - yield "three" - ]; - yield 5; - } - - check(gen(), [0, "one", "two", "three", 2, 3, 4, 5]); - - function *string() { - return yield* "asdf"; - } - - check(string(), ["a", "s", "d", "f"]); - }); - - it("should evaluate to the return value of the delegate", function() { - function *inner() { - yield 1; - return 2; - } - - function *outer(delegate) { - return yield* delegate; - } - - check(outer(inner()), [1], 2); - - var arrayDelegate = [3, 4]; - if (!runningInTranslation) { - // Node v0.11 doesn't know how to turn arrays into iterators over - // their elements without a little help. - arrayDelegate = regeneratorRuntime.values(arrayDelegate); - } - check(outer(arrayDelegate), [3, 4], void 0); // See issue #143. - - if (!runningInTranslation) { - return; - } - - check(outer({ - next: function() { - return { value: "oyez", done: true }; - } - }), [], "oyez"); - }); - - it("should work as a subexpression", function() { - function *inner(arg) { - return arg; - } - - function *gen(delegate) { - // Unfortunately these parentheses appear to be necessary. - return 1 + (yield* delegate); - } - - check(gen(inner(2)), [], 3); - check(gen(inner(3)), [], 4); - - if (!runningInTranslation) { - return; - } - - check(gen({ - next: function() { - return { value: "foo", done: true }; - } - }), [], "1foo"); - }); -}); - -describe("function declaration hoisting", function() { - it("should work even if the declarations are out of order", function() { - function *gen(n) { - yield increment(n); - - function increment(x) { - return x + 1; - } - - if (n % 2) { - yield halve(decrement(n)); - - function halve(x) { - return x >> 1; - } - - function decrement(x) { - return x - 1; - } - } else { - // The behavior of function declarations nested inside conditional - // blocks is notoriously underspecified, and in V8 it appears the - // halve function is still defined when we take this branch, so - // "undefine" it for consistency with regenerator semantics. - halve = void 0; - } - - yield typeof halve; - - yield increment(increment(n)); - } - - check(gen(3), [4, 1, "function", 5]); - check(gen(4), [5, "undefined", 6]); - }); - - it("should work for nested generator function declarations", function() { - function *outer(n) { - yield 0; - assert.ok(regeneratorRuntime.isGeneratorFunction(inner)); - return yield* inner(n); - - // Note that this function declaration comes after everything else - // in the outer function, but needs to be fully available above. - function *inner(n) { - yield n - 1; - yield n; - return yield n + 1; - } - } - - check(outer(2), [0, 1, 2, 3], 4); - }); - - it("should not interfere with function rebinding", function() { - function rebindTo(value) { - var oldValue = toBeRebound; - toBeRebound = value; - return oldValue; - } - - function *toBeRebound() { - var originalValue = toBeRebound; - yield toBeRebound; - assert.strictEqual(rebindTo(42), originalValue); - yield toBeRebound; - assert.strictEqual(rebindTo("asdf"), 42); - yield toBeRebound; - } - - var original = toBeRebound; - check(toBeRebound(), [original, 42, "asdf"]); - - function attemptToRebind(value) { - var oldValue = safe; - safe = value; - return oldValue; - } - - var safe = function *safe() { - var originalValue = safe; - yield safe; - assert.strictEqual(attemptToRebind(42), originalValue); - yield safe; - assert.strictEqual(attemptToRebind("asdf"), 42); - yield safe; - } - - original = safe; - check(safe(), [safe, safe, safe]); - }); -}); - -describe("the arguments object", function() { - it("should work in simple variadic functions", function() { - function *sum() { - var result = 0; - - for (var i = 0; i < arguments.length; ++i) { - yield result += arguments[i]; - } - - return result; - } - - check(sum(1, 2, 3), [1, 3, 6], 6); - check(sum(9, -5, 3, 0, 2), [9, 4, 7, 7, 9], 9); - }); - - it("should alias function parameters", function() { - function *gen(x, y) { - yield x; - ++arguments[0]; - yield x; - - yield y; - --arguments[1]; - yield y; - - var temp = y; - y = x; - x = temp; - - yield x; - yield y; - } - - check(gen(3, 7), [3, 4, 7, 6, 6, 4]); - check(gen(10, -5), [10, 11, -5, -6, -6, 11]); - }); - - it("should be shadowable by explicit declarations", function() { - function *asParameter(x, arguments) { - arguments = arguments + 1; - yield x + arguments; - } - - check(asParameter(4, 5), [10]); - check(asParameter("asdf", "zxcv"), ["asdfzxcv1"]); - - function *asVariable(x) { - // TODO References to arguments before the variable declaration - // seem to see the object instead of the undefined value. - var arguments = x + 1; - yield arguments; - } - - check(asVariable(4), [5]); - check(asVariable("asdf"), ["asdf1"]); - }); - - it("should not get confused by properties", function() { - function *gen(args) { - var obj = { arguments: args }; - yield obj.arguments; - obj.arguments = "oyez"; - yield obj; - } - - check(gen(42), [42, { arguments: "oyez" }]); - }); - - it("supports .callee", function() { - function *gen(doYield) { - yield 1; - if (doYield) { - yield 2; - } else { - yield 3 - yield* arguments.callee(true); - yield 4 - } - yield 5; - } - - check(gen(false), [1, 3, 1, 2, 5, 4, 5]); - }); -}); - -describe("catch parameter shadowing", function() { - it("should leave outer variables unmodified", function() { - function *gen(x) { - var y = x + 1; - try { - throw x + 2; - } catch (x) { - yield x; - x += 1; - yield x; - } - yield x; - try { - throw x + 3; - } catch (y) { - yield y; - y *= 2; - yield y; - } - yield y; - } - - check(gen(1), [3, 4, 1, 4, 8, 2]); - check(gen(2), [4, 5, 2, 5, 10, 3]); - }); - - it("should not replace variables defined in inner scopes", function() { - function *gen(x) { - try { - throw x; - } catch (x) { - yield x; - - yield (function(x) { - return x += 1; - }(x + 1)); - - yield (function() { - var x = arguments[0]; - return x * 2; - }(x + 2)); - - yield (function() { - function notCalled(x) { - throw x; - } - - x >>= 1; - return x; - }()); - - yield x -= 1; - } - - yield x; - } - - check(gen(10), [10, 12, 24, 5, 4, 10]); - check(gen(11), [11, 13, 26, 5, 4, 11]); - }); - - it("should allow nested catch parameters of the same name", function() { - function *gen() { - try { - raise("e1"); - } catch (e) { - yield e; - try { - raise("e2"); - } catch (e) { - yield e; - } - yield e; - } - } - - check(gen(), ["e1", "e2", "e1"]); - }); - - it("should not interfere with non-referential identifiers", function() { - function *gen() { - try { - yield 1; - raise(new Error("oyez")); - yield 2; - } catch (e) { - yield 3; - e.e = "e.e"; - e[e.message] = "e.oyez"; - return { - e: e, - identity: function(x) { - var e = x; - return e; - } - }; - } - yield 4; - } - - var g = gen(); - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 3, done: false }); - - var info = g.next(); - assert.strictEqual(info.done, true); - assert.strictEqual(info.value.e.message, "oyez"); - assert.strictEqual(info.value.e.e, "e.e"); - assert.strictEqual(info.value.e.oyez, "e.oyez"); - assert.strictEqual(info.value.identity("same"), "same"); - }); -}); - -describe("empty while loops", function() { - it("should be preserved in generated code", function() { - function *gen(x) { - while (x) { - // empty while loop - } - - do { - // empty do-while loop - } while (x); - - return gen.toString(); - } - - var info = gen(false).next(); - assert.strictEqual(info.done, true); - assert.ok(/empty while loop/.test(info.value)); - assert.ok(/empty do-while loop/.test(info.value)); - }); -}); - -describe("object literals with multiple yields", function() { - it("should receive different sent values", function() { - function *gen(fn) { - return { - a: yield "a", - b: yield "b", - c: fn(yield "c", yield "d"), - d: [yield "e", yield "f"] - }; - } - - check(gen(function sum(x, y) { - return x + y; - }), ["a", "b", "c", "d", "e", "f"], { - a: 1, - b: 2, - c: 3 + 4, - d: [5, 6] - }); - }); -}); - -describe("generator .throw method", function() { - (runningInTranslation ? it : xit)("should complete generator", function() { - function *gen(x) { - yield 2; - throw 1; - } - - var u = gen(); - - u.next(); - - try { - u.throw(2); - } catch (err) { - assert.strictEqual(err, 2); - } - - assertAlreadyFinished(u); - }); - - it("should work after the final call to .next", function() { - function *gen() { - yield 1; - } - - var g = gen(); - assert.deepEqual(g.next(), { value: 1, done: false }); - - var exception = new Error("unhandled exception"); - try { - g.throw(exception); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, exception); - } - }); - - it("should immediately complete a new-born generator", function() { - var began = false; - - function *gen() { - began = true; - yield 1; - } - - var g = gen(); - var exception = new Error("unhandled exception"); - try { - g.throw(exception); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, exception); - assert.strictEqual(began, false); - } - }); - - it("should not propagate errors handled inside a delegate", function() { - function *outer() { - try { - yield* inner(); - } catch (err) { - return -1; - } - return 1; - } - - function *inner() { - try { - yield void 0; - } catch (e) { - return; - } - } - - var g = outer(); - g.next(); - assert.equal(g.throw(new Error('foo')).value, 1); - }); - - it("should propagate errors unhandled inside a delegate", function() { - function *outer() { - try { - yield* inner(); - } catch (err) { - return -1; - } - return 1; - } - - function *inner() { - yield void 0; - } - - var g = outer(); - g.next(); - assert.equal(g.throw(new Error('foo')).value, -1); - }); -}); - -describe("unqualified function calls", function() { - it("should have a global `this` object", function() { - function getThis() { - return this; - } - - // This is almost certainly the global object, but there's a chance it - // might be null or undefined (in strict mode). - var unqualifiedThis = getThis(); - - function *invoke() { - // It seems like a bug in the ES6 spec that we have to yield an - // argument instead of just calling (yield)(). - return (yield "dummy")(); - } - - var g = invoke(); - var info = g.next(); - - assert.deepEqual(info, { value: "dummy", done: false }); - - info = g.next(getThis); - - // Avoid using assert.strictEqual when the arguments might equal the - // global object, since JSON.stringify chokes on circular structures. - assert.ok(info.value === unqualifiedThis); - - assert.strictEqual(info.done, true); - }); -}); - -describe("yield* expression results", function () { - it("have correct values", function () { - function* foo() { - yield 0; - return yield* bar(); - } - - function* bar() { - yield 1; - return 2; - } - - check(foo(), [0, 1], 2); - }); - - it("can be used in complex expressions", function () { - function pumpNumber(gen) { - var n = 0; - - while (true) { - var res = n > 0 ? gen.next(n) : gen.next(); - n = res.value; - if (res.done) { - return n; - } - } - } - - function* foo() { - return (yield* bar()) + (yield* bar()); - } - - function* bar() { - return (yield 2) + (yield 3); - } - - assert.strictEqual(pumpNumber(bar()), 5); - assert.strictEqual(pumpNumber(foo()), 10); - }); -}); - -describe("isGeneratorFunction", function() { - it("should work for function declarations", function() { - // Do the assertions up here to make sure the generator function is - // marked at the beginning of the block the function is declared in. - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(genFun), - true - ); - - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(normalFun), - false - ); - - function normalFun() { - return 0; - } - - function *genFun() { - yield 0; - } - }); - - it("should work for function expressions", function() { - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(function *genFun() { - yield 0; - }), - true - ); - - assert.strictEqual( - regeneratorRuntime.isGeneratorFunction(function normalFun() { - return 0; - }), - false - ); - }); -}); - -describe("new expressions", function() { - it("should be able to contain yield sub-expressions", function() { - function A(first, second) { - this.first = first; - this.second = second; - } - - function *gen() { - return yield new (yield 0)(yield 1, yield 2); - } - - var g = gen(); - - assert.deepEqual(g.next(), { value: 0, done: false }); - assert.deepEqual(g.next(A), { value: 1, done: false }); - assert.deepEqual(g.next("asdf"), { value: 2, done: false }); - - var info = g.next("zxcv"); - assert.strictEqual(info.done, false); - assert.ok(info.value instanceof A); - assert.strictEqual(info.value.first, "asdf"); - assert.strictEqual(info.value.second, "zxcv"); - - assert.deepEqual(g.next("qwer"), { value: "qwer", done: true }); - }); -}); - -describe("block binding", function() { - it("should translate block binding correctly", function() { - "use strict"; - - function *gen() { - var a$0 = 0, a$1 = 1; - - let a = 3; - - { - let a = 1; - yield a + a$0; - } - - { - let a = 2; - yield a - 1 + a$1; - } - - yield a; - } - - var g = gen(); - - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: 2, done: false }); - assert.deepEqual(g.next(), { value: 3, done: false }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - }); - - it("should translate block binding with iife correctly", function() { - "use strict"; - - function *gen() { - let arr = []; - - for (let x = 0; x < 3; x++) { - let y = x; - arr.push(function() { return y; }); - } - - { - let x; - while( x = arr.pop() ) { - yield x; - } - } - } - - var g = gen(); - - assert.equal(g.next().value(), 2); - assert.equal(g.next().value(), 1); - assert.equal(g.next().value(), 0); - assert.deepEqual(g.next(), { value: void 0, done: true }); - }); -}); - -describe("newborn generators", function() { - it("should be able to yield* non-newborn generators", function() { - function *inner() { - return [yield 1, yield 2]; - } - - function *outer(delegate) { - return yield* delegate; - } - - var n = inner(); - - assert.deepEqual(n.next(), { - value: 1, - done: false - }); - - var g = outer(n); - - // I would really like to be able to pass 3 to g.next here, but V8 - // ignores values sent to newborn generators, and SpiderMonkey throws - // a TypeError. - assert.deepEqual(g.next(), { - value: 2, - done: false - }); - - assert.deepEqual(g.next(4), { - value: [void 0, 4], - done: true - }); - }); - - it("should support the ignore-initial-yield wrapper idiom", function() { - var markers = []; - - function *inner() { - markers.push(0); - var sent1 = yield 1; - markers.push(2); - var sent2 = yield 2; - markers.push(3); - return [sent1, sent2]; - } - - function wrapper(delegate) { - var gen = (function*() { - // This yield is the "initial yield" whose argument we ignore. - var sent = yield "ignored", info; - - markers.push(1); - - while (!(info = delegate.next(sent)).done) { - sent = yield info.value; - } - - markers.push(4); - - return info.value; - })(); - - // Ensure that gen is not newborn and that the next invocation of - // gen.next(value) can send value to the initial yield expression. - gen.next(); - - return gen; - } - - var n = inner(); - - assert.deepEqual(n.next(), { - value: 1, - done: false - }); - - var g = wrapper(n); - - // Unlike in the previous spec, it's fine to pass 3 to g.next here, - // because g is not newborn, because g.next was already called once - // before g was returned from the wrapper function. - assert.deepEqual(g.next(3), { - value: 2, - done: false - }); - - assert.deepEqual(g.next(4), { - value: [3, 4], - done: true - }); - - // Ensure we encountered the marker points in the expected order. - assert.deepEqual(markers, [0, 1, 2, 3, 4]); - }); - - it("should allow chaining newborn and non-newborn generators", function() { - function *range(n) { - for (var i = 0; i < n; ++i) { - yield i; - } - } - - function *chain(a, b) { - yield* a; - yield* b; - } - - check(chain(range(3), range(5)), [0, 1, 2, 0, 1, 2, 3, 4]); - - function *y3(x) { - return yield yield yield x; - } - - function *y5(x) { - return yield yield yield yield yield x; - } - - check( - chain(y3("foo"), y5("bar")), - ["foo", 1, 2, "bar", 4, 5, 6, 7] - ); - - var g3 = y3("three"); - assert.deepEqual(g3.next(), { - value: "three", - done: false - }); - - var g5 = y5("five"); - assert.deepEqual(g5.next(), { - value: "five", - done: false - }); - - var undef; // A little easier to read than void 0. - check(chain(g3, g5), [undef, 1, undef, 3, 4, 5]); - }); -}); - -describe("labeled break and continue statements", function() { - it("should be able to exit multiple try statements", function() { - var e1 = "first"; - var e2 = "second"; - var e3 = "third"; - var e4 = "fourth"; - - function *gen(n, which) { - try { - yield 0; - raise(e1); - - } finally { - yield 1; - - loop: - for (var i = 0; i < n; ++i) { - yield i; - - try { - raise(e2); - } finally { - yield 2; - - try { - raise(e3); - } finally { - yield 3; - - try { - raise(e4); - } finally { - yield 4; - - if (which === "break") { - yield "breaking"; - break loop; - } - - if (which === "continue") { - yield "continuing"; - continue loop; - } - - yield 5; - } - } - } - } - - yield 6; - } - } - - try { - check(gen(1, "break"), [ - 0, 1, 0, 2, 3, 4, "breaking", 6 - ]); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, e1); - } - - try { - check(gen(3, "continue"), [ - 0, 1, 0, 2, 3, 4, "continuing", - 1, 2, 3, 4, "continuing", - 2, 2, 3, 4, "continuing", - 6 // Loop finished naturally. - ]); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, e1); - } - - try { - check(gen(3, "neither"), [ - 0, 1, 0, 2, 3, 4, 5 - ]); - assert.ok(false, "should have thrown an exception"); - } catch (err) { - assert.strictEqual(err, e4); - } - }); - - it("should allow breaking from any labeled statement", function() { - function* gen(limit) { - yield 0; - - for (var i = 0; i < limit; ++i) { - yield 1; - - label1: { - yield 2; - break label1; - yield 3; - } - - label2: - if (limit === 3) label3: { - yield 4; - if (i === 0) break label2; - yield 5; - if (i === 1) break label3; - label4: yield 6; - // This should break from the for-loop. - if (i === 2) xxx: break; - yield 7; - } - - // This should be a no-op. - xxx: break xxx; - - yield 8 - } - - yield 9; - } - - check(gen(0), [0, 9]); - check(gen(1), [0, 1, 2, 8, 9]); - check(gen(2), [0, 1, 2, 8, 1, 2, 8, 9]); - check(gen(3), [0, 1, 2, 4, 8, 1, 2, 4, 5, 8, 1, 2, 4, 5, 6, 9]); - }); -}); - -describe("for loop with var decl and no update expression", function() { - // https://github.com/facebook/regenerator/issues/103 - function *range() { - for (var i = 0; false; ) { - } - } - - it("should compile and run", function() { - check(range(), []); - }); -}); - -describe("generator function prototype", function() { - function getProto(obj) { - return Object.getPrototypeOf - ? Object.getPrototypeOf(obj) - : obj.__proto__; - } - - it("should follow the expected object model", function() { - var GeneratorFunctionPrototype = getProto(f); - var GeneratorFunction = GeneratorFunctionPrototype.constructor; - - assert.strictEqual(GeneratorFunction.name, 'GeneratorFunction'); - assert.strictEqual(GeneratorFunction.prototype, - GeneratorFunctionPrototype); - assert.strictEqual(GeneratorFunctionPrototype.prototype.constructor, - GeneratorFunctionPrototype); - assert.strictEqual(GeneratorFunctionPrototype.prototype, - getProto(f.prototype)); - assert.strictEqual(getProto(GeneratorFunctionPrototype), - Function.prototype); - - if (typeof process === "undefined" || - process.version.slice(1, 3) === "0.") { - // Node version strings start with 0. - assert.strictEqual(GeneratorFunctionPrototype.name, - "GeneratorFunctionPrototype"); - } else if (process.version.slice(1, 3) === "1.") { - // iojs version strings start with 1., and iojs gets this .name - // property wrong. TODO report this? - assert.strictEqual(GeneratorFunctionPrototype.name, ""); - } - - assert.strictEqual(typeof f2, "function"); - assert.strictEqual(f2.constructor, GeneratorFunction); - assert.ok(f2 instanceof GeneratorFunction); - assert.strictEqual(f2.name, "f2"); - - var g = f(); - assert.ok(g instanceof f); - assert.strictEqual(getProto(g), f.prototype); - - assert.deepEqual([], Object.getOwnPropertyNames(f.prototype)); - // assert.deepEqual([], Object.getOwnPropertyNames(g)); - - f.prototype.x = 42; - - var g2 = f(); - assert.strictEqual(g2.x, 42); - - var g3 = new f(); - assert.strictEqual(g3.x, 42); - - function* f2() { - yield 1; - } - - assert.strictEqual(getProto(f), getProto(f2)); - assert.strictEqual(f.hasOwnProperty('constructor'), false); - assert.strictEqual(getProto(f).constructor.name, 'GeneratorFunction'); - - // Intentionally at the end to test hoisting. - function* f() { - yield this; - } - - function* f() { - yield 1; - } - - var f2 = f; - f = 42; - var g = f2(); - - assert.deepEqual(g.next(), { value: 1, done: false }); - assert.deepEqual(g.next(), { value: void 0, done: true }); - assert.ok(g instanceof f2); - }); -}); - -describe("for-of loops", function() { - (runningInTranslation ? it : xit) - ("should work for Arrays", function() { - var sum = 0; - for (var x of [1, 2].concat(3)) { - sum += x; - } - assert.strictEqual(sum, 6); - }); - - it("should work for generators", function() { - var value, values = []; - for (value of range(3)) - values.push(value); - assert.deepEqual(values, [0, 1, 2]); - }); - - it("should work inside of generators", function() { - function *yieldPermutations(list) { - if (list.length < 2) { - yield list; - return 1; - } - - var count = 0; - var first = list.slice(0, 1); - var genRest = yieldPermutations(list.slice(1)); - - for (var perm of genRest) { - for (var i = 0; i < list.length; ++i) { - var prefix = perm.slice(0, i); - var suffix = perm.slice(i); - yield prefix.concat(first, suffix); - } - - count += i; - } - - return count; - } - - var count = 0; - for (var perm of yieldPermutations([])) { - assert.deepEqual(perm, []); - ++count; - } - assert.strictEqual(count, 1); - - check(yieldPermutations([1]), [[1]], 1); - - check(yieldPermutations([2, 1]), [ - [2, 1], - [1, 2] - ], 2); - - check(yieldPermutations([1,3,2]), [ - [1, 3, 2], - [3, 1, 2], - [3, 2, 1], - [1, 2, 3], - [2, 1, 3], - [2, 3, 1] - ], 6); - }); -}); - -describe("generator return method", function() { - if (!runningInTranslation) { - // The return method has not been specified or implemented natively, - // yet, so these tests need only pass in translation. - return; - } - - it("should work with newborn generators", function() { - function *gen() { - yield 0; - } - - var g = gen(); - - assert.deepEqual(g.return("argument"), { - value: "argument", - done: true - }); - - assertAlreadyFinished(g); - }); - - it("should behave as if generator actually returned", function() { - var executedFinally = false; - - function *gen() { - try { - yield 0; - } catch (err) { - assert.ok(false, "should not have executed the catch handler"); - } finally { - executedFinally = true; - } - } - - var g = gen(); - assert.deepEqual(g.next(), { value: 0, done: false }); - - assert.deepEqual(g.return("argument"), { - value: "argument", - done: true - }); - - assert.strictEqual(executedFinally, true); - assertAlreadyFinished(g); - }); - - it("should return both delegate and delegator", function() { - var checkpoints = []; - - function* callee(errorToThrow) { - try { - yield 1; - yield 2; - } finally { - checkpoints.push("callee finally"); - if (errorToThrow) { - throw errorToThrow; - } - } - } - - function* caller(errorToThrow) { - try { - yield 0; - yield* callee(errorToThrow); - yield 3; - } finally { - checkpoints.push("caller finally"); - } - } - - var g1 = caller(); - - assert.deepEqual(g1.next(), { value: 0, done: false }); - assert.deepEqual(g1.next(), { value: 1, done: false }); - assert.deepEqual(g1.return(-1), { value: -1, done: true }); - assert.deepEqual(checkpoints, [ - "callee finally", - "caller finally" - ]); - - var error = new Error("thrown from callee"); - var g2 = caller(error); - - assert.deepEqual(g2.next(), { value: 0, done: false }); - assert.deepEqual(g2.next(), { value: 1, done: false }); - - try { - g2.return(-1); - assert.ok(false, "should have thrown an exception"); - } catch (thrown) { - assert.strictEqual(thrown, error); - } - - assert.deepEqual(checkpoints, [ - "callee finally", - "caller finally", - "callee finally", - "caller finally" - ]); - }); -}); - -describe("expressions containing yield subexpressions", function() { - it("should evaluate all subexpressions before yielding", function() { - function *gen(x) { - return x * (yield (function(y) { x = y })); - } - - var g = gen(2); - var result = g.next(); - assert.strictEqual(result.done, false); - - result.value(5); - - assert.deepEqual(g.next(5), { - value: 10, - done: true - }); - }); - - it("should work even with getter member expressions", function() { - function *gen() { - return a.b + (yield "asdf"); - } - - var a = {}; - var b = 0; - - Object.defineProperty(a, "b", { - get: function() { - return ++b; - } - }); - - var g = gen(); - - assert.strictEqual(a.b, 1); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - assert.strictEqual(a.b, 3); - - assert.deepEqual(g.next(2), { - value: 4, - done: true - }); - }); - - it("should evaluate all array elements before yielding", function() { - function *gen() { - return [a, yield "asdf", a]; - } - - var a = 1; - var g = gen(); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - a = 3; - - assert.deepEqual(g.next(2), { - value: [1, 2, 3], - done: true - }); - }); - - it("should handle callee member expressions correctly", function() { - function *gen() { - a = a.slice(0).concat(yield "asdf"); - return a; - } - - var a = []; - var g = gen(); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - a.push(1); - - assert.deepEqual(g.next(2), { - value: [2], - done: true - }); - }); - - it("should handle implicit stringification correctly", function() { - function *gen() { - return a + (yield "asdf"); - } - - var a = [1, 2]; - var g = gen(); - - assert.deepEqual(g.next(), { - value: "asdf", - done: false - }); - - a = [4,5]; - - assert.deepEqual(g.next(",3"), { - value: "1,2,3", - done: true - }); - }); -}); diff --git a/packages/babel-plugin-transform-regenerator/LICENSE b/packages/babel-plugin-transform-regenerator/LICENSE deleted file mode 100644 index 187bfe283df6..000000000000 --- a/packages/babel-plugin-transform-regenerator/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -BSD License - -For "regenerator" software - -Copyright (c) 2014, Facebook, Inc. -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/babel-plugin-transform-regenerator/PATENTS b/packages/babel-plugin-transform-regenerator/PATENTS deleted file mode 100644 index a2bd67d0dbe2..000000000000 --- a/packages/babel-plugin-transform-regenerator/PATENTS +++ /dev/null @@ -1,33 +0,0 @@ -Additional Grant of Patent Rights Version 2 - -"Software" means the Regenerator software distributed by Facebook, Inc. - -Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software -("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable -(subject to the termination provision below) license under any Necessary -Claims, to make, have made, use, sell, offer to sell, import, and otherwise -transfer the Software. For avoidance of doubt, no license is granted under -Facebook's rights in any patent claims that are infringed by (i) modifications -to the Software made by you or any third party or (ii) the Software in -combination with any software or other technology. - -The license granted hereunder will terminate, automatically and without notice, -if you (or any of your subsidiaries, corporate affiliates or agents) initiate -directly or indirectly, or take a direct financial interest in, any Patent -Assertion: (i) against Facebook or any of its subsidiaries or corporate -affiliates, (ii) against any party if such Patent Assertion arises in whole or -in part from any software, technology, product or service of Facebook or any of -its subsidiaries or corporate affiliates, or (iii) against any party relating -to the Software. Notwithstanding the foregoing, if Facebook or any of its -subsidiaries or corporate affiliates files a lawsuit alleging patent -infringement against you in the first instance, and you respond by filing a -patent infringement counterclaim in that lawsuit against that party that is -unrelated to the Software, the license granted hereunder will not terminate -under section (i) of this paragraph due to such counterclaim. - -A "Necessary Claim" is a claim of a patent owned by Facebook that is -necessarily infringed by the Software standing alone. - -A "Patent Assertion" is any lawsuit or other action alleging direct, indirect, -or contributory infringement or inducement to infringe any patent, including a -cross-claim or counterclaim. diff --git a/packages/babel-plugin-transform-regenerator/package.json b/packages/babel-plugin-transform-regenerator/package.json index 491b227a0fd6..bb57a9a2dadc 100644 --- a/packages/babel-plugin-transform-regenerator/package.json +++ b/packages/babel-plugin-transform-regenerator/package.json @@ -2,16 +2,14 @@ "name": "babel-plugin-transform-regenerator", "author": "Ben Newman ", "description": "Explode async and generator functions into a state machine.", - "version": "6.16.1", + "version": "6.20.0", "homepage": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator", "repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-regenerator", "main": "lib/index.js", "dependencies": { - "babel-types": "^6.16.0", - "babel-runtime": "^6.9.0", - "private": "~0.1.5" + "regenerator-transform": "0.9.8" }, - "license": "BSD", + "license": "MIT", "devDependencies": { "babel-helper-plugin-test-runner": "^6.8.0" } diff --git a/packages/babel-plugin-transform-regenerator/src/emit.js b/packages/babel-plugin-transform-regenerator/src/emit.js deleted file mode 100644 index b3378867b3b4..000000000000 --- a/packages/babel-plugin-transform-regenerator/src/emit.js +++ /dev/null @@ -1,1184 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -import * as t from "babel-types"; -import * as leap from "./leap"; -import * as meta from "./meta"; -import * as util from "./util"; - -let hasOwn = Object.prototype.hasOwnProperty; - -function Emitter(contextId) { - assert.ok(this instanceof Emitter); - t.assertIdentifier(contextId); - - // Used to generate unique temporary names. - this.nextTempId = 0; - - // In order to make sure the context object does not collide with - // anything in the local scope, we might have to rename it, so we - // refer to it symbolically instead of just assuming that it will be - // called "context". - this.contextId = contextId; - - // An append-only list of Statements that grows each time this.emit is - // called. - this.listing = []; - - // A sparse array whose keys correspond to locations in this.listing - // that have been marked as branch/jump targets. - this.marked = [true]; - - // The last location will be marked when this.getDispatchLoop is - // called. - this.finalLoc = loc(); - - // A list of all leap.TryEntry statements emitted. - this.tryEntries = []; - - // Each time we evaluate the body of a loop, we tell this.leapManager - // to enter a nested loop context that determines the meaning of break - // and continue statements therein. - this.leapManager = new leap.LeapManager(this); -} - -let Ep = Emitter.prototype; -exports.Emitter = Emitter; - -// Offsets into this.listing that could be used as targets for branches or -// jumps are represented as numeric Literal nodes. This representation has -// the amazingly convenient benefit of allowing the exact value of the -// location to be determined at any time, even after generating code that -// refers to the location. -function loc() { - return t.numericLiteral(-1); -} - -// Sets the exact value of the given location to the offset of the next -// Statement emitted. -Ep.mark = function(loc) { - t.assertLiteral(loc); - let index = this.listing.length; - if (loc.value === -1) { - loc.value = index; - } else { - // Locations can be marked redundantly, but their values cannot change - // once set the first time. - assert.strictEqual(loc.value, index); - } - this.marked[index] = true; - return loc; -}; - -Ep.emit = function(node) { - if (t.isExpression(node)) { - node = t.expressionStatement(node); - } - - t.assertStatement(node); - this.listing.push(node); -}; - -// Shorthand for emitting assignment statements. This will come in handy -// for assignments to temporary variables. -Ep.emitAssign = function(lhs, rhs) { - this.emit(this.assign(lhs, rhs)); - return lhs; -}; - -// Shorthand for an assignment statement. -Ep.assign = function(lhs, rhs) { - return t.expressionStatement( - t.assignmentExpression("=", lhs, rhs)); -}; - -// Convenience function for generating expressions like context.next, -// context.sent, and context.rval. -Ep.contextProperty = function(name, computed) { - return t.memberExpression( - this.contextId, - computed ? t.stringLiteral(name) : t.identifier(name), - !!computed - ); -}; - -// Shorthand for setting context.rval and jumping to `context.stop()`. -Ep.stop = function(rval) { - if (rval) { - this.setReturnValue(rval); - } - - this.jump(this.finalLoc); -}; - -Ep.setReturnValue = function(valuePath) { - t.assertExpression(valuePath.value); - - this.emitAssign( - this.contextProperty("rval"), - this.explodeExpression(valuePath) - ); -}; - -Ep.clearPendingException = function(tryLoc, assignee) { - t.assertLiteral(tryLoc); - - let catchCall = t.callExpression( - this.contextProperty("catch", true), - [tryLoc] - ); - - if (assignee) { - this.emitAssign(assignee, catchCall); - } else { - this.emit(catchCall); - } -}; - -// Emits code for an unconditional jump to the given location, even if the -// exact value of the location is not yet known. -Ep.jump = function(toLoc) { - this.emitAssign(this.contextProperty("next"), toLoc); - this.emit(t.breakStatement()); -}; - -// Conditional jump. -Ep.jumpIf = function(test, toLoc) { - t.assertExpression(test); - t.assertLiteral(toLoc); - - this.emit(t.ifStatement( - test, - t.blockStatement([ - this.assign(this.contextProperty("next"), toLoc), - t.breakStatement() - ]) - )); -}; - -// Conditional jump, with the condition negated. -Ep.jumpIfNot = function(test, toLoc) { - t.assertExpression(test); - t.assertLiteral(toLoc); - - let negatedTest; - if (t.isUnaryExpression(test) && - test.operator === "!") { - // Avoid double negation. - negatedTest = test.argument; - } else { - negatedTest = t.unaryExpression("!", test); - } - - this.emit(t.ifStatement( - negatedTest, - t.blockStatement([ - this.assign(this.contextProperty("next"), toLoc), - t.breakStatement() - ]) - )); -}; - -// Returns a unique MemberExpression that can be used to store and -// retrieve temporary values. Since the object of the member expression is -// the context object, which is presumed to coexist peacefully with all -// other local variables, and since we just increment `nextTempId` -// monotonically, uniqueness is assured. -Ep.makeTempVar = function() { - return this.contextProperty("t" + this.nextTempId++); -}; - -Ep.getContextFunction = function(id) { - return t.functionExpression( - id || null/*Anonymous*/, - [this.contextId], - t.blockStatement([this.getDispatchLoop()]), - false, // Not a generator anymore! - false // Nor an expression. - ); -}; - -// Turns this.listing into a loop of the form -// -// while (1) switch (context.next) { -// case 0: -// ... -// case n: -// return context.stop(); -// } -// -// Each marked location in this.listing will correspond to one generated -// case statement. -Ep.getDispatchLoop = function() { - let self = this; - let cases = []; - let current; - - // If we encounter a break, continue, or return statement in a switch - // case, we can skip the rest of the statements until the next case. - let alreadyEnded = false; - - self.listing.forEach(function(stmt, i) { - if (self.marked.hasOwnProperty(i)) { - cases.push(t.switchCase( - t.numericLiteral(i), - current = [])); - alreadyEnded = false; - } - - if (!alreadyEnded) { - current.push(stmt); - if (t.isCompletionStatement(stmt)) - alreadyEnded = true; - } - }); - - // Now that we know how many statements there will be in this.listing, - // we can finally resolve this.finalLoc.value. - this.finalLoc.value = this.listing.length; - - cases.push( - t.switchCase(this.finalLoc, [ - // Intentionally fall through to the "end" case... - ]), - - // So that the runtime can jump to the final location without having - // to know its offset, we provide the "end" case as a synonym. - t.switchCase(t.stringLiteral("end"), [ - // This will check/clear both context.thrown and context.rval. - t.returnStatement( - t.callExpression(this.contextProperty("stop"), []) - ) - ]) - ); - - return t.whileStatement( - t.numericLiteral(1), - t.switchStatement( - t.assignmentExpression( - "=", - this.contextProperty("prev"), - this.contextProperty("next") - ), - cases - ) - ); -}; - -Ep.getTryLocsList = function() { - if (this.tryEntries.length === 0) { - // To avoid adding a needless [] to the majority of runtime.wrap - // argument lists, force the caller to handle this case specially. - return null; - } - - let lastLocValue = 0; - - return t.arrayExpression( - this.tryEntries.map(function(tryEntry) { - let thisLocValue = tryEntry.firstLoc.value; - assert.ok(thisLocValue >= lastLocValue, "try entries out of order"); - lastLocValue = thisLocValue; - - let ce = tryEntry.catchEntry; - let fe = tryEntry.finallyEntry; - - let locs = [ - tryEntry.firstLoc, - // The null here makes a hole in the array. - ce ? ce.firstLoc : null - ]; - - if (fe) { - locs[2] = fe.firstLoc; - locs[3] = fe.afterLoc; - } - - return t.arrayExpression(locs); - }) - ); -}; - -// All side effects must be realized in order. - -// If any subexpression harbors a leap, all subexpressions must be -// neutered of side effects. - -// No destructive modification of AST nodes. - -Ep.explode = function(path, ignoreResult) { - let node = path.node; - let self = this; - - t.assertNode(node); - - if (t.isDeclaration(node)) - throw getDeclError(node); - - if (t.isStatement(node)) - return self.explodeStatement(path); - - if (t.isExpression(node)) - return self.explodeExpression(path, ignoreResult); - - switch (node.type) { - case "Program": - return path.get("body").map( - self.explodeStatement, - self - ); - - case "VariableDeclarator": - throw getDeclError(node); - - // These node types should be handled by their parent nodes - // (ObjectExpression, SwitchStatement, and TryStatement, respectively). - case "Property": - case "SwitchCase": - case "CatchClause": - throw new Error( - node.type + " nodes should be handled by their parents"); - - default: - throw new Error( - "unknown Node of type " + - JSON.stringify(node.type)); - } -}; - -function getDeclError(node) { - return new Error( - "all declarations should have been transformed into " + - "assignments before the Exploder began its work: " + - JSON.stringify(node)); -} - -Ep.explodeStatement = function(path, labelId) { - let stmt = path.node; - let self = this; - let before, after, head; - - t.assertStatement(stmt); - - if (labelId) { - t.assertIdentifier(labelId); - } else { - labelId = null; - } - - // Explode BlockStatement nodes even if they do not contain a yield, - // because we don't want or need the curly braces. - if (t.isBlockStatement(stmt)) { - path.get("body").forEach(function (path) { - self.explodeStatement(path); - }); - return; - } - - if (!meta.containsLeap(stmt)) { - // Technically we should be able to avoid emitting the statement - // altogether if !meta.hasSideEffects(stmt), but that leads to - // confusing generated code (for instance, `while (true) {}` just - // disappears) and is probably a more appropriate job for a dedicated - // dead code elimination pass. - self.emit(stmt); - return; - } - - switch (stmt.type) { - case "ExpressionStatement": - self.explodeExpression(path.get("expression"), true); - break; - - case "LabeledStatement": - after = loc(); - - // Did you know you can break from any labeled block statement or - // control structure? Well, you can! Note: when a labeled loop is - // encountered, the leap.LabeledEntry created here will immediately - // enclose a leap.LoopEntry on the leap manager's stack, and both - // entries will have the same label. Though this works just fine, it - // may seem a bit redundant. In theory, we could check here to - // determine if stmt knows how to handle its own label; for example, - // stmt happens to be a WhileStatement and so we know it's going to - // establish its own LoopEntry when we explode it (below). Then this - // LabeledEntry would be unnecessary. Alternatively, we might be - // tempted not to pass stmt.label down into self.explodeStatement, - // because we've handled the label here, but that's a mistake because - // labeled loops may contain labeled continue statements, which is not - // something we can handle in this generic case. All in all, I think a - // little redundancy greatly simplifies the logic of this case, since - // it's clear that we handle all possible LabeledStatements correctly - // here, regardless of whether they interact with the leap manager - // themselves. Also remember that labels and break/continue-to-label - // statements are rare, and all of this logic happens at transform - // time, so it has no additional runtime cost. - self.leapManager.withEntry( - new leap.LabeledEntry(after, stmt.label), - function() { - self.explodeStatement(path.get("body"), stmt.label); - } - ); - - self.mark(after); - - break; - - case "WhileStatement": - before = loc(); - after = loc(); - - self.mark(before); - self.jumpIfNot(self.explodeExpression(path.get("test")), after); - self.leapManager.withEntry( - new leap.LoopEntry(after, before, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - self.jump(before); - self.mark(after); - - break; - - case "DoWhileStatement": - let first = loc(); - let test = loc(); - after = loc(); - - self.mark(first); - self.leapManager.withEntry( - new leap.LoopEntry(after, test, labelId), - function() { self.explode(path.get("body")); } - ); - self.mark(test); - self.jumpIf(self.explodeExpression(path.get("test")), first); - self.mark(after); - - break; - - case "ForStatement": - head = loc(); - let update = loc(); - after = loc(); - - if (stmt.init) { - // We pass true here to indicate that if stmt.init is an expression - // then we do not care about its result. - self.explode(path.get("init"), true); - } - - self.mark(head); - - if (stmt.test) { - self.jumpIfNot(self.explodeExpression(path.get("test")), after); - } else { - // No test means continue unconditionally. - } - - self.leapManager.withEntry( - new leap.LoopEntry(after, update, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - - self.mark(update); - - if (stmt.update) { - // We pass true here to indicate that if stmt.update is an - // expression then we do not care about its result. - self.explode(path.get("update"), true); - } - - self.jump(head); - - self.mark(after); - - break; - - case "TypeCastExpression": - return self.explodeExpression(path.get("expression")); - - case "ForInStatement": - head = loc(); - after = loc(); - - let keyIterNextFn = self.makeTempVar(); - self.emitAssign( - keyIterNextFn, - t.callExpression( - util.runtimeProperty("keys"), - [self.explodeExpression(path.get("right"))] - ) - ); - - self.mark(head); - - let keyInfoTmpVar = self.makeTempVar(); - self.jumpIf( - t.memberExpression( - t.assignmentExpression( - "=", - keyInfoTmpVar, - t.callExpression(keyIterNextFn, []) - ), - t.identifier("done"), - false - ), - after - ); - - self.emitAssign( - stmt.left, - t.memberExpression( - keyInfoTmpVar, - t.identifier("value"), - false - ) - ); - - self.leapManager.withEntry( - new leap.LoopEntry(after, head, labelId), - function() { self.explodeStatement(path.get("body")); } - ); - - self.jump(head); - - self.mark(after); - - break; - - case "BreakStatement": - self.emitAbruptCompletion({ - type: "break", - target: self.leapManager.getBreakLoc(stmt.label) - }); - - break; - - case "ContinueStatement": - self.emitAbruptCompletion({ - type: "continue", - target: self.leapManager.getContinueLoc(stmt.label) - }); - - break; - - case "SwitchStatement": - // Always save the discriminant into a temporary variable in case the - // test expressions overwrite values like context.sent. - let disc = self.emitAssign( - self.makeTempVar(), - self.explodeExpression(path.get("discriminant")) - ); - - after = loc(); - let defaultLoc = loc(); - let condition = defaultLoc; - let caseLocs = []; - - // If there are no cases, .cases might be undefined. - let cases = stmt.cases || []; - - for (let i = cases.length - 1; i >= 0; --i) { - let c = cases[i]; - t.assertSwitchCase(c); - - if (c.test) { - condition = t.conditionalExpression( - t.binaryExpression("===", disc, c.test), - caseLocs[i] = loc(), - condition - ); - } else { - caseLocs[i] = defaultLoc; - } - } - - let discriminant = path.get("discriminant"); - discriminant.replaceWith(condition); - self.jump(self.explodeExpression(discriminant)); - - self.leapManager.withEntry( - new leap.SwitchEntry(after), - function() { - path.get("cases").forEach(function(casePath) { - let i = casePath.key; - self.mark(caseLocs[i]); - - casePath.get("consequent").forEach(function (path) { - self.explodeStatement(path); - }); - }); - } - ); - - self.mark(after); - if (defaultLoc.value === -1) { - self.mark(defaultLoc); - assert.strictEqual(after.value, defaultLoc.value); - } - - break; - - case "IfStatement": - let elseLoc = stmt.alternate && loc(); - after = loc(); - - self.jumpIfNot( - self.explodeExpression(path.get("test")), - elseLoc || after - ); - - self.explodeStatement(path.get("consequent")); - - if (elseLoc) { - self.jump(after); - self.mark(elseLoc); - self.explodeStatement(path.get("alternate")); - } - - self.mark(after); - - break; - - case "ReturnStatement": - self.emitAbruptCompletion({ - type: "return", - value: self.explodeExpression(path.get("argument")) - }); - - break; - - case "WithStatement": - throw new Error("WithStatement not supported in generator functions."); - - case "TryStatement": - after = loc(); - - let handler = stmt.handler; - - let catchLoc = handler && loc(); - let catchEntry = catchLoc && new leap.CatchEntry( - catchLoc, - handler.param - ); - - let finallyLoc = stmt.finalizer && loc(); - let finallyEntry = finallyLoc && - new leap.FinallyEntry(finallyLoc, after); - - let tryEntry = new leap.TryEntry( - self.getUnmarkedCurrentLoc(), - catchEntry, - finallyEntry - ); - - self.tryEntries.push(tryEntry); - self.updateContextPrevLoc(tryEntry.firstLoc); - - self.leapManager.withEntry(tryEntry, function() { - self.explodeStatement(path.get("block")); - - if (catchLoc) { - if (finallyLoc) { - // If we have both a catch block and a finally block, then - // because we emit the catch block first, we need to jump over - // it to the finally block. - self.jump(finallyLoc); - - } else { - // If there is no finally block, then we need to jump over the - // catch block to the fall-through location. - self.jump(after); - } - - self.updateContextPrevLoc(self.mark(catchLoc)); - - let bodyPath = path.get("handler.body"); - let safeParam = self.makeTempVar(); - self.clearPendingException(tryEntry.firstLoc, safeParam); - - bodyPath.traverse(catchParamVisitor, { - safeParam: safeParam, - catchParamName: handler.param.name - }); - - self.leapManager.withEntry(catchEntry, function() { - self.explodeStatement(bodyPath); - }); - } - - if (finallyLoc) { - self.updateContextPrevLoc(self.mark(finallyLoc)); - - self.leapManager.withEntry(finallyEntry, function() { - self.explodeStatement(path.get("finalizer")); - }); - - self.emit(t.returnStatement(t.callExpression( - self.contextProperty("finish"), - [finallyEntry.firstLoc] - ))); - } - }); - - self.mark(after); - - break; - - case "ThrowStatement": - self.emit(t.throwStatement( - self.explodeExpression(path.get("argument")) - )); - - break; - - default: - throw new Error( - "unknown Statement of type " + - JSON.stringify(stmt.type)); - } -}; - -let catchParamVisitor = { - Identifier: function(path, state) { - if (path.node.name === state.catchParamName && util.isReference(path)) { - path.replaceWith(state.safeParam); - } - }, - - Scope: function(path, state) { - if (path.scope.hasOwnBinding(state.catchParamName)) { - // Don't descend into nested scopes that shadow the catch - // parameter with their own declarations. - path.skip(); - } - } -}; - -Ep.emitAbruptCompletion = function(record) { - if (!isValidCompletion(record)) { - assert.ok( - false, - "invalid completion record: " + - JSON.stringify(record) - ); - } - - assert.notStrictEqual( - record.type, "normal", - "normal completions are not abrupt" - ); - - let abruptArgs = [t.stringLiteral(record.type)]; - - if (record.type === "break" || - record.type === "continue") { - t.assertLiteral(record.target); - abruptArgs[1] = record.target; - } else if (record.type === "return" || - record.type === "throw") { - if (record.value) { - t.assertExpression(record.value); - abruptArgs[1] = record.value; - } - } - - this.emit( - t.returnStatement( - t.callExpression( - this.contextProperty("abrupt"), - abruptArgs - ) - ) - ); -}; - -function isValidCompletion(record) { - let type = record.type; - - if (type === "normal") { - return !hasOwn.call(record, "target"); - } - - if (type === "break" || - type === "continue") { - return !hasOwn.call(record, "value") - && t.isLiteral(record.target); - } - - if (type === "return" || - type === "throw") { - return hasOwn.call(record, "value") - && !hasOwn.call(record, "target"); - } - - return false; -} - - -// Not all offsets into emitter.listing are potential jump targets. For -// example, execution typically falls into the beginning of a try block -// without jumping directly there. This method returns the current offset -// without marking it, so that a switch case will not necessarily be -// generated for this offset (I say "not necessarily" because the same -// location might end up being marked in the process of emitting other -// statements). There's no logical harm in marking such locations as jump -// targets, but minimizing the number of switch cases keeps the generated -// code shorter. -Ep.getUnmarkedCurrentLoc = function() { - return t.numericLiteral(this.listing.length); -}; - -// The context.prev property takes the value of context.next whenever we -// evaluate the switch statement discriminant, which is generally good -// enough for tracking the last location we jumped to, but sometimes -// context.prev needs to be more precise, such as when we fall -// successfully out of a try block and into a finally block without -// jumping. This method exists to update context.prev to the freshest -// available location. If we were implementing a full interpreter, we -// would know the location of the current instruction with complete -// precision at all times, but we don't have that luxury here, as it would -// be costly and verbose to set context.prev before every statement. -Ep.updateContextPrevLoc = function(loc) { - if (loc) { - t.assertLiteral(loc); - - if (loc.value === -1) { - // If an uninitialized location literal was passed in, set its value - // to the current this.listing.length. - loc.value = this.listing.length; - } else { - // Otherwise assert that the location matches the current offset. - assert.strictEqual(loc.value, this.listing.length); - } - - } else { - loc = this.getUnmarkedCurrentLoc(); - } - - // Make sure context.prev is up to date in case we fell into this try - // statement without jumping to it. TODO Consider avoiding this - // assignment when we know control must have jumped here. - this.emitAssign(this.contextProperty("prev"), loc); -}; - -Ep.explodeExpression = function(path, ignoreResult) { - let expr = path.node; - if (expr) { - t.assertExpression(expr); - } else { - return expr; - } - - let self = this; - let result; // Used optionally by several cases below. - let after; - - function finish(expr) { - t.assertExpression(expr); - if (ignoreResult) { - self.emit(expr); - } else { - return expr; - } - } - - // If the expression does not contain a leap, then we either emit the - // expression as a standalone statement or return it whole. - if (!meta.containsLeap(expr)) { - return finish(expr); - } - - // If any child contains a leap (such as a yield or labeled continue or - // break statement), then any sibling subexpressions will almost - // certainly have to be exploded in order to maintain the order of their - // side effects relative to the leaping child(ren). - let hasLeapingChildren = meta.containsLeap.onlyChildren(expr); - - // In order to save the rest of explodeExpression from a combinatorial - // trainwreck of special cases, explodeViaTempVar is responsible for - // deciding when a subexpression needs to be "exploded," which is my - // very technical term for emitting the subexpression as an assignment - // to a temporary variable and the substituting the temporary variable - // for the original subexpression. Think of exploded view diagrams, not - // Michael Bay movies. The point of exploding subexpressions is to - // control the precise order in which the generated code realizes the - // side effects of those subexpressions. - function explodeViaTempVar(tempVar, childPath, ignoreChildResult) { - assert.ok( - !ignoreChildResult || !tempVar, - "Ignoring the result of a child expression but forcing it to " + - "be assigned to a temporary variable?" - ); - - let result = self.explodeExpression(childPath, ignoreChildResult); - - if (ignoreChildResult) { - // Side effects already emitted above. - - } else if (tempVar || (hasLeapingChildren && - !t.isLiteral(result))) { - // If tempVar was provided, then the result will always be assigned - // to it, even if the result does not otherwise need to be assigned - // to a temporary variable. When no tempVar is provided, we have - // the flexibility to decide whether a temporary variable is really - // necessary. Unfortunately, in general, a temporary variable is - // required whenever any child contains a yield expression, since it - // is difficult to prove (at all, let alone efficiently) whether - // this result would evaluate to the same value before and after the - // yield (see #206). One narrow case where we can prove it doesn't - // matter (and thus we do not need a temporary variable) is when the - // result in question is a Literal value. - result = self.emitAssign( - tempVar || self.makeTempVar(), - result - ); - } - return result; - } - - // If ignoreResult is true, then we must take full responsibility for - // emitting the expression with all its side effects, and we should not - // return a result. - - switch (expr.type) { - case "MemberExpression": - return finish(t.memberExpression( - self.explodeExpression(path.get("object")), - expr.computed - ? explodeViaTempVar(null, path.get("property")) - : expr.property, - expr.computed - )); - - case "CallExpression": - let calleePath = path.get("callee"); - let argsPath = path.get("arguments"); - - let newCallee; - let newArgs = []; - - let hasLeapingArgs = false; - argsPath.forEach(function(argPath) { - hasLeapingArgs = hasLeapingArgs || - meta.containsLeap(argPath.node); - }); - - if (t.isMemberExpression(calleePath.node)) { - if (hasLeapingArgs) { - // If the arguments of the CallExpression contained any yield - // expressions, then we need to be sure to evaluate the callee - // before evaluating the arguments, but if the callee was a member - // expression, then we must be careful that the object of the - // member expression still gets bound to `this` for the call. - - let newObject = explodeViaTempVar( - // Assign the exploded callee.object expression to a temporary - // variable so that we can use it twice without reevaluating it. - self.makeTempVar(), - calleePath.get("object") - ); - - let newProperty = calleePath.node.computed - ? explodeViaTempVar(null, calleePath.get("property")) - : calleePath.node.property; - - newArgs.unshift(newObject); - - newCallee = t.memberExpression( - t.memberExpression( - newObject, - newProperty, - calleePath.node.computed - ), - t.identifier("call"), - false - ); - - } else { - newCallee = self.explodeExpression(calleePath); - } - - } else { - newCallee = self.explodeExpression(calleePath); - - if (t.isMemberExpression(newCallee)) { - // If the callee was not previously a MemberExpression, then the - // CallExpression was "unqualified," meaning its `this` object - // should be the global object. If the exploded expression has - // become a MemberExpression (e.g. a context property, probably a - // temporary variable), then we need to force it to be unqualified - // by using the (0, object.property)(...) trick; otherwise, it - // will receive the object of the MemberExpression as its `this` - // object. - newCallee = t.sequenceExpression([ - t.numericLiteral(0), - newCallee - ]); - } - } - - argsPath.forEach(function(argPath) { - newArgs.push(explodeViaTempVar(null, argPath)); - }); - - return finish(t.callExpression( - newCallee, - newArgs - )); - - case "NewExpression": - return finish(t.newExpression( - explodeViaTempVar(null, path.get("callee")), - path.get("arguments").map(function(argPath) { - return explodeViaTempVar(null, argPath); - }) - )); - - case "ObjectExpression": - return finish(t.objectExpression( - path.get("properties").map(function(propPath) { - if (propPath.isObjectProperty()) { - return t.objectProperty( - propPath.node.key, - explodeViaTempVar(null, propPath.get("value")), - propPath.node.computed - ); - } else { - return propPath.node; - } - }) - )); - - case "ArrayExpression": - return finish(t.arrayExpression( - path.get("elements").map(function(elemPath) { - return explodeViaTempVar(null, elemPath); - }) - )); - - case "SequenceExpression": - let lastIndex = expr.expressions.length - 1; - - path.get("expressions").forEach(function(exprPath) { - if (exprPath.key === lastIndex) { - result = self.explodeExpression(exprPath, ignoreResult); - } else { - self.explodeExpression(exprPath, true); - } - }); - - return result; - - case "LogicalExpression": - after = loc(); - - if (!ignoreResult) { - result = self.makeTempVar(); - } - - let left = explodeViaTempVar(result, path.get("left")); - - if (expr.operator === "&&") { - self.jumpIfNot(left, after); - } else { - assert.strictEqual(expr.operator, "||"); - self.jumpIf(left, after); - } - - explodeViaTempVar(result, path.get("right"), ignoreResult); - - self.mark(after); - - return result; - - case "ConditionalExpression": - let elseLoc = loc(); - after = loc(); - let test = self.explodeExpression(path.get("test")); - - self.jumpIfNot(test, elseLoc); - - if (!ignoreResult) { - result = self.makeTempVar(); - } - - explodeViaTempVar(result, path.get("consequent"), ignoreResult); - self.jump(after); - - self.mark(elseLoc); - explodeViaTempVar(result, path.get("alternate"), ignoreResult); - - self.mark(after); - - return result; - - case "UnaryExpression": - return finish(t.unaryExpression( - expr.operator, - // Can't (and don't need to) break up the syntax of the argument. - // Think about delete a[b]. - self.explodeExpression(path.get("argument")), - !!expr.prefix - )); - - case "BinaryExpression": - return finish(t.binaryExpression( - expr.operator, - explodeViaTempVar(null, path.get("left")), - explodeViaTempVar(null, path.get("right")) - )); - - case "AssignmentExpression": - return finish(t.assignmentExpression( - expr.operator, - self.explodeExpression(path.get("left")), - self.explodeExpression(path.get("right")) - )); - - case "UpdateExpression": - return finish(t.updateExpression( - expr.operator, - self.explodeExpression(path.get("argument")), - expr.prefix - )); - - case "YieldExpression": - after = loc(); - let arg = expr.argument && self.explodeExpression(path.get("argument")); - - if (arg && expr.delegate) { - let result = self.makeTempVar(); - - self.emit(t.returnStatement(t.callExpression( - self.contextProperty("delegateYield"), [ - arg, - t.stringLiteral(result.property.name), - after - ] - ))); - - self.mark(after); - - return result; - } - - self.emitAssign(self.contextProperty("next"), after); - self.emit(t.returnStatement(arg || null)); - self.mark(after); - - return self.contextProperty("sent"); - - default: - throw new Error( - "unknown Expression of type " + - JSON.stringify(expr.type)); - } -}; diff --git a/packages/babel-plugin-transform-regenerator/src/hoist.js b/packages/babel-plugin-transform-regenerator/src/hoist.js deleted file mode 100644 index cac505fdfe12..000000000000 --- a/packages/babel-plugin-transform-regenerator/src/hoist.js +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import * as t from "babel-types"; -let hasOwn = Object.prototype.hasOwnProperty; - -// The hoist function takes a FunctionExpression or FunctionDeclaration -// and replaces any Declaration nodes in its body with assignments, then -// returns a VariableDeclaration containing just the names of the removed -// declarations. -exports.hoist = function(funPath) { - t.assertFunction(funPath.node); - - let vars = {}; - - function varDeclToExpr(vdec, includeIdentifiers) { - t.assertVariableDeclaration(vdec); - // TODO assert.equal(vdec.kind, "var"); - let exprs = []; - - vdec.declarations.forEach(function(dec) { - // Note: We duplicate 'dec.id' here to ensure that the variable declaration IDs don't - // have the same 'loc' value, since that can make sourcemaps and retainLines behave poorly. - vars[dec.id.name] = t.identifier(dec.id.name); - - if (dec.init) { - exprs.push(t.assignmentExpression( - "=", dec.id, dec.init - )); - } else if (includeIdentifiers) { - exprs.push(dec.id); - } - }); - - if (exprs.length === 0) - return null; - - if (exprs.length === 1) - return exprs[0]; - - return t.sequenceExpression(exprs); - } - - funPath.get("body").traverse({ - VariableDeclaration: { - exit: function(path) { - let expr = varDeclToExpr(path.node, false); - if (expr === null) { - path.remove(); - } else { - // We don't need to traverse this expression any further because - // there can't be any new declarations inside an expression. - path.replaceWith(t.expressionStatement(expr)); - } - - // Since the original node has been either removed or replaced, - // avoid traversing it any further. - path.skip(); - } - }, - - ForStatement: function(path) { - let init = path.node.init; - if (t.isVariableDeclaration(init)) { - path.get("init").replaceWith(varDeclToExpr(init, false)); - } - }, - - ForXStatement: function(path) { - let left = path.get("left"); - if (left.isVariableDeclaration()) { - left.replaceWith(varDeclToExpr(left.node, true)); - } - }, - - FunctionDeclaration: function(path) { - let node = path.node; - vars[node.id.name] = node.id; - - let assignment = t.expressionStatement( - t.assignmentExpression( - "=", - node.id, - t.functionExpression( - node.id, - node.params, - node.body, - node.generator, - node.expression - ) - ) - ); - - if (path.parentPath.isBlockStatement()) { - // Insert the assignment form before the first statement in the - // enclosing block. - path.parentPath.unshiftContainer("body", assignment); - - // Remove the function declaration now that we've inserted the - // equivalent assignment form at the beginning of the block. - path.remove(); - } else { - // If the parent node is not a block statement, then we can just - // replace the declaration with the equivalent assignment form - // without worrying about hoisting it. - path.replaceWith(assignment); - } - - // Don't hoist variables out of inner functions. - path.skip(); - }, - - FunctionExpression: function(path) { - // Don't descend into nested function expressions. - path.skip(); - } - }); - - let paramNames = {}; - funPath.get("params").forEach(function(paramPath) { - let param = paramPath.node; - if (t.isIdentifier(param)) { - paramNames[param.name] = param; - } else { - // Variables declared by destructuring parameter patterns will be - // harmlessly re-declared. - } - }); - - let declarations = []; - - Object.keys(vars).forEach(function(name) { - if (!hasOwn.call(paramNames, name)) { - declarations.push(t.variableDeclarator(vars[name], null)); - } - }); - - if (declarations.length === 0) { - return null; // Be sure to handle this case! - } - - return t.variableDeclaration("var", declarations); -}; diff --git a/packages/babel-plugin-transform-regenerator/src/index.js b/packages/babel-plugin-transform-regenerator/src/index.js index 34fea7a15a98..ba3acbf5b92f 100644 --- a/packages/babel-plugin-transform-regenerator/src/index.js +++ b/packages/babel-plugin-transform-regenerator/src/index.js @@ -8,6 +8,4 @@ * the same directory. */ -export default function () { - return require("./visit"); -} +export default require("regenerator-transform"); diff --git a/packages/babel-plugin-transform-regenerator/src/leap.js b/packages/babel-plugin-transform-regenerator/src/leap.js deleted file mode 100644 index 047a2bf263a4..000000000000 --- a/packages/babel-plugin-transform-regenerator/src/leap.js +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -import * as t from "babel-types"; -import { inherits } from "util"; - -function Entry() { - assert.ok(this instanceof Entry); -} - -function FunctionEntry(returnLoc) { - Entry.call(this); - t.assertLiteral(returnLoc); - this.returnLoc = returnLoc; -} - -inherits(FunctionEntry, Entry); -exports.FunctionEntry = FunctionEntry; - -function LoopEntry(breakLoc, continueLoc, label) { - Entry.call(this); - - t.assertLiteral(breakLoc); - t.assertLiteral(continueLoc); - - if (label) { - t.assertIdentifier(label); - } else { - label = null; - } - - this.breakLoc = breakLoc; - this.continueLoc = continueLoc; - this.label = label; -} - -inherits(LoopEntry, Entry); -exports.LoopEntry = LoopEntry; - -function SwitchEntry(breakLoc) { - Entry.call(this); - t.assertLiteral(breakLoc); - this.breakLoc = breakLoc; -} - -inherits(SwitchEntry, Entry); -exports.SwitchEntry = SwitchEntry; - -function TryEntry(firstLoc, catchEntry, finallyEntry) { - Entry.call(this); - - t.assertLiteral(firstLoc); - - if (catchEntry) { - assert.ok(catchEntry instanceof CatchEntry); - } else { - catchEntry = null; - } - - if (finallyEntry) { - assert.ok(finallyEntry instanceof FinallyEntry); - } else { - finallyEntry = null; - } - - // Have to have one or the other (or both). - assert.ok(catchEntry || finallyEntry); - - this.firstLoc = firstLoc; - this.catchEntry = catchEntry; - this.finallyEntry = finallyEntry; -} - -inherits(TryEntry, Entry); -exports.TryEntry = TryEntry; - -function CatchEntry(firstLoc, paramId) { - Entry.call(this); - - t.assertLiteral(firstLoc); - t.assertIdentifier(paramId); - - this.firstLoc = firstLoc; - this.paramId = paramId; -} - -inherits(CatchEntry, Entry); -exports.CatchEntry = CatchEntry; - -function FinallyEntry(firstLoc, afterLoc) { - Entry.call(this); - t.assertLiteral(firstLoc); - t.assertLiteral(afterLoc); - this.firstLoc = firstLoc; - this.afterLoc = afterLoc; -} - -inherits(FinallyEntry, Entry); -exports.FinallyEntry = FinallyEntry; - -function LabeledEntry(breakLoc, label) { - Entry.call(this); - - t.assertLiteral(breakLoc); - t.assertIdentifier(label); - - this.breakLoc = breakLoc; - this.label = label; -} - -inherits(LabeledEntry, Entry); -exports.LabeledEntry = LabeledEntry; - -function LeapManager(emitter) { - assert.ok(this instanceof LeapManager); - - let Emitter = require("./emit").Emitter; - assert.ok(emitter instanceof Emitter); - - this.emitter = emitter; - this.entryStack = [new FunctionEntry(emitter.finalLoc)]; -} - -let LMp = LeapManager.prototype; -exports.LeapManager = LeapManager; - -LMp.withEntry = function(entry, callback) { - assert.ok(entry instanceof Entry); - this.entryStack.push(entry); - try { - callback.call(this.emitter); - } finally { - let popped = this.entryStack.pop(); - assert.strictEqual(popped, entry); - } -}; - -LMp._findLeapLocation = function(property, label) { - for (let i = this.entryStack.length - 1; i >= 0; --i) { - let entry = this.entryStack[i]; - let loc = entry[property]; - if (loc) { - if (label) { - if (entry.label && - entry.label.name === label.name) { - return loc; - } - } else if (entry instanceof LabeledEntry) { - // Ignore LabeledEntry entries unless we are actually breaking to - // a label. - } else { - return loc; - } - } - } - - return null; -}; - -LMp.getBreakLoc = function(label) { - return this._findLeapLocation("breakLoc", label); -}; - -LMp.getContinueLoc = function(label) { - return this._findLeapLocation("continueLoc", label); -}; diff --git a/packages/babel-plugin-transform-regenerator/src/meta.js b/packages/babel-plugin-transform-regenerator/src/meta.js deleted file mode 100644 index dc7ffcdd45a1..000000000000 --- a/packages/babel-plugin-transform-regenerator/src/meta.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -let m = require("private").makeAccessor(); -import * as t from "babel-types"; -let hasOwn = Object.prototype.hasOwnProperty; - -function makePredicate(propertyName, knownTypes) { - function onlyChildren(node) { - t.assertNode(node); - - // Assume no side effects until we find out otherwise. - let result = false; - - function check(child) { - if (result) { - // Do nothing. - } else if (Array.isArray(child)) { - child.some(check); - } else if (t.isNode(child)) { - assert.strictEqual(result, false); - result = predicate(child); - } - return result; - } - - let keys = t.VISITOR_KEYS[node.type]; - if (keys) { - for (let i = 0; i < keys.length; i++) { - let key = keys[i]; - let child = node[key]; - check(child); - } - } - - return result; - } - - function predicate(node) { - t.assertNode(node); - - let meta = m(node); - if (hasOwn.call(meta, propertyName)) - return meta[propertyName]; - - // Certain types are "opaque," which means they have no side - // effects or leaps and we don't care about their subexpressions. - if (hasOwn.call(opaqueTypes, node.type)) - return meta[propertyName] = false; - - if (hasOwn.call(knownTypes, node.type)) - return meta[propertyName] = true; - - return meta[propertyName] = onlyChildren(node); - } - - predicate.onlyChildren = onlyChildren; - - return predicate; -} - -let opaqueTypes = { - FunctionExpression: true -}; - -// These types potentially have side effects regardless of what side -// effects their subexpressions have. -let sideEffectTypes = { - CallExpression: true, // Anything could happen! - ForInStatement: true, // Modifies the key variable. - UnaryExpression: true, // Think delete. - BinaryExpression: true, // Might invoke .toString() or .valueOf(). - AssignmentExpression: true, // Side-effecting by definition. - UpdateExpression: true, // Updates are essentially assignments. - NewExpression: true // Similar to CallExpression. -}; - -// These types are the direct cause of all leaps in control flow. -let leapTypes = { - YieldExpression: true, - BreakStatement: true, - ContinueStatement: true, - ReturnStatement: true, - ThrowStatement: true -}; - -// All leap types are also side effect types. -for (let type in leapTypes) { - if (hasOwn.call(leapTypes, type)) { - sideEffectTypes[type] = leapTypes[type]; - } -} - -exports.hasSideEffects = makePredicate("hasSideEffects", sideEffectTypes); -exports.containsLeap = makePredicate("containsLeap", leapTypes); diff --git a/packages/babel-plugin-transform-regenerator/src/util.js b/packages/babel-plugin-transform-regenerator/src/util.js deleted file mode 100644 index 78bfa0f28b10..000000000000 --- a/packages/babel-plugin-transform-regenerator/src/util.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import * as t from "babel-types"; - -export function runtimeProperty(name) { - return t.memberExpression( - t.identifier("regeneratorRuntime"), - t.identifier(name), - false - ); -} - -export function isReference(path) { - return path.isReferenced() || path.parentPath.isAssignmentExpression({ left: path.node }); -} diff --git a/packages/babel-plugin-transform-regenerator/src/visit.js b/packages/babel-plugin-transform-regenerator/src/visit.js deleted file mode 100644 index 8d96f2b40495..000000000000 --- a/packages/babel-plugin-transform-regenerator/src/visit.js +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) 2014, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * https://raw.github.com/facebook/regenerator/master/LICENSE file. An - * additional grant of patent rights can be found in the PATENTS file in - * the same directory. - */ - -import assert from "assert"; -import * as t from "babel-types"; -import { hoist } from "./hoist"; -import { Emitter } from "./emit"; -import * as util from "./util"; - -let getMarkInfo = require("private").makeAccessor(); - -exports.visitor = { - Function: { - exit: function(path, state) { - let node = path.node; - - if (node.generator) { - if (node.async) { - // Async generator - if (state.opts.asyncGenerators === false) return; - } else { - // Plain generator - if (state.opts.generators === false) return; - } - } else if (node.async) { - // Async function - if (state.opts.async === false) return; - } else { - // Not a generator or async function. - return; - } - - let contextId = path.scope.generateUidIdentifier("context"); - let argsId = path.scope.generateUidIdentifier("args"); - - path.ensureBlock(); - let bodyBlockPath = path.get("body"); - - if (node.async) { - bodyBlockPath.traverse(awaitVisitor); - } - - bodyBlockPath.traverse(functionSentVisitor, { - context: contextId - }); - - let outerBody = []; - let innerBody = []; - - bodyBlockPath.get("body").forEach(function(childPath) { - let node = childPath.node; - if (node && node._blockHoist != null) { - outerBody.push(node); - } else { - innerBody.push(node); - } - }); - - if (outerBody.length > 0) { - // Only replace the inner body if we actually hoisted any statements - // to the outer body. - bodyBlockPath.node.body = innerBody; - } - - let outerFnExpr = getOuterFnExpr(path); - // Note that getOuterFnExpr has the side-effect of ensuring that the - // function has a name (so node.id will always be an Identifier), even - // if a temporary name has to be synthesized. - t.assertIdentifier(node.id); - let innerFnId = t.identifier(node.id.name + "$"); - - // Turn all declarations into vars, and replace the original - // declarations with equivalent assignment expressions. - let vars = hoist(path); - - let didRenameArguments = renameArguments(path, argsId); - if (didRenameArguments) { - vars = vars || t.variableDeclaration("var", []); - vars.declarations.push(t.variableDeclarator( - argsId, t.identifier("arguments") - )); - } - - let emitter = new Emitter(contextId); - emitter.explode(path.get("body")); - - if (vars && vars.declarations.length > 0) { - outerBody.push(vars); - } - - let wrapArgs = [ - emitter.getContextFunction(innerFnId), - // Async functions that are not generators don't care about the - // outer function because they don't need it to be marked and don't - // inherit from its .prototype. - node.generator ? outerFnExpr : t.nullLiteral(), - t.thisExpression() - ]; - - let tryLocsList = emitter.getTryLocsList(); - if (tryLocsList) { - wrapArgs.push(tryLocsList); - } - - let wrapCall = t.callExpression( - util.runtimeProperty(node.async ? "async" : "wrap"), - wrapArgs - ); - - outerBody.push(t.returnStatement(wrapCall)); - node.body = t.blockStatement(outerBody); - - let wasGeneratorFunction = node.generator; - if (wasGeneratorFunction) { - node.generator = false; - } - - if (node.async) { - node.async = false; - } - - if (wasGeneratorFunction && t.isExpression(node)) { - path.replaceWith(t.callExpression(util.runtimeProperty("mark"), [node])); - } - - // Generators are processed in 'exit' handlers so that regenerator only has to run on - // an ES5 AST, but that means traversal will not pick up newly inserted references - // to things like 'regeneratorRuntime'. To avoid this, we explicitly requeue. - path.requeue(); - } - } -}; - -// Given a NodePath for a Function, return an Expression node that can be -// used to refer reliably to the function object from inside the function. -// This expression is essentially a replacement for arguments.callee, with -// the key advantage that it works in strict mode. -function getOuterFnExpr(funPath) { - let node = funPath.node; - t.assertFunction(node); - - if (!node.id) { - // Default-exported function declarations, and function expressions may not - // have a name to reference, so we explicitly add one. - node.id = funPath.scope.parent.generateUidIdentifier("callee"); - } - - if (node.generator && // Non-generator functions don't need to be marked. - t.isFunctionDeclaration(node)) { - let pp = funPath.findParent(function (path) { - return path.isProgram() || path.isBlockStatement(); - }); - - if (!pp) { - return node.id; - } - - let markDecl = getRuntimeMarkDecl(pp); - let markedArray = markDecl.declarations[0].id; - let funDeclIdArray = markDecl.declarations[0].init.callee.object; - t.assertArrayExpression(funDeclIdArray); - - let index = funDeclIdArray.elements.length; - funDeclIdArray.elements.push(node.id); - - return t.memberExpression( - markedArray, - t.numericLiteral(index), - true - ); - } - - return node.id; -} - -function getRuntimeMarkDecl(blockPath) { - let block = blockPath.node; - assert.ok(Array.isArray(block.body)); - - let info = getMarkInfo(block); - if (info.decl) { - return info.decl; - } - - info.decl = t.variableDeclaration("var", [ - t.variableDeclarator( - blockPath.scope.generateUidIdentifier("marked"), - t.callExpression( - t.memberExpression( - t.arrayExpression([]), - t.identifier("map"), - false - ), - [util.runtimeProperty("mark")] - ) - ) - ]); - - blockPath.unshiftContainer("body", info.decl); - - return info.decl; -} - -function renameArguments(funcPath, argsId) { - let state = { - didRenameArguments: false, - argsId: argsId - }; - - funcPath.traverse(argumentsVisitor, state); - - // If the traversal replaced any arguments references, then we need to - // alias the outer function's arguments binding (be it the implicit - // arguments object or some other parameter or variable) to the variable - // named by argsId. - return state.didRenameArguments; -} - -let argumentsVisitor = { - "FunctionExpression|FunctionDeclaration": function(path) { - path.skip(); - }, - - Identifier: function(path, state) { - if (path.node.name === "arguments" && util.isReference(path)) { - path.replaceWith(state.argsId); - state.didRenameArguments = true; - } - } -}; - -let functionSentVisitor = { - MetaProperty(path) { - let { node } = path; - - if (node.meta.name === "function" && node.property.name === "sent") { - path.replaceWith(t.memberExpression(this.context, t.identifier("_sent"))); - } - } -}; - -let awaitVisitor = { - Function: function(path) { - path.skip(); // Don't descend into nested function scopes. - }, - - AwaitExpression: function(path) { - // Convert await expressions to yield expressions. - let argument = path.node.argument; - - // Transforming `await x` to `yield regeneratorRuntime.awrap(x)` - // causes the argument to be wrapped in such a way that the runtime - // can distinguish between awaited and merely yielded values. - path.replaceWith(t.yieldExpression( - t.callExpression( - util.runtimeProperty("awrap"), - [argument] - ), - false - )); - } -}; diff --git a/packages/babel-plugin-transform-runtime/README.md b/packages/babel-plugin-transform-runtime/README.md index 747c11d09ac2..ff42d7e9af98 100644 --- a/packages/babel-plugin-transform-runtime/README.md +++ b/packages/babel-plugin-transform-runtime/README.md @@ -18,18 +18,21 @@ See the [technical details](#technical-details) section for more information on ## Installation +**NOTE - Production vs. development dependencies** + +In most cases, you should install `babel-plugin-transform-runtime` as a development dependency (with `--save-dev`). + ```sh npm install --save-dev babel-plugin-transform-runtime ``` -It is also recommended you install the [babel-runtime](https://www.npmjs.com/package/babel-runtime) package as a runtime dependency, if you haven't already, as the transformed code will require that package. See the examples below for more details. - -**NOTE - Production vs. development dependencies** - -In most cases, you should install `babel-plugin-transform-runtime` as a development dependency (with `--save-dev`) and `babel-runtime` as a production dependency (with `--save`). +and `babel-runtime` as a production dependency (with `--save`). -The transformation plugin is typically used only in development, but the runtime itself will be depended on by your deployed/published code. +```sh +npm install --save babel-runtime +``` +The transformation plugin is typically used only in development, but the runtime itself will be depended on by your deployed/published code. See the examples below for more details. ## Usage diff --git a/packages/babel-polyfill/package.json b/packages/babel-polyfill/package.json index ee174bcf5f65..15cd6f4379de 100644 --- a/packages/babel-polyfill/package.json +++ b/packages/babel-polyfill/package.json @@ -1,6 +1,6 @@ { "name": "babel-polyfill", - "version": "6.16.0", + "version": "6.20.0", "description": "Provides polyfills necessary for a full ES2015+ environment", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", @@ -9,7 +9,7 @@ "main": "lib/index.js", "dependencies": { "core-js": "^2.4.0", - "babel-runtime": "^6.9.1", + "babel-runtime": "^6.20.0", "regenerator-runtime": "^0.10.0" } } diff --git a/packages/babel-runtime/package.json b/packages/babel-runtime/package.json index 4f47c1daf189..73eb96ec151a 100644 --- a/packages/babel-runtime/package.json +++ b/packages/babel-runtime/package.json @@ -1,6 +1,6 @@ { "name": "babel-runtime", - "version": "6.18.0", + "version": "6.20.0", "description": "babel selfContained runtime", "license": "MIT", "repository": "https://github.com/babel/babel/tree/master/packages/babel-runtime", diff --git a/packages/babel-traverse/package.json b/packages/babel-traverse/package.json index 1ac645f20ca4..01bfc59f3975 100644 --- a/packages/babel-traverse/package.json +++ b/packages/babel-traverse/package.json @@ -1,6 +1,6 @@ { "name": "babel-traverse", - "version": "6.19.0", + "version": "6.20.0", "description": "The Babel Traverse module maintains the overall tree state, and is responsible for replacing, removing, and adding nodes", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", @@ -8,10 +8,10 @@ "repository": "https://github.com/babel/babel/tree/master/packages/babel-traverse", "main": "lib/index.js", "dependencies": { - "babel-code-frame": "^6.16.0", + "babel-code-frame": "^6.20.0", "babel-messages": "^6.8.0", - "babel-runtime": "^6.9.0", - "babel-types": "^6.19.0", + "babel-runtime": "^6.20.0", + "babel-types": "^6.20.0", "babylon": "^6.11.0", "debug": "^2.2.0", "globals": "^9.0.0", diff --git a/packages/babel-traverse/src/path/family.js b/packages/babel-traverse/src/path/family.js index b27538edafb9..f7a630d0a79c 100644 --- a/packages/babel-traverse/src/path/family.js +++ b/packages/babel-traverse/src/path/family.js @@ -125,3 +125,64 @@ export function getBindingIdentifiers(duplicates?) { export function getOuterBindingIdentifiers(duplicates?) { return t.getOuterBindingIdentifiers(this.node, duplicates); } + +// original source - https://github.com/babel/babel/blob/master/packages/babel-types/src/retrievers.js +// path.getBindingIdentifiers returns nodes where the following re-implementation +// returns paths +export function getBindingIdentifierPaths(duplicates = false, outerOnly = false) { + let path = this; + let search = [].concat(path); + let ids = Object.create(null); + + while (search.length) { + let id = search.shift(); + if (!id) continue; + if (!id.node) continue; + + let keys = t.getBindingIdentifiers.keys[id.node.type]; + + if (id.isIdentifier()) { + if (duplicates) { + let _ids = ids[id.node.name] = ids[id.node.name] || []; + _ids.push(id); + } else { + ids[id.node.name] = id; + } + continue; + } + + if (id.isExportDeclaration()) { + const declaration = id.get("declaration"); + if (declaration.isDeclaration()) { + search.push(declaration); + } + continue; + } + + if (outerOnly) { + if (id.isFunctionDeclaration()) { + search.push(id.get("id")); + continue; + } + if (id.isFunctionExpression()) { + continue; + } + } + + if (keys) { + for (let i = 0; i < keys.length; i++) { + let key = keys[i]; + let child = id.get(key); + if (Array.isArray(child) || child.node) { + search = search.concat(child); + } + } + } + } + + return ids; +} + +export function getOuterBindingIdentifierPaths(duplicates?) { + return this.getBindingIdentifierPaths(duplicates, true); +} diff --git a/packages/babel-traverse/src/scope/index.js b/packages/babel-traverse/src/scope/index.js index 5c5395877291..83e80df926a8 100644 --- a/packages/babel-traverse/src/scope/index.js +++ b/packages/babel-traverse/src/scope/index.js @@ -37,6 +37,36 @@ function getCache(path, parentScope, self) { } } +// Recursively gathers the identifying names of a node. +function gatherNodeParts(node: Object, parts: Array) { + if (t.isModuleDeclaration(node)) { + if (node.source) { + gatherNodeParts(node.source, parts); + } else if (node.specifiers && node.specifiers.length) { + for (let specifier of (node.specifiers: Array)) { + gatherNodeParts(specifier, parts); + } + } else if (node.declaration) { + gatherNodeParts(node.declaration, parts); + } + } else if (t.isModuleSpecifier(node)) { + gatherNodeParts(node.local, parts); + } else if (t.isMemberExpression(node)) { + gatherNodeParts(node.object, parts); + gatherNodeParts(node.property, parts); + } else if (t.isIdentifier(node)) { + parts.push(node.name); + } else if (t.isLiteral(node)) { + parts.push(node.value); + } else if (t.isCallExpression(node)) { + gatherNodeParts(node.callee, parts); + } else if (t.isObjectExpression(node) || t.isObjectPattern(node)) { + for (let prop of (node.properties: Array)) { + gatherNodeParts(prop.key || prop.argument, parts); + } + } +} + // let collectorVisitor = { @@ -254,38 +284,8 @@ export default class Scope { node = node.key; } - let parts = []; - - let add = function (node) { - if (t.isModuleDeclaration(node)) { - if (node.source) { - add(node.source); - } else if (node.specifiers && node.specifiers.length) { - for (let specifier of (node.specifiers: Array)) { - add(specifier); - } - } else if (node.declaration) { - add(node.declaration); - } - } else if (t.isModuleSpecifier(node)) { - add(node.local); - } else if (t.isMemberExpression(node)) { - add(node.object); - add(node.property); - } else if (t.isIdentifier(node)) { - parts.push(node.name); - } else if (t.isLiteral(node)) { - parts.push(node.value); - } else if (t.isCallExpression(node)) { - add(node.callee); - } else if (t.isObjectExpression(node) || t.isObjectPattern(node)) { - for (let prop of (node.properties: Array)) { - add(prop.key || prop.argument); - } - } - }; - - add(node); + const parts = []; + gatherNodeParts(node, parts); let id = parts.join("$"); id = id.replace(/^_/, "") || defaultName || "ref"; diff --git a/packages/babel-traverse/test/family.js b/packages/babel-traverse/test/family.js new file mode 100644 index 000000000000..c6fc297f86a6 --- /dev/null +++ b/packages/babel-traverse/test/family.js @@ -0,0 +1,60 @@ +let traverse = require("../lib").default; +let assert = require("assert"); +let parse = require("babylon").parse; + +describe("path/family", function () { + describe("getBindingIdentifiers", function () { + let ast = parse("var a = 1, {b} = c, [d] = e; function f() {}"); + let nodes = {}, paths = {}, outerNodes = {}, outerPaths = {}; + traverse(ast, { + VariableDeclaration(path) { + nodes = path.getBindingIdentifiers(); + paths = path.getBindingIdentifierPaths(); + }, + FunctionDeclaration(path) { + outerNodes = path.getOuterBindingIdentifiers(); + outerPaths = path.getOuterBindingIdentifierPaths(); + } + }); + + it("should contain keys of nodes in paths", function () { + Object.keys(nodes).forEach((id) => { + assert.strictEqual(hop(paths, id), true, "Node's keys exists in paths"); + }); + }); + + it("should contain outer bindings", function () { + Object.keys(outerNodes).forEach((id) => { + assert.strictEqual(hop(outerPaths, id), true, "Has same outer keys"); + }); + }); + + it("should return paths", function () { + Object.keys(paths).forEach((id) => { + assert.strictEqual(!!paths[id].node, true, "Has a property node that's not falsy"); + assert.strictEqual(paths[id].type, paths[id].node.type, "type matches"); + }); + + Object.keys(outerPaths).forEach((id) => { + assert.strictEqual(!!outerPaths[id].node, true, "has property node"); + assert.strictEqual(outerPaths[id].type, outerPaths[id].node.type, "type matches"); + }); + }); + + it("should match paths and nodes returned for the same ast", function () { + Object.keys(nodes).forEach((id) => { + assert.strictEqual(nodes[id], paths[id].node, "Nodes match"); + }); + }); + + it("should match paths and nodes returned for outer Bindings", function () { + Object.keys(outerNodes).forEach((id) => { + assert.strictEqual(outerNodes[id], outerPaths[id].node, "nodes match"); + }); + }); + }); +}); + +function hop(o, key) { + return Object.hasOwnProperty.call(o, key); +} diff --git a/packages/babel-types/README.md b/packages/babel-types/README.md index 9620485d124d..855bc4cef530 100644 --- a/packages/babel-types/README.md +++ b/packages/babel-types/README.md @@ -865,6 +865,18 @@ Aliases: `Statement`, `Conditional` - `consequent`: `Statement` (required) - `alternate`: `Statement` (default: `null`) +--- + +### import +```javascript +t.import() +``` + +See also `t.isImport(node, opts)` and `t.assertImport(node, opts)`. + +Aliases: `Expression` + + --- ### importDeclaration diff --git a/packages/babel-types/package.json b/packages/babel-types/package.json index 82188332e343..96a12cce7140 100644 --- a/packages/babel-types/package.json +++ b/packages/babel-types/package.json @@ -1,6 +1,6 @@ { "name": "babel-types", - "version": "6.19.0", + "version": "6.20.0", "description": "Babel Types is a Lodash-esque utility library for AST nodes", "author": "Sebastian McKenzie ", "homepage": "https://babeljs.io/", @@ -8,7 +8,7 @@ "repository": "https://github.com/babel/babel/tree/master/packages/babel-types", "main": "lib/index.js", "dependencies": { - "babel-runtime": "^6.9.1", + "babel-runtime": "^6.20.0", "esutils": "^2.0.2", "lodash": "^4.2.0", "to-fast-properties": "^1.0.1"