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

Optimize the use of assertThisInitialized after super() #16345

Merged
merged 3 commits into from Mar 14, 2024
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
29 changes: 13 additions & 16 deletions packages/babel-helper-replace-supers/src/index.ts
Expand Up @@ -26,12 +26,11 @@ if (!process.env.BABEL_8_BREAKING && !USE_ESM && !IS_STANDALONE) {
exports.skipAllButComputedKey = ns.skipAllButComputedKey;
}

type ThisRef =
| {
memo: t.AssignmentExpression;
this: t.Identifier;
}
| { this: t.ThisExpression };
type ThisRef = {
needAccessFirst?: boolean;
this: t.ThisExpression;
};

/**
* Creates an expression which result is the proto of objectRef.
*
Expand Down Expand Up @@ -175,21 +174,18 @@ const specHandlers: SpecHandler = {
this.isPrivateMethod,
);
return callExpression(this.file.addHelper("get"), [
// @ts-expect-error memo does not exist when this.isDerivedConstructor is false
thisRefs.memo ? sequenceExpression([thisRefs.memo, proto]) : proto,
thisRefs.needAccessFirst
? sequenceExpression([thisRefs.this, proto])
: proto,
this.prop(superMember),
thisRefs.this,
]);
},

_getThisRefs(this: Handler & SpecHandler): ThisRef {
if (!this.isDerivedConstructor) {
return { this: thisExpression() };
}
const thisRef = this.scope.generateDeclaredUidIdentifier("thisSuper");
return {
memo: assignmentExpression("=", thisRef, thisExpression()),
this: cloneNode(thisRef),
needAccessFirst: this.isDerivedConstructor,
this: thisExpression(),
};
},

Expand All @@ -206,8 +202,9 @@ const specHandlers: SpecHandler = {
this.isPrivateMethod,
);
return callExpression(this.file.addHelper("set"), [
// @ts-expect-error memo does not exist when this.isDerivedConstructor is false
thisRefs.memo ? sequenceExpression([thisRefs.memo, proto]) : proto,
thisRefs.needAccessFirst
? sequenceExpression([thisRefs.this, proto])
: proto,
this.prop(superMember),
value,
thisRefs.this,
Expand Down
Expand Up @@ -14,11 +14,11 @@ let Hello = /*#__PURE__*/function () {
let Outer = /*#__PURE__*/function (_Hello) {
function Outer() {
let _computedKey;
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, Outer);
_this = babelHelpers.callSuper(this, Outer);
var _A = /*#__PURE__*/new WeakMap();
_computedKey = babelHelpers.toPropertyKey(babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper));
_computedKey = babelHelpers.toPropertyKey(babelHelpers.get((_this, babelHelpers.getPrototypeOf(Outer.prototype)), "toString", _this).call(_this));
let Inner = /*#__PURE__*/function (_computedKey4, _computedKey5) {
function Inner() {
babelHelpers.classCallCheck(this, Inner);
Expand Down
Expand Up @@ -15,7 +15,7 @@ let Outer = /*#__PURE__*/function (_Hello) {
function Outer() {
var _Inner;
let _init_hello, _init_extra_hello;
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, Outer);
_this = babelHelpers.callSuper(this, Outer);
let Inner = /*#__PURE__*/babelHelpers.createClass(function Inner() {
Expand All @@ -24,7 +24,7 @@ let Outer = /*#__PURE__*/function (_Hello) {
_init_extra_hello(this);
});
_Inner = Inner;
[_init_hello, _init_extra_hello] = babelHelpers.applyDecs2311(_Inner, [], [[babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Outer.prototype)), "dec", _thisSuper), 0, "hello"]]).e;
[_init_hello, _init_extra_hello] = babelHelpers.applyDecs2311(_Inner, [], [[babelHelpers.get((_this, babelHelpers.getPrototypeOf(Outer.prototype)), "dec", _this), 0, "hello"]]).e;
return babelHelpers.possibleConstructorReturn(_this, new Inner());
}
babelHelpers.inherits(Outer, _Hello);
Expand Down
Expand Up @@ -14,10 +14,10 @@ let Hello = /*#__PURE__*/function () {
let Outer = /*#__PURE__*/function (_Hello) {
function Outer() {
let _babelHelpers$get$cal;
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, Outer);
_this = babelHelpers.callSuper(this, Outer);
_babelHelpers$get$cal = babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Outer.prototype)), "toString", _thisSuper).call(_thisSuper);
_babelHelpers$get$cal = babelHelpers.get((_this, babelHelpers.getPrototypeOf(Outer.prototype)), "toString", _this).call(_this);
let Inner = /*#__PURE__*/babelHelpers.createClass(function Inner() {
babelHelpers.classCallCheck(this, Inner);
babelHelpers.defineProperty(this, _babelHelpers$get$cal, 'hello');
Expand Down
Expand Up @@ -16,7 +16,7 @@ var Bar = /*#__PURE__*/function (_Foo2) {
var _this;
babelHelpers.classCallCheck(this, Bar);
_this = babelHelpers.callSuper(this, Bar, [...args]);
Object.defineProperty(babelHelpers.assertThisInitialized(_this), _prop2, {
Object.defineProperty(_this, _prop2, {
writable: true,
value: "bar"
});
Expand Down
Expand Up @@ -6,7 +6,7 @@ let Child = /*#__PURE__*/function (_Parent) {
var _this;
babelHelpers.classCallCheck(this, Child);
_this = babelHelpers.callSuper(this, Child);
Object.defineProperty(babelHelpers.assertThisInitialized(_this), _scopedFunctionWithThis, {
Object.defineProperty(_this, _scopedFunctionWithThis, {
writable: true,
value: function () {
_this.name = {};
Expand Down
Expand Up @@ -20,7 +20,7 @@ var Foo = /*#__PURE__*/function () {
var _this;
babelHelpers.classCallCheck(this, Nested);
_this = babelHelpers.callSuper(this, Nested, [...args]);
Object.defineProperty(babelHelpers.assertThisInitialized(_this), _foo2, {
Object.defineProperty(_this, _foo2, {
writable: true,
value: 3
});
Expand Down
Expand Up @@ -19,7 +19,7 @@ var Foo = /*#__PURE__*/function () {
var _this;
babelHelpers.classCallCheck(this, Nested);
_this = babelHelpers.callSuper(this, Nested, [...args]);
Object.defineProperty(babelHelpers.assertThisInitialized(_this), _foo2, {
Object.defineProperty(_this, _foo2, {
writable: true,
value: 3
});
Expand Down
Expand Up @@ -6,7 +6,7 @@ var Foo = /*#__PURE__*/function (_Bar) {
var _this;
babelHelpers.classCallCheck(this, Foo);
_this = babelHelpers.callSuper(this, Foo);
Object.defineProperty(babelHelpers.assertThisInitialized(_this), _bar, {
Object.defineProperty(_this, _bar, {
writable: true,
value: "foo"
});
Expand Down
Expand Up @@ -13,7 +13,7 @@ let Bar = /*#__PURE__*/function (_Foo2) {
var _this;
babelHelpers.classCallCheck(this, Bar);
_this = babelHelpers.callSuper(this, Bar, [...args]);
babelHelpers.classPrivateFieldInitSpec(babelHelpers.assertThisInitialized(_this), _prop2, "bar");
babelHelpers.classPrivateFieldInitSpec(_this, _prop2, "bar");
return _this;
}
babelHelpers.inherits(Bar, _Foo2);
Expand Down
Expand Up @@ -6,7 +6,7 @@ let Child = /*#__PURE__*/function (_Parent) {
var _this;
babelHelpers.classCallCheck(this, Child);
_this = babelHelpers.callSuper(this, Child);
babelHelpers.classPrivateFieldInitSpec(babelHelpers.assertThisInitialized(_this), _scopedFunctionWithThis, () => {
babelHelpers.classPrivateFieldInitSpec(_this, _scopedFunctionWithThis, () => {
_this.name = {};
});
return _this;
Expand Down
Expand Up @@ -17,7 +17,7 @@ let Foo = /*#__PURE__*/function () {
var _this;
babelHelpers.classCallCheck(this, Nested);
_this = babelHelpers.callSuper(this, Nested, [...args]);
babelHelpers.classPrivateFieldInitSpec(babelHelpers.assertThisInitialized(_this), _foo2, 3);
babelHelpers.classPrivateFieldInitSpec(_this, _foo2, 3);
return _this;
}
babelHelpers.inherits(Nested, _ref);
Expand Down
Expand Up @@ -16,7 +16,7 @@ let Foo = /*#__PURE__*/function () {
var _this;
babelHelpers.classCallCheck(this, Nested);
_this = babelHelpers.callSuper(this, Nested, [...args]);
babelHelpers.classPrivateFieldInitSpec(babelHelpers.assertThisInitialized(_this), _foo2, 3);
babelHelpers.classPrivateFieldInitSpec(_this, _foo2, 3);
return _this;
}
babelHelpers.inherits(Nested, _ref);
Expand Down
Expand Up @@ -16,10 +16,10 @@ let B = /*#__PURE__*/function (_A) {
"use strict";

function B(...args) {
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, B);
_this = babelHelpers.callSuper(this, B, [...args]);
babelHelpers.classPrivateFieldInitSpec(babelHelpers.assertThisInitialized(_this), _foo, babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(B.prototype)), "foo", _thisSuper).call(_thisSuper));
babelHelpers.classPrivateFieldInitSpec(_this, _foo, babelHelpers.get((_this, babelHelpers.getPrototypeOf(B.prototype)), "foo", _this).call(_this));
return _this;
}
babelHelpers.inherits(B, _A);
Expand Down
Expand Up @@ -6,7 +6,7 @@ let Foo = /*#__PURE__*/function (_Bar) {
var _this;
babelHelpers.classCallCheck(this, Foo);
_this = babelHelpers.callSuper(this, Foo);
babelHelpers.classPrivateFieldInitSpec(babelHelpers.assertThisInitialized(_this), _bar, "foo");
babelHelpers.classPrivateFieldInitSpec(_this, _bar, "foo");
return _this;
}
babelHelpers.inherits(Foo, _Bar);
Expand Down
Expand Up @@ -15,10 +15,10 @@ var B = /*#__PURE__*/function (_A) {
"use strict";

function B(...args) {
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, B);
_this = babelHelpers.callSuper(this, B, [...args]);
_this.foo = babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(B.prototype)), "foo", _thisSuper).call(_thisSuper);
_this.foo = babelHelpers.get((_this, babelHelpers.getPrototypeOf(B.prototype)), "foo", _this).call(_this);
return _this;
}
babelHelpers.inherits(B, _A);
Expand Down
Expand Up @@ -5,7 +5,7 @@ var Foo = /*#__PURE__*/function (_Bar) {
var _this;
babelHelpers.classCallCheck(this, Foo);
_this = babelHelpers.callSuper(this, Foo, [...args]);
babelHelpers.defineProperty(babelHelpers.assertThisInitialized(_this), "bar", "foo");
babelHelpers.defineProperty(_this, "bar", "foo");
return _this;
}
babelHelpers.inherits(Foo, _Bar);
Expand Down
Expand Up @@ -5,7 +5,7 @@ var Child = /*#__PURE__*/function (_Parent) {
var _this;
babelHelpers.classCallCheck(this, Child);
_this = babelHelpers.callSuper(this, Child);
babelHelpers.defineProperty(babelHelpers.assertThisInitialized(_this), "scopedFunctionWithThis", function () {
babelHelpers.defineProperty(_this, "scopedFunctionWithThis", function () {
_this.name = {};
});
return _this;
Expand Down
Expand Up @@ -15,10 +15,10 @@ var B = /*#__PURE__*/function (_A) {
"use strict";

function B(...args) {
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, B);
_this = babelHelpers.callSuper(this, B, [...args]);
babelHelpers.defineProperty(babelHelpers.assertThisInitialized(_this), "foo", babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(B.prototype)), "foo", _thisSuper).call(_thisSuper));
babelHelpers.defineProperty(_this, "foo", babelHelpers.get((_this, babelHelpers.getPrototypeOf(B.prototype)), "foo", _this).call(_this));
return _this;
}
babelHelpers.inherits(B, _A);
Expand Down
Expand Up @@ -5,7 +5,7 @@ var Foo = /*#__PURE__*/function (_Bar) {
var _this;
babelHelpers.classCallCheck(this, Foo);
_this = babelHelpers.callSuper(this, Foo);
babelHelpers.defineProperty(babelHelpers.assertThisInitialized(_this), "bar", "foo");
babelHelpers.defineProperty(_this, "bar", "foo");
return _this;
}
babelHelpers.inherits(Foo, _Bar);
Expand Down
Expand Up @@ -5,14 +5,14 @@ var Test = /*#__PURE__*/babelHelpers.createClass(function Test() {
babelHelpers.classCallCheck(this, Test);
var Other = /*#__PURE__*/function (_Test) {
function Other() {
var _thisSuper, _this;
var _this;
babelHelpers.classCallCheck(this, Other);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = babelHelpers.callSuper(this, Other, [].concat(args));
babelHelpers.defineProperty(babelHelpers.assertThisInitialized(_this), "a", function () {
return babelHelpers.get((_thisSuper = babelHelpers.assertThisInitialized(_this), babelHelpers.getPrototypeOf(Other.prototype)), "test", _thisSuper);
babelHelpers.defineProperty(_this, "a", function () {
return babelHelpers.get((_this, babelHelpers.getPrototypeOf(Other.prototype)), "test", _this);
});
return _this;
}
Expand Down
57 changes: 43 additions & 14 deletions packages/babel-plugin-transform-classes/src/transformClass.ts
Expand Up @@ -398,10 +398,15 @@ export default function transformClass(
const path = classState.userConstructorPath;
const body = path.get("body");

const constructorBody = path.get("body");

let maxGuaranteedSuperBeforeIndex = constructorBody.node.body.length;

path.traverse(findThisesVisitor);

let thisRef = function () {
const ref = path.scope.generateDeclaredUidIdentifier("this");
maxGuaranteedSuperBeforeIndex++;
thisRef = () => t.cloneNode(ref);
return ref;
};
Expand All @@ -413,15 +418,6 @@ export default function transformClass(
);
};

for (const thisPath of classState.superThises) {
const { node, parentPath } = thisPath;
if (parentPath.isMemberExpression({ object: node })) {
thisPath.replaceWith(thisRef());
continue;
}
thisPath.replaceWith(buildAssertThisInitialized());
}

const bareSupers: NodePath<t.CallExpression>[] = [];
path.traverse(
traverse.visitors.merge([
Expand All @@ -437,15 +433,18 @@ export default function transformClass(
]),
);

let guaranteedSuperBeforeFinish = !!bareSupers.length;

for (const bareSuper of bareSupers) {
wrapSuperCall(bareSuper, classState.superName, thisRef, body);

if (guaranteedSuperBeforeFinish) {
if (maxGuaranteedSuperBeforeIndex >= 0) {
let lastParentPath: NodePath;
bareSuper.find(function (parentPath) {
// hit top so short circuit
if (parentPath === path) {
if (parentPath === constructorBody) {
maxGuaranteedSuperBeforeIndex = Math.min(
maxGuaranteedSuperBeforeIndex,
lastParentPath.key as number,
);
return true;
}

Expand All @@ -454,13 +453,40 @@ export default function transformClass(
parentPath.isConditional() ||
parentPath.isArrowFunctionExpression()
) {
guaranteedSuperBeforeFinish = false;
maxGuaranteedSuperBeforeIndex = -1;
return true;
}

lastParentPath = parentPath;
});
}
}

for (const thisPath of classState.superThises) {
const { node, parentPath } = thisPath;
if (parentPath.isMemberExpression({ object: node })) {
thisPath.replaceWith(thisRef());
continue;
}

let thisIndex: number;
thisPath.find(function (parentPath) {
if (parentPath.parentPath === constructorBody) {
thisIndex = parentPath.key as number;
return true;
}
});

if (
maxGuaranteedSuperBeforeIndex != -1 &&
thisIndex > maxGuaranteedSuperBeforeIndex
) {
thisPath.replaceWith(thisRef());
} else {
thisPath.replaceWith(buildAssertThisInitialized());
}
}

let wrapReturn;

if (classState.isLoose) {
Expand All @@ -486,6 +512,9 @@ export default function transformClass(
// if we have a return as the last node in the body then we've already caught that
// return
const bodyPaths = body.get("body");
const guaranteedSuperBeforeFinish =
maxGuaranteedSuperBeforeIndex !== -1 &&
maxGuaranteedSuperBeforeIndex < bodyPaths.length;
if (!bodyPaths.length || !bodyPaths.pop().isReturnStatement()) {
body.pushContainer(
"body",
Expand Down
Expand Up @@ -7,11 +7,11 @@ var Test = /*#__PURE__*/function (_Foo) {
babelHelpers.classCallCheck(this, Test);
woops.super.test();
_this = babelHelpers.callSuper(this, Test);
_Foo.prototype.test.call(babelHelpers.assertThisInitialized(_this));
_Foo.prototype.test.call(_this);
_this = babelHelpers.callSuper(this, Test, arguments);
_this = babelHelpers.callSuper(this, Test, ["test"].concat(Array.prototype.slice.call(arguments)));
_Foo.prototype.test.apply(babelHelpers.assertThisInitialized(_this), arguments);
(_Foo$prototype$test = _Foo.prototype.test).call.apply(_Foo$prototype$test, [babelHelpers.assertThisInitialized(_this), "test"].concat(Array.prototype.slice.call(arguments)));
_Foo.prototype.test.apply(_this, arguments);
(_Foo$prototype$test = _Foo.prototype.test).call.apply(_Foo$prototype$test, [_this, "test"].concat(Array.prototype.slice.call(arguments)));
return _this;
}
babelHelpers.inherits(Test, _Foo);
Expand Down