fix(compiler): do not use constants for colocated shallow references

Currently the TS generated output (not JIT), breaks at runtime because
we emit all variables using the `Final` modifier. i.e. constants.

This breaks shallow references when we run our acceptance signal
tests using AOT.
This commit is contained in:
Paul Gschwendtner
2023-07-05 16:41:56 +00:00
parent 51ce1d6228
commit 8151dd3cb6
5 changed files with 25 additions and 8 deletions
@@ -68,18 +68,25 @@ export interface VariableOp<OpT extends Op<OpT>> extends Op<OpT> {
* 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<OpT extends Op<OpT>>(
xref: XrefId, variable: SemanticVariable, initializer: o.Expression): VariableOp<OpT> {
xref: XrefId, variable: SemanticVariable, initializer: o.Expression,
isConstant: boolean): VariableOp<OpT> {
return {
kind: OpKind.Variable,
xref,
variable,
initializer,
isConstant,
...NEW_OP,
};
}
@@ -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;
@@ -179,7 +179,8 @@ function generateVariablesInScopeForView<OpT extends ir.Op<OpT>>(
// 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<OpT extends ir.Op<OpT>>(
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) {
@@ -122,6 +122,8 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList<ir.CreateOp
if (op.variable.name === null) {
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
}
// Optimization for variable co-location. If we know the initializer is undefined,
// we don't need to set an explicit one to avoid e.g. a `let b = undefined`.
let initializer: o.Expression|undefined = op.initializer;
if (initializer instanceof o.LiteralExpr && initializer.value === undefined) {
initializer = undefined;
@@ -129,7 +131,8 @@ function reifyCreateOperations(unit: CompilationUnit, ops: ir.OpList<ir.CreateOp
ir.OpList.replace<ir.CreateOp>(
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<ir.UpdateO
ir.OpList.replace<ir.UpdateOp>(
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) {
@@ -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