fix(ivy): fix bug with banana-in-a-box expressions in nested templates (#25321)

Inside of a nested template, an attempt to generate code for a banana-
in-a-box expression would cause a crash in the _AstToIrVisitor, as it
was not handling the case where a write would be generated to a local
variable.

This change supports such a mode of operation.

PR Close #25321
This commit is contained in:
Alex Rickabaugh 2018-08-06 11:48:26 +02:00 committed by Kara Erickson
parent 02e201ab1a
commit fefc860f35
2 changed files with 36 additions and 5 deletions

View File

@ -640,4 +640,21 @@ describe('ngtsc behavioral tests', () => {
expect(emptyFactory).toContain(`import * as i0 from '@angular/core';`);
expect(emptyFactory).toContain(`export var ɵNonEmptyModule = true;`);
});
it('should compile a banana-in-a-box inside of a template', () => {
writeConfig();
write('test.ts', `
import {Component} from '@angular/core';
@Component({
template: '<div *tmpl [(bananaInABox)]="prop"></div>',
selector: 'test'
})
class TestCmp {}
`);
const exitCode = main(['-p', basePath], errorSpy);
expect(errorSpy).not.toHaveBeenCalled();
expect(exitCode).toBe(0);
});
});

View File

@ -449,14 +449,28 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
const receiver: o.Expression = this._visit(ast.receiver, _Mode.Expression);
let varExpr: o.ReadPropExpr|null = null;
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (varExpr) {
throw new Error('Cannot assign to a reference or variable!');
const localExpr = this._getLocal(ast.name);
if (localExpr) {
if (localExpr instanceof o.ReadPropExpr) {
// If the local variable is a property read expression, it's a reference
// to a 'context.property' value and will be used as the target of the
// write expression.
varExpr = localExpr;
} else {
// Otherwise it's an error.
throw new Error('Cannot assign to a reference or variable!');
}
}
}
return convertToStatementIfNeeded(
mode, receiver.prop(ast.name).set(this._visit(ast.value, _Mode.Expression)));
// If no local expression could be produced, use the original receiver's
// property as the target.
if (varExpr === null) {
varExpr = receiver.prop(ast.name);
}
return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
}
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {