diff --git a/packages/babel-core/src/transformation/internal-plugins/shadow-functions.js b/packages/babel-core/src/transformation/internal-plugins/shadow-functions.js index 13e5e955ac..5a5681d67c 100644 --- a/packages/babel-core/src/transformation/internal-plugins/shadow-functions.js +++ b/packages/babel-core/src/transformation/internal-plugins/shadow-functions.js @@ -19,7 +19,7 @@ function shouldShadow(path, shadowPath) { if (path.is("_forceShadow")) { return true; } else { - return shadowPath && !shadowPath.isArrowFunctionExpression(); + return shadowPath; } } @@ -29,7 +29,9 @@ function remap(path, key, create) { if (!shouldShadow(path, shadowPath)) return; let shadowFunction = path.node._shadowedFunctionLiteral; + let currentFunction; + let passedShadowFunction = false; let fnPath = path.findParent(function (path) { if (path.isProgram() || path.isFunction()) { @@ -38,21 +40,37 @@ function remap(path, key, create) { } if (path.isProgram()) { + passedShadowFunction = true; + return true; - } else if (path.isFunction()) { + } else if (path.isFunction() && !path.isArrowFunctionExpression()) { if (shadowFunction) { - return path === shadowFunction || path.node === shadowFunction.node; + if (path === shadowFunction || path.node === shadowFunction.node) return true; } else { - return !path.is("shadow"); + if (!path.is("shadow")) return true; } + + passedShadowFunction = true; + return false; } return false; }); + if (shadowFunction && fnPath.isProgram() && !shadowFunction.isProgram()){ + // If the shadow wasn't found, take the closest function as a backup. + // This is a bit of a hack, but it will allow the parameter transforms to work properly + // without introducing yet another shadow-controlling flag. + fnPath = path.findParent((p) => p.isProgram() || p.isFunction()); + } + // no point in realiasing if we're in this function if (fnPath === currentFunction) return; + // If the only functions that were encountered are arrow functions, skip remapping the + // binding since arrow function syntax already does that. + if (!passedShadowFunction) return; + let cached = fnPath.getData(key); if (cached) return path.replaceWith(cached); 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 f22ab48e35..c1aa6d34a2 100644 --- a/packages/babel-helper-remap-async-to-generator/src/index.js +++ b/packages/babel-helper-remap-async-to-generator/src/index.js @@ -6,7 +6,7 @@ import template from "babel-template"; import * as t from "babel-types"; let buildWrapper = template(` - (function () { + (() => { var ref = FUNCTION; return function NAME(PARAMS) { return ref.apply(this, arguments); @@ -15,7 +15,7 @@ let buildWrapper = template(` `); let namedBuildWrapper = template(` - (function () { + (() => { var ref = FUNCTION; function NAME(PARAMS) { return ref.apply(this, arguments); @@ -24,15 +24,6 @@ let namedBuildWrapper = template(` }) `); -let arrowBuildWrapper = template(` - (() => { - var ref = FUNCTION, _this = this; - return function(PARAMS) { - return ref.apply(_this, arguments); - }; - }) -`); - let awaitVisitor = { ArrowFunctionExpression(path) { if (!path.node.async) { @@ -69,19 +60,12 @@ function plainFunction(path: NodePath, callId: Object) { if (path.isArrowFunctionExpression()) { path.arrowFunctionToShadowed(); - wrapper = arrowBuildWrapper; } else if (!isDeclaration && asyncFnId) { wrapper = namedBuildWrapper; } node.async = false; node.generator = true; - // Either the wrapped generator is invoked with `.apply(this, arguments)` or it has no params, - // so it should capture `arguments` - if (node.shadow) { - // node.shadow may be `true` or an object - node.shadow = Object.assign({}, node.shadow, { arguments: false }); - } node.id = null; diff --git a/packages/babel-helper-replace-supers/src/index.js b/packages/babel-helper-replace-supers/src/index.js index 76b05d7ad4..7010cb1670 100644 --- a/packages/babel-helper-replace-supers/src/index.js +++ b/packages/babel-helper-replace-supers/src/index.js @@ -20,11 +20,7 @@ function isMemberExpressionSuper(node) { } let visitor = { - "ObjectMethod|ClassMethod"(path) { - path.skip(); - }, - - "FunctionDeclaration|FunctionExpression"(path) { + Function(path) { if (!path.inShadow("this")) { path.skip(); } diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/async-arrow-in-method/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/async-arrow-in-method/expected.js index 3d421469f5..a307b98c83 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/async-arrow-in-method/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/async-arrow-in-method/expected.js @@ -1,19 +1,17 @@ -let TestClass = { - name: "John Doe", - - testMethodFailure() { - return new Promise((() => { - var _this2 = this; - - var ref = babelHelpers.asyncToGenerator(function* (resolve) { - console.log(_this2); - setTimeout(resolve, 1000); - }), - _this = this; - - return function (_x) { - return ref.apply(_this, arguments); - }; - })()); - } -}; +let TestClass = { + name: "John Doe", + + testMethodFailure() { + var _this = this; + + return new Promise((() => { + var ref = babelHelpers.asyncToGenerator(function* (resolve) { + console.log(_this); + setTimeout(resolve, 1000); + }); + return function (_x) { + return ref.apply(this, arguments); + }; + })()); + } +}; diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/actual.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/actual.js index 7d51e3957f..54b9fa5000 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/actual.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/actual.js @@ -1,14 +1,16 @@ -async function s(x) { - let t = async (y, a) => { - let r = async (z, b) => { - await z; - return this.x; - } - await r(); - - return this.g(r); - } - - await t(); - return this.h(t); -} +async function s(x, ...args) { + let t = async (y, a) => { + let r = async (z, b, ...innerArgs) => { + await z; + console.log(this, innerArgs, arguments); + return this.x; + } + await r(); + + console.log(this, args, arguments); + return this.g(r); + } + + await t(); + return this.h(t); +} diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/expected.js index 9bc186bfde..94c5622431 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/deeply-nested-asyncs/expected.js @@ -1,37 +1,42 @@ -let s = function () { - var ref = babelHelpers.asyncToGenerator(function* (x) { - let t = (() => { - var _this3 = this; - - var ref = babelHelpers.asyncToGenerator(function* (y, a) { - let r = (() => { - var _this2 = this; - - var ref = babelHelpers.asyncToGenerator(function* (z, b) { - yield z; - return _this2.x; - }), - _this = this; - - return function r(_x4, _x5) { - return ref.apply(_this, arguments); - }; - })(); - yield r(); - - return _this3.g(r); - }), - _this = this; - - return function t(_x2, _x3) { - return ref.apply(_this, arguments); - }; - })(); - - yield t(); - return this.h(t); - }); - return function s(_x) { - return ref.apply(this, arguments); - }; -}(); +let s = (() => { + var ref = babelHelpers.asyncToGenerator(function* (x) { + var _this = this, + _arguments = arguments; + + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + let t = (() => { + var ref = babelHelpers.asyncToGenerator(function* (y, a) { + let r = (() => { + var ref = babelHelpers.asyncToGenerator(function* (z, b) { + for (var _len2 = arguments.length, innerArgs = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { + innerArgs[_key2 - 2] = arguments[_key2]; + } + + yield z; + console.log(_this, innerArgs, _arguments); + return _this.x; + }); + return function r(_x4, _x5) { + return ref.apply(this, arguments); + }; + })(); + yield r(); + + console.log(_this, args, _arguments); + return _this.g(r); + }); + return function t(_x2, _x3) { + return ref.apply(this, arguments); + }; + })(); + + yield t(); + return this.h(t); + }); + return function s(_x) { + return ref.apply(this, arguments); + }; +})(); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/expression/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/expression/expected.js index e3c7d2218f..baff50e27f 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/expression/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/expression/expected.js @@ -1,8 +1,8 @@ -var foo = function () { +var foo = (() => { var ref = babelHelpers.asyncToGenerator(function* () { var wat = yield bar(); }); return function foo() { return ref.apply(this, arguments); }; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/named-expression/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/named-expression/expected.js index c6282326b3..fabf097a50 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/named-expression/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/named-expression/expected.js @@ -1,4 +1,4 @@ -var foo = function () { +var foo = (() => { var ref = babelHelpers.asyncToGenerator(function* () { console.log(bar); }); @@ -8,4 +8,4 @@ var foo = function () { } return bar; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/options.json b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/options.json index 54b64ad0fb..78b845844f 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/options.json +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/options.json @@ -1,3 +1,7 @@ { - "plugins": ["external-helpers", "transform-async-to-generator"] + "plugins": [ + "external-helpers", + "transform-es2015-parameters", + "transform-async-to-generator" + ] } diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/parameters/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/parameters/expected.js index 2cbaf5139e..bee7bb6870 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/parameters/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/parameters/expected.js @@ -1,6 +1,6 @@ -let foo = function () { +let foo = (() => { var ref = babelHelpers.asyncToGenerator(function* (bar) {}); return function foo(_x) { return ref.apply(this, arguments); }; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/statement/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/statement/expected.js index 51140320d4..4fa3965734 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/statement/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/async-to-generator/statement/expected.js @@ -1,8 +1,8 @@ -let foo = function () { +let foo = (() => { var ref = babelHelpers.asyncToGenerator(function* () { var wat = yield bar(); }); return function foo() { return ref.apply(this, arguments); }; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/import-and-export/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/import-and-export/expected.js index 7ea795d5ca..5ac84b7eb0 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/import-and-export/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/import-and-export/expected.js @@ -5,12 +5,12 @@ Object.defineProperty(exports, "__esModule", { }); exports.foo = undefined; -let foo = exports.foo = function () { +let foo = exports.foo = (() => { var ref = babelHelpers.asyncToGenerator(function* () {}); return function foo() { return ref.apply(this, arguments); }; -}(); +})(); var _bar = require('bar'); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/lone-export/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/lone-export/expected.js index 3f1072c7e3..99b7a1da10 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/lone-export/expected.js +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/export-async/lone-export/expected.js @@ -4,9 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); -let foo = exports.foo = function () { +let foo = exports.foo = (() => { var ref = babelHelpers.asyncToGenerator(function* () {}); return function foo() { return ref.apply(this, arguments); }; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T6882/options.json b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T6882/options.json new file mode 100644 index 0000000000..d5312adc2d --- /dev/null +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T6882/options.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + "external-helpers", + "transform-async-to-generator", + "transform-es2015-block-scoping", + "transform-es2015-arrow-functions", + "transform-regenerator" + ] +} diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/actual.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/actual.js new file mode 100644 index 0000000000..68075431ac --- /dev/null +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/actual.js @@ -0,0 +1,33 @@ +class Test{ + static async method1() { + console.log(this); + + setTimeout(async () => { + console.log(this); + }); + } + + static async method2() { + console.log(this); + + setTimeout(async (arg) => { + console.log(this); + }); + } + + async method1() { + console.log(this); + + setTimeout(async () => { + console.log(this); + }); + } + + async method2() { + console.log(this); + + setTimeout(async (arg) => { + console.log(this); + }); + } +} diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/expected.js b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/expected.js new file mode 100644 index 0000000000..a2dd94f127 --- /dev/null +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/expected.js @@ -0,0 +1,59 @@ +class Test { + static method1() { + var _this = this; + + return babelHelpers.asyncToGenerator(function* () { + console.log(_this); + + setTimeout(babelHelpers.asyncToGenerator(function* () { + console.log(_this); + })); + })(); + } + + static method2() { + var _this2 = this; + + return babelHelpers.asyncToGenerator(function* () { + console.log(_this2); + + setTimeout((() => { + var ref = babelHelpers.asyncToGenerator(function* (arg) { + console.log(_this2); + }); + return function (_x) { + return ref.apply(this, arguments); + }; + })()); + })(); + } + + method1() { + var _this3 = this; + + return babelHelpers.asyncToGenerator(function* () { + console.log(_this3); + + setTimeout(babelHelpers.asyncToGenerator(function* () { + console.log(_this3); + })); + })(); + } + + method2() { + var _this4 = this; + + return babelHelpers.asyncToGenerator(function* () { + console.log(_this4); + + setTimeout((() => { + var ref = babelHelpers.asyncToGenerator(function* (arg) { + console.log(_this4); + }); + return function (_x2) { + return ref.apply(this, arguments); + }; + })()); + })(); + } +} diff --git a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/options.json b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/options.json index 94bdeeb0eb..6b5744a918 100644 --- a/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/options.json +++ b/packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/options.json @@ -1,3 +1,6 @@ { - "plugins": ["external-helpers", "transform-es2015-block-scoping", "transform-regenerator", "transform-async-to-generator"] + "plugins": [ + "external-helpers", + "transform-async-to-generator" + ] } diff --git a/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/expression/expected.js b/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/expression/expected.js index 1d7bf3bba5..d4567b0dea 100644 --- a/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/expression/expected.js +++ b/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/expression/expected.js @@ -1,5 +1,5 @@ import { coroutine as _coroutine } from "bluebird"; -var foo = function () { +var foo = (() => { var ref = _coroutine(function* () { var wat = yield bar(); }); @@ -7,4 +7,4 @@ var foo = function () { return function foo() { return ref.apply(this, arguments); }; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/named-expression/expected.js b/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/named-expression/expected.js index 082562347d..91a3c61229 100644 --- a/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/named-expression/expected.js +++ b/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/named-expression/expected.js @@ -1,5 +1,5 @@ import { coroutine as _coroutine } from "bluebird"; -var foo = function () { +var foo = (() => { var ref = _coroutine(function* () { console.log(bar); }); @@ -9,4 +9,4 @@ var foo = function () { } return bar; -}(); +})(); diff --git a/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/statement/expected.js b/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/statement/expected.js index ef7b97d56b..72167461b0 100644 --- a/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/statement/expected.js +++ b/packages/babel-plugin-transform-async-to-module-method/test/fixtures/bluebird-coroutines/statement/expected.js @@ -1,6 +1,6 @@ import { coroutine as _coroutine } from "bluebird"; -let foo = function () { +let foo = (() => { var ref = _coroutine(function* () { var wat = yield bar(); }); @@ -8,4 +8,4 @@ let foo = function () { return function foo() { return ref.apply(this, arguments); }; -}(); +})(); diff --git a/packages/babel-traverse/src/path/ancestry.js b/packages/babel-traverse/src/path/ancestry.js index 7e52857056..e7c3217093 100644 --- a/packages/babel-traverse/src/path/ancestry.js +++ b/packages/babel-traverse/src/path/ancestry.js @@ -188,30 +188,57 @@ export function inType() { } /** - * Check if we're inside a shadowed function. + * Checks whether the binding for 'key' is a local binding in its current function context. + * + * Checks if the current path either is, or has a direct parent function that is, inside + * of a function that is marked for shadowing of a binding matching 'key'. Also returns + * the parent path if the parent path is an arrow, since arrow functions pass through + * binding values to their parent, meaning they have no local bindings. + * + * Shadowing means that when the given binding is transformed, it will read the binding + * value from the container containing the shadow function, rather than from inside the + * shadow function. + * + * Function shadowing is acheieved by adding a "shadow" property on "FunctionExpression" + * and "FunctionDeclaration" node types. + * + * Node's "shadow" props have the following behavior: + * + * - Boolean true will cause the function to shadow both "this" and "arguments". + * - {this: false} Shadows "arguments" but not "this". + * - {arguments: false} Shadows "this" but not "arguments". + * + * Separately, individual identifiers can be flagged with two flags: + * + * - _forceShadow - If truthy, this specific identifier will be bound in the closest + * Function that is not flagged "shadow", or the Program. + * - _shadowedFunctionLiteral - When set to a NodePath, this specific identifier will be bound + * to this NodePath/Node or the Program. If this path is not found relative to the + * starting location path, the closest function will be used. + * + * Please Note, these flags are for private internal use only and should be avoided. + * Only "shadow" is a public property that other transforms may manipulate. */ export function inShadow(key?) { - let path = this; - do { - if (path.isFunction()) { - let shadow = path.node.shadow; - if (shadow) { - // this is because sometimes we may have a `shadow` value of: - // - // { this: false } - // - // we need to catch this case if `inShadow` has been passed a `key` - if (!key || shadow[key] !== false) { - return path; - } - } else if (path.isArrowFunctionExpression()) { - return path; - } + let parentFn = this.isFunction() ? this : this.findParent((p) => p.isFunction()); + if (!parentFn) return; - // normal function, we've found our function context - return null; + if (parentFn.isFunctionExpression() || parentFn.isFunctionDeclaration()) { + let shadow = parentFn.node.shadow; + + // this is because sometimes we may have a `shadow` value of: + // + // { this: false } + // + // we need to catch this case if `inShadow` has been passed a `key` + if (shadow && (!key || shadow[key] !== false)) { + return parentFn; } - } while (path = path.parentPath); + } else if (parentFn.isArrowFunctionExpression()) { + return parentFn; + } + + // normal function, we've found our function context return null; }