fix(ivy): compile interpolated bindings without superfluous bind instruction (#23923)
This fixes the case where the compiler would generate a bind(interpolation#()) instruction. PR Close #23923
This commit is contained in:
parent
fb906a87e8
commit
bd149e5d67
|
@ -33,7 +33,7 @@ export class Identifiers {
|
|||
|
||||
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
|
||||
|
||||
static textCreateBound: o.ExternalReference = {name: 'ɵt', moduleName: CORE};
|
||||
static textBinding: o.ExternalReference = {name: 'ɵt', moduleName: CORE};
|
||||
|
||||
static bind: o.ExternalReference = {name: 'ɵb', moduleName: CORE};
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import {CompileReflector} from '../../compile_reflector';
|
|||
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||
import {ConstantPool} from '../../constant_pool';
|
||||
import * as core from '../../core';
|
||||
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../../expression_parser/ast';
|
||||
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, Interpolation, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../../expression_parser/ast';
|
||||
import {Lexer} from '../../expression_parser/lexer';
|
||||
import {Parser} from '../../expression_parser/parser';
|
||||
import * as html from '../../ml_parser/ast';
|
||||
|
@ -340,10 +340,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
const instruction = BINDING_INSTRUCTION_MAP[input.type];
|
||||
if (instruction) {
|
||||
// TODO(chuckj): runtime: security context?
|
||||
const value = o.importExpr(R3.bind).callFn([convertedBinding]);
|
||||
this.instruction(
|
||||
this._bindingCode, input.sourceSpan, instruction, o.literal(elementIndex),
|
||||
o.literal(input.name), value);
|
||||
o.literal(input.name), convertedBinding);
|
||||
} else {
|
||||
this._unsupported(`binding type ${input.type}`);
|
||||
}
|
||||
|
@ -418,7 +417,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
const convertedBinding = this.convertPropertyBinding(context, input.value);
|
||||
this.instruction(
|
||||
this._bindingCode, template.sourceSpan, R3.elementProperty, o.literal(templateIndex),
|
||||
o.literal(input.name), o.importExpr(R3.bind).callFn([convertedBinding]));
|
||||
o.literal(input.name), convertedBinding);
|
||||
});
|
||||
|
||||
// Create the template function
|
||||
|
@ -443,7 +442,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
this.instruction(this._creationCode, text.sourceSpan, R3.text, o.literal(nodeIndex));
|
||||
|
||||
this.instruction(
|
||||
this._bindingCode, text.sourceSpan, R3.textCreateBound, o.literal(nodeIndex),
|
||||
this._bindingCode, text.sourceSpan, R3.textBinding, o.literal(nodeIndex),
|
||||
this.convertPropertyBinding(o.variable(CONTEXT_NAME), text.value));
|
||||
}
|
||||
|
||||
|
@ -483,11 +482,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
|
||||
private convertPropertyBinding(implicit: o.Expression, value: AST): o.Expression {
|
||||
const pipesConvertedValue = value.visit(this._valueConverter);
|
||||
const convertedPropertyBinding = convertPropertyBinding(
|
||||
this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple,
|
||||
interpolate);
|
||||
this._bindingCode.push(...convertedPropertyBinding.stmts);
|
||||
return convertedPropertyBinding.currValExpr;
|
||||
if (pipesConvertedValue instanceof Interpolation) {
|
||||
const convertedPropertyBinding = convertPropertyBinding(
|
||||
this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple,
|
||||
interpolate);
|
||||
this._bindingCode.push(...convertedPropertyBinding.stmts);
|
||||
return convertedPropertyBinding.currValExpr;
|
||||
} else {
|
||||
const convertedPropertyBinding = convertPropertyBinding(
|
||||
this, implicit, pipesConvertedValue, this.bindingContext(), BindingForm.TrySimple,
|
||||
() => error('Unexpected interpolation'));
|
||||
this._bindingCode.push(...convertedPropertyBinding.stmts);
|
||||
return o.importExpr(R3.bind).callFn([convertedPropertyBinding.currValExpr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {MockDirectory, setup} from '../aot/test_util';
|
||||
import {compile, expectEmit} from './mock_compile';
|
||||
|
||||
describe('compiler compliance: bindings', () => {
|
||||
const angularFiles = setup({
|
||||
compileAngular: true,
|
||||
compileAnimations: false,
|
||||
compileCommon: false,
|
||||
});
|
||||
|
||||
describe('text bindings', () => {
|
||||
it('should generate interpolation instruction', () => {
|
||||
const files: MockDirectory = {
|
||||
app: {
|
||||
'example.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`
|
||||
<div>Hello {{ name }}</div>\`
|
||||
})
|
||||
export class MyComponent {
|
||||
name = 'World';
|
||||
}
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
template:function MyComponent_Template(rf: IDENT, $ctx$: IDENT){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵE(0, 'div');
|
||||
$i0$.ɵT(1);
|
||||
$i0$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵt(1, $i0$.ɵi1('Hello ', $ctx$.name, ''));
|
||||
}
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect interpolated text binding');
|
||||
});
|
||||
});
|
||||
|
||||
describe('property bindings', () => {
|
||||
it('should generate bind instruction', () => {
|
||||
const files: MockDirectory = {
|
||||
app: {
|
||||
'example.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: '<a [title]="title"></a>'
|
||||
})
|
||||
export class MyComponent {
|
||||
title = 'Hello World';
|
||||
}
|
||||
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
template:function MyComponent_Template(rf: IDENT, $ctx$: IDENT){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵE(0, 'a');
|
||||
$i0$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵp(0, 'title', $i0$.ɵb($ctx$.title));
|
||||
}
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect property binding');
|
||||
});
|
||||
|
||||
it('should generate interpolation instruction for {{...}} bindings', () => {
|
||||
const files: MockDirectory = {
|
||||
app: {
|
||||
'example.ts': `
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: \`
|
||||
<a title="Hello {{name}}"></a>\`
|
||||
})
|
||||
export class MyComponent {
|
||||
name = 'World';
|
||||
}
|
||||
@NgModule({declarations: [MyComponent]})
|
||||
export class MyModule {}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
const template = `
|
||||
template:function MyComponent_Template(rf: IDENT, $ctx$: IDENT){
|
||||
if (rf & 1) {
|
||||
$i0$.ɵE(0, 'a');
|
||||
$i0$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵp(0, 'title', $i0$.ɵi1('Hello ', $ctx$.name, ''));
|
||||
}
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
expectEmit(result.source, template, 'Incorrect interpolated property binding');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue