Skip to content

Commit

Permalink
feat: optimize for private member get
Browse files Browse the repository at this point in the history
  • Loading branch information
JLHwung committed Nov 2, 2020
1 parent 1a854d4 commit 645910b
Show file tree
Hide file tree
Showing 10 changed files with 991 additions and 26 deletions.
113 changes: 87 additions & 26 deletions packages/babel-helper-member-expression-to-functions/src/index.js
Expand Up @@ -29,6 +29,38 @@ class AssignmentMemoiser {
}
}

/**
* Test if a NodePath will be cast to boolean when evaluated.
*
* @example
* // returns true
* const nodePathAQDotB = NodePath("if (a?.#b) {}").get("test"); // a?.#b
* willPathCastToBoolean(nodePathAQDotB)
* @example
* // returns false
* willPathCastToBoolean(NodePath("a?.#b"))
* @todo Respect transparent expression wrappers
* @see {@link packages/babel-plugin-proposal-optional-chaining/src/index.js}
* @param {NodePath} path
* @returns {boolean}
*/

function willPathCastToBoolean(path: NodePath): boolean {
const maybeWrapped = path;
const { node, parentPath } = maybeWrapped;
if (parentPath.isLogicalExpression()) {
const { operator } = parentPath.node;
if (operator === "&&" || operator === "||") {
return willPathCastToBoolean(parentPath);
}
}
return (
parentPath.isConditional({ test: node }) ||
parentPath.isUnaryExpression({ operator: "!" }) ||
parentPath.isLoop({ test: node })
);
}

function toNonOptional(path, base) {
const { node } = path;
if (path.isOptionalMemberExpression()) {
Expand Down Expand Up @@ -129,6 +161,8 @@ const handle = {
return;
}

const willEndPathCastToBoolean = willPathCastToBoolean(endPath);

const rootParentPath = endPath.parentPath;
if (
rootParentPath.isUpdateExpression({ argument: node }) ||
Expand Down Expand Up @@ -238,33 +272,60 @@ const handle = {
regular = endParentPath.node;
}

replacementPath.replaceWith(
t.conditionalExpression(
t.logicalExpression(
"||",
t.binaryExpression(
"===",
baseNeedsMemoised
? t.assignmentExpression(
"=",
t.cloneNode(baseRef),
t.cloneNode(startingNode),
)
: t.cloneNode(baseRef),
t.nullLiteral(),
),
t.binaryExpression(
"===",
t.cloneNode(baseRef),
scope.buildUndefinedNode(),
),
if (willEndPathCastToBoolean) {
const nonNullishCheck = t.logicalExpression(
"&&",
t.binaryExpression(
"!==",
baseNeedsMemoised
? t.assignmentExpression(
"=",
t.cloneNode(baseRef),
t.cloneNode(startingNode),
)
: t.cloneNode(baseRef),
t.nullLiteral(),
),
isDeleteOperation
? t.booleanLiteral(true)
: scope.buildUndefinedNode(),
regular,
),
);
t.binaryExpression(
"!==",
t.cloneNode(baseRef),
scope.buildUndefinedNode(),
),
);
replacementPath.replaceWith(
t.logicalExpression("&&", nonNullishCheck, regular),
);
} else {
// todo: respect assumptions.noDocumentAll when assumptions are implemented
const nullishCheck = t.logicalExpression(
"||",
t.binaryExpression(
"===",
baseNeedsMemoised
? t.assignmentExpression(
"=",
t.cloneNode(baseRef),
t.cloneNode(startingNode),
)
: t.cloneNode(baseRef),
t.nullLiteral(),
),
t.binaryExpression(
"===",
t.cloneNode(baseRef),
scope.buildUndefinedNode(),
),
);
replacementPath.replaceWith(
t.conditionalExpression(
nullishCheck,
isDeleteOperation
? t.booleanLiteral(true)
: scope.buildUndefinedNode(),
regular,
),
);
}

// context and isDeleteOperation can not be both truthy
if (context) {
Expand Down
@@ -0,0 +1,108 @@
class C {
static #a = {
b: {
c: {
d: 2,
},
},
};
static testIf(o) {
if (o?.#a.b.c.d) {
return true;
}
return false;
}
static testConditional(o) {
return o?.#a.b?.c.d ? true : false;
}
static testLoop(o) {
while (o?.#a.b.c.d) {
for (; o?.#a.b.c?.d; ) {
let i = 0;
do {
i++;
if (i === 2) {
return true;
}
} while (o?.#a.b?.c.d);
}
}
return false;
}
static testNegate(o) {
return !!o?.#a.b?.c.d;
}
static testIfDeep(o) {
if (o.obj?.#a.b?.c.d) {
return true;
}
return false;
}
static testConditionalDeep(o) {
return o.obj?.#a.b?.c.d ? true : false;
}
static testLoopDeep(o) {
while (o.obj?.#a.b.c.d) {
for (; o.obj?.#a.b.c?.d; ) {
let i = 0;
do {
i++;
if (i === 2) {
return true;
}
} while (o.obj?.#a.b?.c.d);
}
}
return false;
}
static testNegateDeep(o) {
return !!o.obj?.#a.b?.c.d;
}

static testLogicalInIf(o) {
if (o?.#a.b?.c.d && o?.#a?.b.c.d) {
return true;
}
return false;
}

static testLogicalInReturn(o) {
return o?.#a.b?.c.d && o?.#a?.b.c.d;
}

static test() {
const c = C;
expect(C.testIf(c)).toBe(true);
expect(C.testConditional(c)).toBe(true);
expect(C.testLoop(c)).toBe(true);
expect(C.testNegate(c)).toBe(true);

expect(C.testIfDeep({ obj: c })).toBe(true);
expect(C.testConditionalDeep({ obj: c })).toBe(true);
expect(C.testLoopDeep({ obj: c })).toBe(true);
expect(C.testNegateDeep({ obj: c })).toBe(true);

expect(C.testLogicalInIf(c)).toBe(true);
expect(C.testLogicalInReturn(c)).toBe(2);
}

static testNullish() {
for (const n of [null, undefined]) {
expect(C.testIf(n)).toBe(false);
expect(C.testConditional(n)).toBe(false);
expect(C.testLoop(n)).toBe(false);
expect(C.testNegate(n)).toBe(false);

expect(C.testIfDeep({ obj: n })).toBe(false);
expect(C.testConditionalDeep({ obj: n })).toBe(false);
expect(C.testLoopDeep({ obj: n })).toBe(false);
expect(C.testNegateDeep({ obj: n })).toBe(false);

expect(C.testLogicalInIf(n)).toBe(false);
expect(C.testLogicalInReturn(n)).toBe(undefined);
}
}
}

C.test();
C.testNullish();
@@ -0,0 +1,75 @@
class C {
static #a = {
b: {
c: {
d: 2,
},
},
};
static testIf(o) {
if (o?.#a.b.c.d) {
return true;
}
return false;
}
static testConditional(o) {
return o?.#a.b?.c.d ? true : false;
}
static testLoop(o) {
while (o?.#a.b.c.d) {
for (; o?.#a.b.c?.d; ) {
let i = 0;
do {
i++;
if (i === 2) {
return true;
}
} while (o?.#a.b?.c.d);
}
}
return false;
}
static testNegate(o) {
return !!o?.#a.b?.c.d;
}
static testIfDeep(o) {
if (o.obj?.#a.b?.c.d) {
return true;
}
return false;
}
static testConditionalDeep(o) {
return o.obj?.#a.b?.c.d ? true : false;
}
static testLoopDeep(o) {
while (o.obj?.#a.b.c.d) {
for (; o.obj?.#a.b.c?.d; ) {
let i = 0;
do {
i++;
if (i === 2) {
return true;
}
} while (o.obj?.#a.b?.c.d);
}
}
return false;
}
static testNegateDeep(o) {
return !!o.obj?.#a.b?.c.d;
}

static testLogicalInIf(o) {
if (o?.#a.b?.c.d && o?.#a?.b.c.d) {
return true;
}
return false;
}

static testLogicalInReturn(o) {
return o?.#a.b?.c.d && o?.#a?.b.c.d;
}
}

C.test();
C.testNullish();

0 comments on commit 645910b

Please sign in to comment.