Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement newableArrowFunctions assumption #12613

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/babel-core/src/config/validation/options.js
Expand Up @@ -337,6 +337,7 @@ export const assumptionsNames = new Set<string>([
"ignoreToPrimitiveHint",
"iterableIsArray",
"mutableTemplateObject",
"newableArrowFunctions",
"noDocumentAll",
"pureGetters",
"setClassMethods",
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-helper-remap-async-to-generator/src/index.js
Expand Up @@ -31,6 +31,7 @@ const awaitVisitor = {
export default function (
path: NodePath,
helpers: { wrapAsync: Object, wrapAwait: Object },
newableArrowFunctions?: boolean,
) {
path.traverse(awaitVisitor, {
wrapAwait: helpers.wrapAwait,
Expand All @@ -41,7 +42,7 @@ export default function (
path.node.async = false;
path.node.generator = true;

wrapFunction(path, t.cloneNode(helpers.wrapAsync));
wrapFunction(path, t.cloneNode(helpers.wrapAsync), newableArrowFunctions);

const isProperty =
path.isObjectMethod() ||
Expand Down
17 changes: 13 additions & 4 deletions packages/babel-helper-wrap-function/src/index.js
Expand Up @@ -57,7 +57,11 @@ function classOrObjectMethod(path: NodePath, callId: Object) {
.unwrapFunctionEnvironment();
}

function plainFunction(path: NodePath, callId: Object) {
function plainFunction(
path: NodePath,
callId: Object,
newableArrowFunctions: boolean,
) {
const node = path.node;
const isDeclaration = path.isFunctionDeclaration();
const functionId = node.id;
Expand All @@ -68,7 +72,7 @@ function plainFunction(path: NodePath, callId: Object) {
: buildAnonymousExpressionWrapper;

if (path.isArrowFunctionExpression()) {
path.arrowFunctionToExpression();
path.arrowFunctionToExpression({ newableArrowFunctions });
}

node.id = null;
Expand Down Expand Up @@ -123,10 +127,15 @@ function plainFunction(path: NodePath, callId: Object) {
}
}

export default function wrapFunction(path: NodePath, callId: Object) {
export default function wrapFunction(
path: NodePath,
callId: Object,
// TODO(Babel 8): Consider defaulting to false for spec compliancy
newableArrowFunctions: boolean = true,
) {
if (path.isMethod()) {
classOrObjectMethod(path, callId);
} else {
plainFunction(path, callId);
plainFunction(path, callId, newableArrowFunctions);
}
}
Expand Up @@ -70,6 +70,8 @@ export default declare(api => {

path.traverse(yieldStarVisitor, state);

// We don't need to pass the newableArrowFunctions assumption, since
// async generators are never arrow functions.
remapAsyncToGenerator(path, {
wrapAsync: state.addHelper("wrapAsyncGenerator"),
wrapAwait: state.addHelper("awaitAsyncGenerator"),
Expand Down
9 changes: 7 additions & 2 deletions packages/babel-plugin-transform-arrow-functions/src/index.js
Expand Up @@ -4,7 +4,9 @@ import type NodePath from "@babel/traverse";
export default declare((api, options) => {
api.assertVersion(7);

const { spec } = options;
const newableArrowFunctions =
api.assumption("newableArrowFunctions") ?? !options.spec;

return {
name: "transform-arrow-functions",

Expand All @@ -20,7 +22,10 @@ export default declare((api, options) => {
// While other utils may be fine inserting other arrows to make more transforms possible,
// the arrow transform itself absolutely cannot insert new arrow functions.
allowInsertArrow: false,
specCompliant: !!spec,
newableArrowFunctions,

// TODO(Babel 8): This is only needed for backward compat with @babel/traverse <7.13.0
specCompliant: !newableArrowFunctions,
});
},
},
Expand Down

This file was deleted.

@@ -0,0 +1,10 @@
function foo() {
arr.map(x => x * x);
var f = (x, y) => x * y;
(function () {
return () => this;
})();
return {
g: () => this
}
}
@@ -0,0 +1,29 @@
function foo() {
var _this = this;

arr.map(function (x) {
babelHelpers.newArrowCheck(this, _this);
return x * x;
}.bind(this));

var f = function f(x, y) {
babelHelpers.newArrowCheck(this, _this);
return x * y;
}.bind(this);

(function () {
var _this2 = this;

return function () {
babelHelpers.newArrowCheck(this, _this2);
return this;
}.bind(this);
})();

return {
g: function g() {
babelHelpers.newArrowCheck(this, _this);
return this;
}.bind(this)
};
}
@@ -0,0 +1,6 @@
{
"plugins": ["external-helpers", "transform-arrow-functions"],
"assumptions": {
"newableArrowFunctions": false
}
}
@@ -0,0 +1 @@
let a = () => 1;
@@ -0,0 +1,6 @@
var _this = this;

let a = function a() {
babelHelpers.newArrowCheck(this, _this);
return 1;
}.bind(this);
@@ -0,0 +1 @@
let a = () => 1;
@@ -0,0 +1,9 @@
{
"plugins": [
"external-helpers",
["transform-arrow-functions", { "spec": false }]
],
"assumptions": {
"newableArrowFunctions": false
}
}
@@ -0,0 +1,6 @@
var _this = this;

let a = function a() {
babelHelpers.newArrowCheck(this, _this);
return 1;
}.bind(this);
@@ -0,0 +1 @@
let a = () => 1;
@@ -0,0 +1,9 @@
{
"plugins": [
"external-helpers",
["transform-arrow-functions", { "spec": true }]
],
"assumptions": {
"newableArrowFunctions": true
}
}
@@ -0,0 +1,3 @@
let a = function () {
return 1;
};
11 changes: 7 additions & 4 deletions packages/babel-plugin-transform-async-to-generator/src/index.js
Expand Up @@ -7,6 +7,7 @@ export default declare((api, options) => {
api.assertVersion(7);

const { method, module } = options;
const newableArrowFunctions = api.assumption("newableArrowFunctions");

if (method && module) {
return {
Expand All @@ -23,7 +24,7 @@ export default declare((api, options) => {
wrapAsync = state.methodWrapper = addNamed(path, method, module);
}

remapAsyncToGenerator(path, { wrapAsync });
remapAsyncToGenerator(path, { wrapAsync }, newableArrowFunctions);
},
},
};
Expand All @@ -36,9 +37,11 @@ export default declare((api, options) => {
Function(path, state) {
if (!path.node.async || path.node.generator) return;

remapAsyncToGenerator(path, {
wrapAsync: state.addHelper("asyncToGenerator"),
});
remapAsyncToGenerator(
path,
{ wrapAsync: state.addHelper("asyncToGenerator") },
newableArrowFunctions,
);
},
},
};
Expand Down
@@ -0,0 +1 @@
async () => 2;
@@ -0,0 +1,6 @@
{
"plugins": ["external-helpers", "transform-async-to-generator"],
"assumptions": {
"newableArrowFunctions": false
}
}
@@ -0,0 +1,7 @@
var _this = this;

/*#__PURE__*/
babelHelpers.asyncToGenerator(function* () {
babelHelpers.newArrowCheck(this, _this);
return 2;
});
@@ -0,0 +1,12 @@
{
"plugins": [
"external-helpers",
[
"transform-async-to-generator",
{ "module": "bluebird", "method": "coroutine" }
]
],
"assumptions": {
"newableArrowFunctions": true
}
}
1 change: 1 addition & 0 deletions packages/babel-plugin-transform-classes/src/index.js
Expand Up @@ -68,6 +68,7 @@ export default declare((api, options) => {
if (path.isCallExpression()) {
annotateAsPure(path);
if (path.get("callee").isArrowFunctionExpression()) {
// This is an IIFE, so we don't need to worry about the newableArrowFunctions assumption
path.get("callee").arrowFunctionToExpression();
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-plugin-transform-parameters/src/index.js
Expand Up @@ -8,6 +8,7 @@ export default declare((api, options) => {

const ignoreFunctionLength =
api.assumption("ignoreFunctionLength") ?? options.loose;
const newableArrowFunctions = api.assumption("newableArrowFunctions");

return {
name: "transform-parameters",
Expand All @@ -21,7 +22,7 @@ export default declare((api, options) => {
.some(param => param.isRestElement() || param.isAssignmentPattern())
) {
// default/rest visitors require access to `arguments`, so it cannot be an arrow
path.arrowFunctionToExpression();
path.arrowFunctionToExpression({ newableArrowFunctions });
}

const convertedRest = convertFunctionRest(path);
Expand Down
2 changes: 2 additions & 0 deletions packages/babel-plugin-transform-parameters/src/params.js
Expand Up @@ -206,6 +206,8 @@ export default function convertFunctionParams(
// sure that we correctly handle this and arguments.
const bodyPath = path.get("body.body");
const arrowPath = bodyPath[bodyPath.length - 1].get("argument.callee");

// This is an IIFE, so we don't need to worry about the newableArrowFunctions assumption
arrowPath.arrowFunctionToExpression();

arrowPath.node.generator = path.node.generator;
Expand Down
17 changes: 11 additions & 6 deletions packages/babel-traverse/src/path/conversion.js
Expand Up @@ -72,6 +72,7 @@ export function ensureBlock() {
/**
* Keeping this for backward-compatibility. You should use arrowFunctionToExpression() for >=7.x.
*/
// TODO(Babel 8): Remove this
export function arrowFunctionToShadowed() {
if (!this.isArrowFunctionExpression()) return;

Expand Down Expand Up @@ -103,7 +104,10 @@ export function unwrapFunctionEnvironment() {
*/
export function arrowFunctionToExpression({
allowInsertArrow = true,
/** @deprecated Use `newableArrowFunctions` instead */
specCompliant = false,
// TODO(Babel 8): Consider defaulting to `false` for spec compliancy
newableArrowFunctions = !specCompliant,
} = {}) {
if (!this.isArrowFunctionExpression()) {
throw this.buildCodeFrameError(
Expand All @@ -113,13 +117,13 @@ export function arrowFunctionToExpression({

const thisBinding = hoistFunctionEnvironment(
this,
specCompliant,
newableArrowFunctions,
allowInsertArrow,
);

this.ensureBlock();
this.node.type = "FunctionExpression";
if (specCompliant) {
if (!newableArrowFunctions) {
const checkBinding = thisBinding
? null
: this.parentPath.scope.generateUidIdentifier("arrowCheckId");
Expand Down Expand Up @@ -160,7 +164,8 @@ export function arrowFunctionToExpression({
*/
function hoistFunctionEnvironment(
fnPath,
specCompliant = false,
// TODO(Babel 8): Consider defaulting to `false` for spec compliancy
newableArrowFunctions = true,
allowInsertArrow = true,
) {
const thisEnvFn = fnPath.findParent(p => {
Expand Down Expand Up @@ -298,11 +303,11 @@ function hoistFunctionEnvironment(

// Convert all "this" references in the arrow to point at the alias.
let thisBinding;
if (thisPaths.length > 0 || specCompliant) {
if (thisPaths.length > 0 || !newableArrowFunctions) {
thisBinding = getThisBinding(thisEnvFn, inConstructor);

if (
!specCompliant ||
newableArrowFunctions ||
// In subclass constructors, still need to rewrite because "this" can't be bound in spec mode
// because it might not have been initialized yet.
(inConstructor && hasSuperClass(thisEnvFn))
Expand All @@ -316,7 +321,7 @@ function hoistFunctionEnvironment(
thisChild.replaceWith(thisRef);
});

if (specCompliant) thisBinding = null;
if (!newableArrowFunctions) thisBinding = null;
}
}

Expand Down
2 changes: 2 additions & 0 deletions packages/babel-traverse/src/path/replacement.js
Expand Up @@ -260,6 +260,8 @@ export function replaceExpressionWithStatements(nodes: Array<Object>) {
}

const callee = this.get("callee");

// This is an IIFE, so we don't need to worry about the newableArrowFunctions assumption
callee.arrowFunctionToExpression();

// (() => await xxx)() -> await (async () => await xxx)();
Expand Down