Skip to content

Commit

Permalink
Optimize the use of assertThisInitialized after super() (#16345)
Browse files Browse the repository at this point in the history
* improve

* add tests

* remove thisSuper
  • Loading branch information
liuxingbaoyu committed Mar 14, 2024
1 parent dc891b6 commit da821bd
Show file tree
Hide file tree
Showing 54 changed files with 196 additions and 109 deletions.
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

0 comments on commit da821bd

Please sign in to comment.