From dde81ba0cdb55834dfc5633c89f7912042013252 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 12 Apr 2021 17:36:31 +0200 Subject: [PATCH] perf(compiler): reduce amount of generated code for safe accesses and nullish coalescing (#41563) This is follow-up from #41437 and it reduces the amount of code we generate for safe property accesses (`a?.b`) and nullish coalescing (`a ?? b`) by: 1. Reusing variables in nested nullish coalescing expressions. 2. Not initializing temporary variables to `null`. The way our code is generated means that the value will always be overwritten before we compare against it so the initializer didn't really matter. Fixes #41491. PR Close #41563 --- .../nullish_coalescing_host_bindings.js | 9 ++++----- .../nullish_coalescing_interpolation_template.js | 9 ++++----- .../nullish_coalescing_property_template.js | 9 ++++----- .../host_bindings/host_bindings_with_temporaries.js | 2 +- .../property_bindings/temporary_variables.js | 4 ++-- .../interpolation_complex_expressions.js | 2 +- .../nested_nodes/interpolation_complex_expressions.js | 4 ++-- .../compiler/src/compiler_util/expression_converter.ts | 10 ++++------ 8 files changed, 22 insertions(+), 27 deletions(-) diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_host_bindings.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_host_bindings.js index ce0fbb747e..0205b73b78 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_host_bindings.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_host_bindings.js @@ -1,13 +1,12 @@ hostBindings: function MyApp_HostBindings(rf, ctx) { if (rf & 1) { i0.ɵɵlistener("click", function MyApp_click_HostBindingHandler() { - let tmp_b_0 = null; - let tmp_b_1 = null; - return ctx.logLastName((tmp_b_0 = (tmp_b_1 = ctx.lastName) !== null && tmp_b_1 !== undefined ? tmp_b_1 : ctx.lastNameFallback) !== null && tmp_b_0 !== undefined ? tmp_b_0 : "unknown"); + let $tmp$; + return ctx.logLastName(($tmp$ = ($tmp$ = ctx.lastName) !== null && $tmp$ !== undefined ? $tmp$ : ctx.lastNameFallback) !== null && $tmp$ !== undefined ? $tmp$ : "unknown"); }); } if (rf & 2) { - let tmp_b_0 = null; - i0.ɵɵattribute("first-name", "Hello, " + ((tmp_b_0 = ctx.firstName) !== null && tmp_b_0 !== undefined ? tmp_b_0 : "Frodo") + "!"); + let $tmp$; + i0.ɵɵattribute("first-name", "Hello, " + (($tmp$ = ctx.firstName) !== null && $tmp$ !== undefined ? $tmp$ : "Frodo") + "!"); } } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_interpolation_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_interpolation_template.js index af887ed333..f49392a339 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_interpolation_template.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_interpolation_template.js @@ -8,12 +8,11 @@ template: function MyApp_Template(rf, ctx) { i0.ɵɵelementEnd(); } if (rf & 2) { - let tmp_0_0 = null; - let tmp_1_0 = null; - let tmp_1_1 = null; + let $tmp_0_0$; + let $tmp_1_0$; i0.ɵɵadvance(1); - i0.ɵɵtextInterpolate1("Hello, ", (tmp_0_0 = ctx.firstName) !== null && tmp_0_0 !== undefined ? tmp_0_0 : "Frodo", "!"); + i0.ɵɵtextInterpolate1("Hello, ", ($tmp_0_0$ = ctx.firstName) !== null && $tmp_0_0$ !== undefined ? $tmp_0_0$ : "Frodo", "!"); i0.ɵɵadvance(2); - i0.ɵɵtextInterpolate1("Your last name is ", (tmp_1_0 = (tmp_1_1 = ctx.lastName) !== null && tmp_1_1 !== undefined ? tmp_1_1 : ctx.lastNameFallback) !== null && tmp_1_0 !== undefined ? tmp_1_0 : "unknown", ""); + i0.ɵɵtextInterpolate1("Your last name is ", ($tmp_1_0$ = ($tmp_1_0$ = ctx.lastName) !== null && $tmp_1_0$ !== undefined ? $tmp_1_0$ : ctx.lastNameFallback) !== null && $tmp_1_0$ !== undefined ? $tmp_1_0$ : "unknown", ""); } } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_property_template.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_property_template.js index 297595f004..fbd91fb061 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_property_template.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/nullish_coalescing/nullish_coalescing_property_template.js @@ -4,11 +4,10 @@ template: function MyApp_Template(rf, ctx) { i0.ɵɵelement(1, "span", 0); } if (rf & 2) { - let tmp_0_0 = null; - let tmp_1_0 = null; - let tmp_1_1 = null; - i0.ɵɵproperty("title", "Hello, " + ((tmp_0_0 = ctx.firstName) !== null && tmp_0_0 !== undefined ? tmp_0_0 : "Frodo") + "!"); + let $tmp_0_0$; + let $tmp_1_0$; + i0.ɵɵproperty("title", "Hello, " + (($tmp_0_0$ = ctx.firstName) !== null && $tmp_0_0$ !== undefined ? $tmp_0_0$ : "Frodo") + "!"); i0.ɵɵadvance(1); - i0.ɵɵproperty("title", (tmp_1_0 = (tmp_1_1 = "Your last name is " + ctx.lastName) !== null && tmp_1_1 !== undefined ? tmp_1_1 : ctx.lastNameFallback) !== null && tmp_1_0 !== undefined ? tmp_1_0 : "unknown"); + i0.ɵɵproperty("title", ($tmp_1_0$ = ($tmp_1_0$ = "Your last name is " + ctx.lastName) !== null && $tmp_1_0$ !== undefined ? $tmp_1_0$ : ctx.lastNameFallback) !== null && $tmp_1_0$ !== undefined ? $tmp_1_0$ : "unknown"); } } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_bindings_with_temporaries.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_bindings_with_temporaries.js index ea3d5a209d..0ae9700180 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_bindings_with_temporaries.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/host_bindings_with_temporaries.js @@ -5,7 +5,7 @@ HostBindingDir.ɵdir = /*@__PURE__*/ $r3$.ɵɵdefineDirective({ hostVars: 1, hostBindings: function HostBindingDir_HostBindings(rf, ctx) { if (rf & 2) { - let $tmp0$ = null; + let $tmp0$; $r3$.ɵɵhostProperty("id", ($tmp0$ = ctx.getData()) == null ? null : $tmp0$.id); } } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/temporary_variables.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/temporary_variables.js index eccf250d9e..b7dc723c1c 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/temporary_variables.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/property_bindings/temporary_variables.js @@ -1,7 +1,7 @@ template: function MyComponent_Template(rf, ctx) { … if (rf & 2) { - let $tmp0$ = null; - $r3$.ɵɵproperty("title", ctx.myTitle)("id", (tmp_1_0 = i0.ɵɵpipeBind1(1, 3, ctx.auth().identity())) == null ? null : tmp_1_0.id)("tabindex", 1); + let $tmp0$; + $r3$.ɵɵproperty("title", ctx.myTitle)("id", ($tmp0$ = i0.ɵɵpipeBind1(1, 3, ctx.auth().identity())) == null ? null : $tmp0$.id)("tabindex", 1); } } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js index 76daa891f9..e7d37015f3 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/element_attributes/interpolation_complex_expressions.js @@ -14,7 +14,7 @@ template: function MyComponent_Template(rf, ctx) { $r3$.ɵɵelementEnd(); } if (rf & 2) { - let $tmp_0_0$ = null; + let $tmp_0_0$; $r3$.ɵɵi18nExp(($tmp_0_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_0_0$.getTitle()); $r3$.ɵɵi18nApply(1); } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js index 5f577ba2fb..be5a9ef843 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/nested_nodes/interpolation_complex_expressions.js @@ -12,11 +12,11 @@ template: function MyComponent_Template(rf, ctx) { $r3$.ɵɵelementEnd(); } if (rf & 2) { - let $tmp_2_0$ = null; + let $tmp_2_0$; $r3$.ɵɵadvance(2); $r3$.ɵɵi18nExp($r3$.ɵɵpipeBind1(2, 3, ctx.valueA)) (ctx.valueA == null ? null : ctx.valueA.a == null ? null : ctx.valueA.a.b) (($tmp_2_0$ = ctx.valueA.getRawValue()) == null ? null : $tmp_2_0$.getTitle()); $r3$.ɵɵi18nApply(1); } -} \ No newline at end of file +} diff --git a/packages/compiler/src/compiler_util/expression_converter.ts b/packages/compiler/src/compiler_util/expression_converter.ts index 318e548cc4..eb650f791c 100644 --- a/packages/compiler/src/compiler_util/expression_converter.ts +++ b/packages/compiler/src/compiler_util/expression_converter.ts @@ -259,8 +259,8 @@ function temporaryName(bindingId: string, temporaryNumber: number): string { return `tmp_${bindingId}_${temporaryNumber}`; } -export function temporaryDeclaration(bindingId: string, temporaryNumber: number): o.Statement { - return new o.DeclareVarStmt(temporaryName(bindingId, temporaryNumber), o.NULL_EXPR); +function temporaryDeclaration(bindingId: string, temporaryNumber: number): o.Statement { + return new o.DeclareVarStmt(temporaryName(bindingId, temporaryNumber)); } function prependTemporaryDecls( @@ -728,15 +728,13 @@ class _AstToIrVisitor implements cdAst.AstVisitor { } // Produce the conditional - return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access)); + return convertToStatementIfNeeded(mode, condition.conditional(o.NULL_EXPR, access)); } private convertNullishCoalesce(ast: cdAst.Binary, mode: _Mode): any { - // Allocate the temporary variable before visiting the LHS and RHS, because they - // may allocate temporary variables too and we don't want them to be reused. - const temporary = this.allocateTemporary(); const left: o.Expression = this._visit(ast.left, _Mode.Expression); const right: o.Expression = this._visit(ast.right, _Mode.Expression); + const temporary = this.allocateTemporary(); this.releaseTemporary(temporary); // Generate the following expression. It is identical to how TS