Skip to content

Commit

Permalink
Implement pureGetters assumption (#12504)
Browse files Browse the repository at this point in the history
* feat: allow pureGetters in assumptions

* feat: respect pureGetters assumption
  • Loading branch information
JLHwung authored and nicolo-ribaudo committed Jan 12, 2021
1 parent 5ab11fc commit 169a413
Show file tree
Hide file tree
Showing 9 changed files with 120 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/babel-core/src/config/validation/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ export const assumptionsNames = new Set<string>([
"ignoreToPrimitiveHint",
"iterableIsArray",
"mutableTemplateObject",
"pureGetters",
"setClassMethods",
"setComputedProperties",
"setPublicClassFields",
Expand Down
12 changes: 7 additions & 5 deletions packages/babel-plugin-proposal-optional-chaining/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default declare((api, options) => {

const { loose = false } = options;
const noDocumentAll = api.assumption("noDocumentAll") ?? loose;
const pureGetters = api.assumption("pureGetters") ?? loose;

function isSimpleMemberExpression(expression) {
expression = skipTransparentExprWrappers(expression);
Expand Down Expand Up @@ -131,8 +132,8 @@ export default declare((api, options) => {
check = ref = chain;
// `eval?.()` is an indirect eval call transformed to `(0,eval)()`
node[replaceKey] = t.sequenceExpression([t.numericLiteral(0), ref]);
} else if (loose && isCall && isSimpleMemberExpression(chain)) {
// If we are using a loose transform (avoiding a Function#call) and we are at the call,
} else if (pureGetters && isCall && isSimpleMemberExpression(chain)) {
// If we assume getters are pure (avoiding a Function#call) and we are at the call,
// 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 = chainWithTypes;
Expand All @@ -157,7 +158,7 @@ export default declare((api, options) => {
// Ensure call expressions have the proper `this`
// `foo.bar()` has context `foo`.
if (isCall && t.isMemberExpression(chain)) {
if (loose && isSimpleMemberExpression(chain)) {
if (pureGetters && isSimpleMemberExpression(chain)) {
// 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 = chainWithTypes;
Expand Down Expand Up @@ -192,8 +193,9 @@ export default declare((api, options) => {
replacementPath.get("object"),
).node;
let baseRef;
if (!loose || !isSimpleMemberExpression(object)) {
// memoize the context object in non-loose mode
if (!pureGetters || !isSimpleMemberExpression(object)) {
// memoize the context object when getters are not always pure
// or the object is not a simple member expression
// `(a?.b.c)()` to `(a == null ? undefined : (_a$b = a.b).c.bind(_a$b))()`
baseRef = scope.maybeGenerateMemoised(object);
if (baseRef) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
foo?.(foo);

foo?.bar()

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

foo?.bar?.(foo.bar, true)

foo?.().bar

foo?.()?.bar

foo.bar?.().baz

foo.bar?.()?.baz

foo?.bar?.().baz

foo?.bar?.()?.baz
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var _foo, _foo2, _foo3, _foo$bar, _foo4, _foo5, _foo5$bar;

foo === null || foo === void 0 ? void 0 : foo(foo);
(_foo = foo) === null || _foo === void 0 ? void 0 : _foo.bar();
foo.bar === null || foo.bar === void 0 ? void 0 : foo.bar(foo.bar, false);
(_foo2 = foo) === null || _foo2 === void 0 ? void 0 : _foo2.bar === null || _foo2.bar === void 0 ? void 0 : _foo2.bar(foo.bar, true);
foo === null || foo === void 0 ? void 0 : foo().bar;
foo === null || foo === void 0 ? void 0 : (_foo3 = foo()) === null || _foo3 === void 0 ? void 0 : _foo3.bar;
foo.bar === null || foo.bar === void 0 ? void 0 : foo.bar().baz;
foo.bar === null || foo.bar === void 0 ? void 0 : (_foo$bar = foo.bar()) === null || _foo$bar === void 0 ? void 0 : _foo$bar.baz;
(_foo4 = foo) === null || _foo4 === void 0 ? void 0 : _foo4.bar === null || _foo4.bar === void 0 ? void 0 : _foo4.bar().baz;
(_foo5 = foo) === null || _foo5 === void 0 ? void 0 : _foo5.bar === null || _foo5.bar === void 0 ? void 0 : (_foo5$bar = _foo5.bar()) === null || _foo5$bar === void 0 ? void 0 : _foo5$bar.baz;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
function test(foo) {
foo?.bar;

foo?.bar?.baz;

foo?.(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)

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

foo?.bar?.baz(foo.bar, true)

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

foo?.bar?.baz?.(foo.bar, true)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function test(foo) {
var _foo$bar, _foo$get, _foo$bar2, _foo$bar3, _foo$bar$baz, _foo$bar4, _foo$bar$baz2, _foo$bar5, _foo$bar6, _foo$bar7, _foo$bar8, _foo$bar9;

foo === null || foo === void 0 ? void 0 : foo.bar;
foo === null || foo === void 0 ? void 0 : (_foo$bar = foo.bar) === null || _foo$bar === void 0 ? void 0 : _foo$bar.baz;
foo === null || foo === void 0 ? void 0 : foo(foo);
foo === null || foo === void 0 ? void 0 : foo.bar();
(_foo$get = foo.get(bar)) === null || _foo$get === void 0 ? void 0 : _foo$get();
(_foo$bar2 = foo.bar()) === null || _foo$bar2 === void 0 ? void 0 : _foo$bar2();
(_foo$bar3 = foo[bar]()) === null || _foo$bar3 === void 0 ? void 0 : _foo$bar3();
(_foo$bar$baz = (_foo$bar4 = foo.bar()).baz) === null || _foo$bar$baz === void 0 ? void 0 : _foo$bar$baz.call(_foo$bar4);
(_foo$bar$baz2 = (_foo$bar5 = foo[bar]()).baz) === null || _foo$bar$baz2 === void 0 ? void 0 : _foo$bar$baz2.call(_foo$bar5);
foo.bar === null || foo.bar === void 0 ? void 0 : foo.bar(foo.bar, false);
foo === null || foo === void 0 ? void 0 : foo.bar === null || foo.bar === void 0 ? void 0 : foo.bar(foo.bar, true);
(_foo$bar6 = foo.bar) === null || _foo$bar6 === void 0 ? void 0 : _foo$bar6.baz(foo.bar, false);
foo === null || foo === void 0 ? void 0 : (_foo$bar7 = foo.bar) === null || _foo$bar7 === void 0 ? void 0 : _foo$bar7.baz(foo.bar, true);
(_foo$bar8 = foo.bar) === null || _foo$bar8 === void 0 ? void 0 : _foo$bar8.baz === null || _foo$bar8.baz === void 0 ? void 0 : _foo$bar8.baz(foo.bar, false);
foo === null || foo === void 0 ? void 0 : (_foo$bar9 = foo.bar) === null || _foo$bar9 === void 0 ? void 0 : _foo$bar9.baz === null || _foo$bar9.baz === void 0 ? void 0 : _foo$bar9.baz(foo.bar, true);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"plugins": ["proposal-optional-chaining"],
"assumptions": {
"pureGetters": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"use strict";
class Base {
method() {
return 'Hello!';
}
}

class Derived extends Base {
method() {
return super.method?.()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use strict";

class Base {
method() {
return 'Hello!';
}

}

class Derived extends Base {
method() {
return super.method === null || super.method === void 0 ? void 0 : super.method();
}

}

0 comments on commit 169a413

Please sign in to comment.