From 6b4d0bff409f07ef7e4acd2cde6f7ca0dc4d28fb Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Fri, 26 Jun 2020 08:38:05 +0800 Subject: [PATCH] Avoid effect of element access expression (#39174) * Avoid effect of element access expression * Avoid unnecessary copy * Add more tests --- src/compiler/transformers/esnext.ts | 37 +++++++++------ .../logicalAssignment10(target=es2015).js | 28 ++++++++++++ ...logicalAssignment10(target=es2015).symbols | 32 +++++++++++++ .../logicalAssignment10(target=es2015).types | 45 +++++++++++++++++++ .../logicalAssignment10(target=es2020).js | 27 +++++++++++ ...logicalAssignment10(target=es2020).symbols | 32 +++++++++++++ .../logicalAssignment10(target=es2020).types | 45 +++++++++++++++++++ .../logicalAssignment10(target=esnext).js | 26 +++++++++++ ...logicalAssignment10(target=esnext).symbols | 32 +++++++++++++ .../logicalAssignment10(target=esnext).types | 45 +++++++++++++++++++ .../logicalAssignment2(target=es2015).js | 20 ++++----- .../logicalAssignment2(target=es2020).js | 20 ++++----- .../logicalAssignment3(target=es2015).js | 7 ++- .../logicalAssignment3(target=es2020).js | 7 ++- .../baselines/reference/logicalAssignment9.js | 5 +-- .../logicalAssignment/logicalAssignment10.ts | 14 ++++++ 16 files changed, 377 insertions(+), 45 deletions(-) create mode 100644 tests/baselines/reference/logicalAssignment10(target=es2015).js create mode 100644 tests/baselines/reference/logicalAssignment10(target=es2015).symbols create mode 100644 tests/baselines/reference/logicalAssignment10(target=es2015).types create mode 100644 tests/baselines/reference/logicalAssignment10(target=es2020).js create mode 100644 tests/baselines/reference/logicalAssignment10(target=es2020).symbols create mode 100644 tests/baselines/reference/logicalAssignment10(target=es2020).types create mode 100644 tests/baselines/reference/logicalAssignment10(target=esnext).js create mode 100644 tests/baselines/reference/logicalAssignment10(target=esnext).symbols create mode 100644 tests/baselines/reference/logicalAssignment10(target=esnext).types create mode 100644 tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index f38d0b7262..09b68d57e2 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -25,7 +25,7 @@ namespace ts { if (isLogicalOrCoalescingAssignmentExpression(binaryExpression)) { return transformLogicalAssignment(binaryExpression); } - // falls through + // falls through default: return visitEachChild(node, visitor, context); } @@ -37,32 +37,41 @@ namespace ts { let left = skipParentheses(visitNode(binaryExpression.left, visitor, isLeftHandSideExpression)); let assignmentTarget = left; const right = skipParentheses(visitNode(binaryExpression.right, visitor, isExpression)); + if (isAccessExpression(left)) { - const tempVariable = factory.createTempVariable(hoistVariableDeclaration); + const propertyAccessTargetSimpleCopiable = isSimpleCopiableExpression(left.expression); + const propertyAccessTarget = propertyAccessTargetSimpleCopiable ? left.expression : + factory.createTempVariable(hoistVariableDeclaration); + const propertyAccessTargetAssignment = propertyAccessTargetSimpleCopiable ? left.expression : factory.createAssignment( + propertyAccessTarget, + left.expression + ); + if (isPropertyAccessExpression(left)) { assignmentTarget = factory.createPropertyAccessExpression( - tempVariable, + propertyAccessTarget, left.name ); left = factory.createPropertyAccessExpression( - factory.createAssignment( - tempVariable, - left.expression - ), + propertyAccessTargetAssignment, left.name ); } else { + const elementAccessArgumentSimpleCopiable = isSimpleCopiableExpression(left.argumentExpression); + const elementAccessArgument = elementAccessArgumentSimpleCopiable ? left.argumentExpression : + factory.createTempVariable(hoistVariableDeclaration); + assignmentTarget = factory.createElementAccessExpression( - tempVariable, - left.argumentExpression + propertyAccessTarget, + elementAccessArgument ); left = factory.createElementAccessExpression( - factory.createAssignment( - tempVariable, - left.expression - ), - left.argumentExpression + propertyAccessTargetAssignment, + elementAccessArgumentSimpleCopiable ? left.argumentExpression : factory.createAssignment( + elementAccessArgument, + left.argumentExpression + ) ); } } diff --git a/tests/baselines/reference/logicalAssignment10(target=es2015).js b/tests/baselines/reference/logicalAssignment10(target=es2015).js new file mode 100644 index 0000000000..b002f9baae --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=es2015).js @@ -0,0 +1,28 @@ +//// [logicalAssignment10.ts] +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +const oobj = { + obj +} + +obj[incr()] ??= incr(); +oobj["obj"][incr()] ??= incr(); + + +//// [logicalAssignment10.js] +var _a, _b; +var _c, _d, _e; +var count = 0; +var obj = {}; +function incr() { + return ++count; +} +const oobj = { + obj +}; +(_a = obj[_c = incr()]) !== null && _a !== void 0 ? _a : (obj[_c] = incr()); +(_b = (_d = oobj["obj"])[_e = incr()]) !== null && _b !== void 0 ? _b : (_d[_e] = incr()); diff --git a/tests/baselines/reference/logicalAssignment10(target=es2015).symbols b/tests/baselines/reference/logicalAssignment10(target=es2015).symbols new file mode 100644 index 0000000000..d9a4e30998 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=es2015).symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts === +var count = 0; +>count : Symbol(count, Decl(logicalAssignment10.ts, 0, 3)) + +var obj = {}; +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 1, 3)) + +function incr() { +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + + return ++count; +>count : Symbol(count, Decl(logicalAssignment10.ts, 0, 3)) +} + +const oobj = { +>oobj : Symbol(oobj, Decl(logicalAssignment10.ts, 6, 5)) + + obj +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 6, 14)) +} + +obj[incr()] ??= incr(); +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 1, 3)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + +oobj["obj"][incr()] ??= incr(); +>oobj : Symbol(oobj, Decl(logicalAssignment10.ts, 6, 5)) +>"obj" : Symbol(obj, Decl(logicalAssignment10.ts, 6, 14)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + diff --git a/tests/baselines/reference/logicalAssignment10(target=es2015).types b/tests/baselines/reference/logicalAssignment10(target=es2015).types new file mode 100644 index 0000000000..d62c3c6710 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=es2015).types @@ -0,0 +1,45 @@ +=== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts === +var count = 0; +>count : number +>0 : 0 + +var obj = {}; +>obj : {} +>{} : {} + +function incr() { +>incr : () => number + + return ++count; +>++count : number +>count : number +} + +const oobj = { +>oobj : { obj: {}; } +>{ obj} : { obj: {}; } + + obj +>obj : {} +} + +obj[incr()] ??= incr(); +>obj[incr()] ??= incr() : any +>obj[incr()] : error +>obj : {} +>incr() : number +>incr : () => number +>incr() : number +>incr : () => number + +oobj["obj"][incr()] ??= incr(); +>oobj["obj"][incr()] ??= incr() : any +>oobj["obj"][incr()] : error +>oobj["obj"] : {} +>oobj : { obj: {}; } +>"obj" : "obj" +>incr() : number +>incr : () => number +>incr() : number +>incr : () => number + diff --git a/tests/baselines/reference/logicalAssignment10(target=es2020).js b/tests/baselines/reference/logicalAssignment10(target=es2020).js new file mode 100644 index 0000000000..773d395100 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=es2020).js @@ -0,0 +1,27 @@ +//// [logicalAssignment10.ts] +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +const oobj = { + obj +} + +obj[incr()] ??= incr(); +oobj["obj"][incr()] ??= incr(); + + +//// [logicalAssignment10.js] +var _a, _b, _c; +var count = 0; +var obj = {}; +function incr() { + return ++count; +} +const oobj = { + obj +}; +obj[_a = incr()] ?? (obj[_a] = incr()); +(_b = oobj["obj"])[_c = incr()] ?? (_b[_c] = incr()); diff --git a/tests/baselines/reference/logicalAssignment10(target=es2020).symbols b/tests/baselines/reference/logicalAssignment10(target=es2020).symbols new file mode 100644 index 0000000000..d9a4e30998 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=es2020).symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts === +var count = 0; +>count : Symbol(count, Decl(logicalAssignment10.ts, 0, 3)) + +var obj = {}; +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 1, 3)) + +function incr() { +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + + return ++count; +>count : Symbol(count, Decl(logicalAssignment10.ts, 0, 3)) +} + +const oobj = { +>oobj : Symbol(oobj, Decl(logicalAssignment10.ts, 6, 5)) + + obj +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 6, 14)) +} + +obj[incr()] ??= incr(); +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 1, 3)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + +oobj["obj"][incr()] ??= incr(); +>oobj : Symbol(oobj, Decl(logicalAssignment10.ts, 6, 5)) +>"obj" : Symbol(obj, Decl(logicalAssignment10.ts, 6, 14)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + diff --git a/tests/baselines/reference/logicalAssignment10(target=es2020).types b/tests/baselines/reference/logicalAssignment10(target=es2020).types new file mode 100644 index 0000000000..d62c3c6710 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=es2020).types @@ -0,0 +1,45 @@ +=== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts === +var count = 0; +>count : number +>0 : 0 + +var obj = {}; +>obj : {} +>{} : {} + +function incr() { +>incr : () => number + + return ++count; +>++count : number +>count : number +} + +const oobj = { +>oobj : { obj: {}; } +>{ obj} : { obj: {}; } + + obj +>obj : {} +} + +obj[incr()] ??= incr(); +>obj[incr()] ??= incr() : any +>obj[incr()] : error +>obj : {} +>incr() : number +>incr : () => number +>incr() : number +>incr : () => number + +oobj["obj"][incr()] ??= incr(); +>oobj["obj"][incr()] ??= incr() : any +>oobj["obj"][incr()] : error +>oobj["obj"] : {} +>oobj : { obj: {}; } +>"obj" : "obj" +>incr() : number +>incr : () => number +>incr() : number +>incr : () => number + diff --git a/tests/baselines/reference/logicalAssignment10(target=esnext).js b/tests/baselines/reference/logicalAssignment10(target=esnext).js new file mode 100644 index 0000000000..40b2ed7c09 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=esnext).js @@ -0,0 +1,26 @@ +//// [logicalAssignment10.ts] +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +const oobj = { + obj +} + +obj[incr()] ??= incr(); +oobj["obj"][incr()] ??= incr(); + + +//// [logicalAssignment10.js] +var count = 0; +var obj = {}; +function incr() { + return ++count; +} +const oobj = { + obj +}; +obj[incr()] ??= incr(); +oobj["obj"][incr()] ??= incr(); diff --git a/tests/baselines/reference/logicalAssignment10(target=esnext).symbols b/tests/baselines/reference/logicalAssignment10(target=esnext).symbols new file mode 100644 index 0000000000..d9a4e30998 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=esnext).symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts === +var count = 0; +>count : Symbol(count, Decl(logicalAssignment10.ts, 0, 3)) + +var obj = {}; +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 1, 3)) + +function incr() { +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + + return ++count; +>count : Symbol(count, Decl(logicalAssignment10.ts, 0, 3)) +} + +const oobj = { +>oobj : Symbol(oobj, Decl(logicalAssignment10.ts, 6, 5)) + + obj +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 6, 14)) +} + +obj[incr()] ??= incr(); +>obj : Symbol(obj, Decl(logicalAssignment10.ts, 1, 3)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + +oobj["obj"][incr()] ??= incr(); +>oobj : Symbol(oobj, Decl(logicalAssignment10.ts, 6, 5)) +>"obj" : Symbol(obj, Decl(logicalAssignment10.ts, 6, 14)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) +>incr : Symbol(incr, Decl(logicalAssignment10.ts, 1, 13)) + diff --git a/tests/baselines/reference/logicalAssignment10(target=esnext).types b/tests/baselines/reference/logicalAssignment10(target=esnext).types new file mode 100644 index 0000000000..d62c3c6710 --- /dev/null +++ b/tests/baselines/reference/logicalAssignment10(target=esnext).types @@ -0,0 +1,45 @@ +=== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts === +var count = 0; +>count : number +>0 : 0 + +var obj = {}; +>obj : {} +>{} : {} + +function incr() { +>incr : () => number + + return ++count; +>++count : number +>count : number +} + +const oobj = { +>oobj : { obj: {}; } +>{ obj} : { obj: {}; } + + obj +>obj : {} +} + +obj[incr()] ??= incr(); +>obj[incr()] ??= incr() : any +>obj[incr()] : error +>obj : {} +>incr() : number +>incr : () => number +>incr() : number +>incr : () => number + +oobj["obj"][incr()] ??= incr(); +>oobj["obj"][incr()] ??= incr() : any +>oobj["obj"][incr()] : error +>oobj["obj"] : {} +>oobj : { obj: {}; } +>"obj" : "obj" +>incr() : number +>incr : () => number +>incr() : number +>incr : () => number + diff --git a/tests/baselines/reference/logicalAssignment2(target=es2015).js b/tests/baselines/reference/logicalAssignment2(target=es2015).js index c5dbfbac49..4a5b10d2ff 100644 --- a/tests/baselines/reference/logicalAssignment2(target=es2015).js +++ b/tests/baselines/reference/logicalAssignment2(target=es2015).js @@ -31,13 +31,13 @@ c.foo.bar().baz ??= result.foo.bar().baz //// [logicalAssignment2.js] "use strict"; var _a, _b, _c; -var _d, _e, _f, _g, _h, _j, _k, _l, _m; -(_d = a).baz && (_d.baz = result.baz); -(_e = b).baz || (_e.baz = result.baz); -(_a = (_f = c).baz) !== null && _a !== void 0 ? _a : (_f.baz = result.baz); -(_g = a.foo)["baz"] && (_g["baz"] = result.foo.baz); -(_h = b.foo)["baz"] || (_h["baz"] = result.foo.baz); -(_b = (_j = c.foo)["baz"]) !== null && _b !== void 0 ? _b : (_j["baz"] = result.foo.baz); -(_k = a.foo.bar()).baz && (_k.baz = result.foo.bar().baz); -(_l = b.foo.bar()).baz || (_l.baz = result.foo.bar().baz); -(_c = (_m = c.foo.bar()).baz) !== null && _c !== void 0 ? _c : (_m.baz = result.foo.bar().baz); +var _d, _e, _f, _g, _h, _j; +a.baz && (a.baz = result.baz); +b.baz || (b.baz = result.baz); +(_a = c.baz) !== null && _a !== void 0 ? _a : (c.baz = result.baz); +(_d = a.foo)["baz"] && (_d["baz"] = result.foo.baz); +(_e = b.foo)["baz"] || (_e["baz"] = result.foo.baz); +(_b = (_f = c.foo)["baz"]) !== null && _b !== void 0 ? _b : (_f["baz"] = result.foo.baz); +(_g = a.foo.bar()).baz && (_g.baz = result.foo.bar().baz); +(_h = b.foo.bar()).baz || (_h.baz = result.foo.bar().baz); +(_c = (_j = c.foo.bar()).baz) !== null && _c !== void 0 ? _c : (_j.baz = result.foo.bar().baz); diff --git a/tests/baselines/reference/logicalAssignment2(target=es2020).js b/tests/baselines/reference/logicalAssignment2(target=es2020).js index fd285b64ad..08181ce3d5 100644 --- a/tests/baselines/reference/logicalAssignment2(target=es2020).js +++ b/tests/baselines/reference/logicalAssignment2(target=es2020).js @@ -30,13 +30,13 @@ c.foo.bar().baz ??= result.foo.bar().baz //// [logicalAssignment2.js] "use strict"; -var _a, _b, _c, _d, _e, _f, _g, _h, _j; -(_a = a).baz && (_a.baz = result.baz); -(_b = b).baz || (_b.baz = result.baz); -(_c = c).baz ?? (_c.baz = result.baz); -(_d = a.foo)["baz"] && (_d["baz"] = result.foo.baz); -(_e = b.foo)["baz"] || (_e["baz"] = result.foo.baz); -(_f = c.foo)["baz"] ?? (_f["baz"] = result.foo.baz); -(_g = a.foo.bar()).baz && (_g.baz = result.foo.bar().baz); -(_h = b.foo.bar()).baz || (_h.baz = result.foo.bar().baz); -(_j = c.foo.bar()).baz ?? (_j.baz = result.foo.bar().baz); +var _a, _b, _c, _d, _e, _f; +a.baz && (a.baz = result.baz); +b.baz || (b.baz = result.baz); +c.baz ?? (c.baz = result.baz); +(_a = a.foo)["baz"] && (_a["baz"] = result.foo.baz); +(_b = b.foo)["baz"] || (_b["baz"] = result.foo.baz); +(_c = c.foo)["baz"] ?? (_c["baz"] = result.foo.baz); +(_d = a.foo.bar()).baz && (_d.baz = result.foo.bar().baz); +(_e = b.foo.bar()).baz || (_e.baz = result.foo.bar().baz); +(_f = c.foo.bar()).baz ?? (_f.baz = result.foo.bar().baz); diff --git a/tests/baselines/reference/logicalAssignment3(target=es2015).js b/tests/baselines/reference/logicalAssignment3(target=es2015).js index 47f0a21041..8a171ab043 100644 --- a/tests/baselines/reference/logicalAssignment3(target=es2015).js +++ b/tests/baselines/reference/logicalAssignment3(target=es2015).js @@ -17,7 +17,6 @@ declare const c: A; //// [logicalAssignment3.js] "use strict"; var _a; -var _b, _c, _d; -(_b = a).baz && (_b.baz = result.baz); -(_c = b).baz || (_c.baz = result.baz); -(_a = (_d = c).baz) !== null && _a !== void 0 ? _a : (_d.baz = result.baz); +a.baz && (a.baz = result.baz); +b.baz || (b.baz = result.baz); +(_a = c.baz) !== null && _a !== void 0 ? _a : (c.baz = result.baz); diff --git a/tests/baselines/reference/logicalAssignment3(target=es2020).js b/tests/baselines/reference/logicalAssignment3(target=es2020).js index 57060aea11..98660201f1 100644 --- a/tests/baselines/reference/logicalAssignment3(target=es2020).js +++ b/tests/baselines/reference/logicalAssignment3(target=es2020).js @@ -16,7 +16,6 @@ declare const c: A; //// [logicalAssignment3.js] "use strict"; -var _a, _b, _c; -(_a = a).baz && (_a.baz = result.baz); -(_b = b).baz || (_b.baz = result.baz); -(_c = c).baz ?? (_c.baz = result.baz); +a.baz && (a.baz = result.baz); +b.baz || (b.baz = result.baz); +c.baz ?? (c.baz = result.baz); diff --git a/tests/baselines/reference/logicalAssignment9.js b/tests/baselines/reference/logicalAssignment9.js index 88f006d372..97a10da3e6 100644 --- a/tests/baselines/reference/logicalAssignment9.js +++ b/tests/baselines/reference/logicalAssignment9.js @@ -8,6 +8,5 @@ x.a &&= false; //// [logicalAssignment9.js] "use strict"; var _a; -var _b, _c; -(_a = (_b = x).a) !== null && _a !== void 0 ? _a : (_b.a = true); -(_c = x).a && (_c.a = false); +(_a = x.a) !== null && _a !== void 0 ? _a : (x.a = true); +x.a && (x.a = false); diff --git a/tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts b/tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts new file mode 100644 index 0000000000..11c98cea77 --- /dev/null +++ b/tests/cases/conformance/esnext/logicalAssignment/logicalAssignment10.ts @@ -0,0 +1,14 @@ +// @target: esnext, es2020, es2015 + +var count = 0; +var obj = {}; +function incr() { + return ++count; +} + +const oobj = { + obj +} + +obj[incr()] ??= incr(); +oobj["obj"][incr()] ??= incr();