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

Memoize call expressions in optional chains in loose mode #11261

Merged
merged 4 commits into from Mar 20, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
19 changes: 15 additions & 4 deletions packages/babel-plugin-proposal-optional-chaining/src/index.js
Expand Up @@ -7,6 +7,16 @@ export default declare((api, options) => {

const { loose = false } = options;

function isSimpleMemberExpression(expression) {
return (
t.isIdentifier(expression) ||
t.isSuper(expression) ||
(t.isMemberExpression(expression) &&
!expression.computed &&
isSimpleMemberExpression(expression.object))
);
}

return {
name: "proposal-optional-chaining",
inherits: syntaxOptionalChaining,
Expand Down Expand Up @@ -50,9 +60,10 @@ export default declare((api, options) => {

let ref;
let check;
if (loose && isCall) {
if (loose && isCall && isSimpleMemberExpression(chain)) {
// If we are using a loose transform (avoiding a Function#call) and we are at the call,
// we can avoid a needless memoize.
// we can avoid a needless memoize. We only do this if the callee is a simple member
// expression, to avoid multiple calls to nested call expressions.
check = ref = chain;
} else {
ref = scope.maybeGenerateMemoised(chain);
Expand All @@ -72,12 +83,12 @@ export default declare((api, options) => {

// Ensure call expressions have the proper `this`
// `foo.bar()` has context `foo`.
if (isCall && t.isMemberExpression(chain)) {
oliverdunk marked this conversation as resolved.
Show resolved Hide resolved
if (isCall && isSimpleMemberExpression(chain)) {
if (loose) {
// To avoid a Function#call, we can instead re-grab the property from the context object.
// `a.?b.?()` translates roughly to `_a.b != null && _a.b()`
node.callee = chain;
} else {
} else if (chain.object) {
oliverdunk marked this conversation as resolved.
Show resolved Hide resolved
// Otherwise, we need to memoize the context object, and change the call into a Function#call.
// `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)`
const { object } = chain;
Expand Down
Expand Up @@ -7,6 +7,14 @@ function test(foo) {

foo?.bar()

foo.get(bar)?.()

foo.bar()?.()
foo[bar]()?.()

foo.bar().baz?.()
foo[bar]().baz?.()

foo.bar?.(foo.bar, false)

foo?.bar?.(foo.bar, true)
Expand Down
@@ -1,14 +1,19 @@
function test(foo) {
var _foo$bar, _foo$bar2, _foo$bar3, _foo$bar4, _foo$bar5;
var _foo$bar, _foo$get, _foo$bar2, _foo$bar3, _foo$bar$baz, _foo$bar$baz2, _foo$bar4, _foo$bar5, _foo$bar6, _foo$bar7;

foo == null ? void 0 : foo.bar;
foo == null ? void 0 : (_foo$bar = foo.bar) == null ? void 0 : _foo$bar.baz;
foo == null ? void 0 : foo(foo);
foo == null ? void 0 : foo.bar();
(_foo$get = foo.get(bar)) == null ? void 0 : _foo$get();
(_foo$bar2 = foo.bar()) == null ? void 0 : _foo$bar2();
(_foo$bar3 = foo[bar]()) == null ? void 0 : _foo$bar3();
(_foo$bar$baz = foo.bar().baz) == null ? void 0 : _foo$bar$baz();
(_foo$bar$baz2 = foo[bar]().baz) == null ? void 0 : _foo$bar$baz2();
foo.bar == null ? void 0 : foo.bar(foo.bar, false);
foo == null ? void 0 : foo.bar == null ? void 0 : foo.bar(foo.bar, true);
(_foo$bar2 = foo.bar) == null ? void 0 : _foo$bar2.baz(foo.bar, false);
foo == null ? void 0 : (_foo$bar3 = foo.bar) == null ? void 0 : _foo$bar3.baz(foo.bar, true);
(_foo$bar4 = foo.bar) == null ? void 0 : _foo$bar4.baz == null ? void 0 : _foo$bar4.baz(foo.bar, false);
foo == null ? void 0 : (_foo$bar5 = foo.bar) == null ? void 0 : _foo$bar5.baz == null ? void 0 : _foo$bar5.baz(foo.bar, true);
(_foo$bar4 = foo.bar) == null ? void 0 : _foo$bar4.baz(foo.bar, false);
foo == null ? void 0 : (_foo$bar5 = foo.bar) == null ? void 0 : _foo$bar5.baz(foo.bar, true);
(_foo$bar6 = foo.bar) == null ? void 0 : _foo$bar6.baz == null ? void 0 : _foo$bar6.baz(foo.bar, false);
foo == null ? void 0 : (_foo$bar7 = foo.bar) == null ? void 0 : _foo$bar7.baz == null ? void 0 : _foo$bar7.baz(foo.bar, true);
}