Merge pull request #3405 from loganfsmyth/shadowing-fixes
Fix shadow function processing for async functions
This commit is contained in:
commit
bbc3401c71
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
})());
|
||||
}
|
||||
};
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
})();
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -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;
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "transform-async-to-generator"]
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-es2015-parameters",
|
||||
"transform-async-to-generator"
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
let foo = function () {
|
||||
let foo = (() => {
|
||||
var ref = babelHelpers.asyncToGenerator(function* (bar) {});
|
||||
return function foo(_x) {
|
||||
return ref.apply(this, arguments);
|
||||
};
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -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');
|
||||
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
{
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-async-to-generator",
|
||||
"transform-es2015-block-scoping",
|
||||
"transform-es2015-arrow-functions",
|
||||
"transform-regenerator"
|
||||
]
|
||||
}
|
||||
33
packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/actual.js
vendored
Normal file
33
packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/actual.js
vendored
Normal file
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
59
packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/expected.js
vendored
Normal file
59
packages/babel-plugin-transform-async-to-generator/test/fixtures/regression/T7108/expected.js
vendored
Normal file
@ -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);
|
||||
};
|
||||
})());
|
||||
})();
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,6 @@
|
||||
{
|
||||
"plugins": ["external-helpers", "transform-es2015-block-scoping", "transform-regenerator", "transform-async-to-generator"]
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
"transform-async-to-generator"
|
||||
]
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -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;
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}();
|
||||
})();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user