diff --git a/packages/compiler/src/template/pipeline/ir/src/ops/shared.ts b/packages/compiler/src/template/pipeline/ir/src/ops/shared.ts index 07ee7aa93c6..2e27a67673e 100644 --- a/packages/compiler/src/template/pipeline/ir/src/ops/shared.ts +++ b/packages/compiler/src/template/pipeline/ir/src/ops/shared.ts @@ -68,18 +68,25 @@ export interface VariableOp> extends Op { * Expression representing the value of the variable. */ initializer: o.Expression; + + /** + * Whether the variable created is a constant. + */ + isConstant: boolean; } /** * Create a `VariableOp`. */ export function createVariableOp>( - xref: XrefId, variable: SemanticVariable, initializer: o.Expression): VariableOp { + xref: XrefId, variable: SemanticVariable, initializer: o.Expression, + isConstant: boolean): VariableOp { return { kind: OpKind.Variable, xref, variable, initializer, + isConstant, ...NEW_OP, }; } diff --git a/packages/compiler/src/template/pipeline/src/phases/creation_var_colocation.ts b/packages/compiler/src/template/pipeline/src/phases/creation_var_colocation.ts index 1268d4c5924..6f72ff51a54 100644 --- a/packages/compiler/src/template/pipeline/src/phases/creation_var_colocation.ts +++ b/packages/compiler/src/template/pipeline/src/phases/creation_var_colocation.ts @@ -79,6 +79,8 @@ function processUnit(unit: CompilationUnit): void { // initializer to `undefined`, and set the variable to its value after its declaration. const initializer = declOp.initializer; declOp.initializer = o.literal(undefined); + declOp.isConstant = false; + const readVar = new ir.ReadVariableExpr(declOp.xref); // TODO: variable naming should run after this and take care of this for us. readVar.name = declOp.variable.name; diff --git a/packages/compiler/src/template/pipeline/src/phases/generate_variables.ts b/packages/compiler/src/template/pipeline/src/phases/generate_variables.ts index ff79c5a2fd1..cb9884e7935 100644 --- a/packages/compiler/src/template/pipeline/src/phases/generate_variables.ts +++ b/packages/compiler/src/template/pipeline/src/phases/generate_variables.ts @@ -179,7 +179,8 @@ function generateVariablesInScopeForView>( // view with a `nextContext` expression. This context switching operation itself declares a // variable, because the context of the view may be referenced directly. newOps.push(ir.createVariableOp( - view.job.allocateXrefId(), scope.viewContextVariable, new ir.NextContextExpr())); + view.job.allocateXrefId(), scope.viewContextVariable, new ir.NextContextExpr(), + /* isConstant */ true)); } // Add variables for all context variables available in this scope's view. @@ -189,13 +190,15 @@ function generateVariablesInScopeForView>( const variable = value === ir.CTX_REF ? context : new o.ReadPropExpr(context, value); // Add the variable declaration. newOps.push(ir.createVariableOp( - view.job.allocateXrefId(), scope.contextVariables.get(name)!, variable)); + view.job.allocateXrefId(), scope.contextVariables.get(name)!, variable, + /* isConstant */ true)); } // Add variables for all local references declared for elements in this scope. for (const ref of scope.references) { newOps.push(ir.createVariableOp( - view.job.allocateXrefId(), ref.variable, new ir.ReferenceExpr(ref.targetId, ref.offset))); + view.job.allocateXrefId(), ref.variable, new ir.ReferenceExpr(ref.targetId, ref.offset), + /* isConstant */ true)); } if (scope.parent !== null) { diff --git a/packages/compiler/src/template/pipeline/src/phases/reify.ts b/packages/compiler/src/template/pipeline/src/phases/reify.ts index fadf86b0861..e833d7ba4be 100644 --- a/packages/compiler/src/template/pipeline/src/phases/reify.ts +++ b/packages/compiler/src/template/pipeline/src/phases/reify.ts @@ -122,6 +122,8 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList( op, ir.createStatementOp(new o.DeclareVarStmt( - op.variable.name, initializer, undefined, o.StmtModifier.Final))); + op.variable.name, initializer, undefined, + op.isConstant ? o.StmtModifier.Final : undefined))); break; case ir.OpKind.Namespace: switch (op.active) { @@ -280,7 +283,8 @@ function reifyUpdateOperations(_unit: CompilationUnit, ops: ir.OpList( op, ir.createStatementOp(new o.DeclareVarStmt( - op.variable.name, op.initializer, undefined, o.StmtModifier.Final))); + op.variable.name, op.initializer, undefined, + op.isConstant ? o.StmtModifier.Final : undefined))); break; case ir.OpKind.Conditional: if (op.processed === null) { diff --git a/packages/compiler/src/template/pipeline/src/phases/save_restore_view.ts b/packages/compiler/src/template/pipeline/src/phases/save_restore_view.ts index 908f5713130..4dd4b28f0c4 100644 --- a/packages/compiler/src/template/pipeline/src/phases/save_restore_view.ts +++ b/packages/compiler/src/template/pipeline/src/phases/save_restore_view.ts @@ -19,7 +19,8 @@ export function phaseSaveRestoreView(job: ComponentCompilationJob): void { name: null, view: view.xref, }, - new ir.GetCurrentViewExpr()), + new ir.GetCurrentViewExpr(), /* isConstant */ true), + ]); for (const op of view.create) { @@ -56,7 +57,7 @@ function addSaveRestoreViewOperationToListener(unit: ViewCompilationUnit, op: ir name: null, view: unit.xref, }, - new ir.RestoreViewExpr(unit.xref)), + new ir.RestoreViewExpr(unit.xref), /* isConstant */ true), ]); // The "restore view" operation in listeners requires a call to `resetView` to reset the