Skip to content
This repository was archived by the owner on Sep 16, 2022. It is now read-only.

Commit a2c949f

Browse files
leonsenftmatanlurey
authored andcommitted
Prevents dynamic cast warning by propagating binding type to expression fields
A cast issue arose when a collection literal was bound to an input. <div [ngStyle]="{'color': color}"> This would generate code similar to the following: class ViewAppComponent { NgStyle _NgStyle; var _map; var _expr; build() { ... // Returns a Map<String, dynamic>. _map = pureProxy1((p) { return {'background': p}; }); } void detectChangesInternal() { ... final currVal = _map(_ctx.color); if (!identical(_expr, currVal)) { // Cast warning: Map<String, dynamic> assigned to Map<String, String> _NgStyle.rawStyle = currVal; _expr = currval; } } } Since we know `[ngStyle]` is expecting something assignable to `Map<String, String>`, we can propagate this type information forward to fields associated with its binding. class ViewAppComponent { NgStyle _NgStyle; Map<String, String> Function(String) _map; ... } Closes #844. PiperOrigin-RevId: 187631882
1 parent d073fb9 commit a2c949f

File tree

6 files changed

+171
-59
lines changed

6 files changed

+171
-59
lines changed

_tests/test/common/directives/ng_style_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ void main() {
1717
var testFixture = await testBed.create();
1818
var content = testFixture.rootElement.querySelector('div');
1919
expect(content.style.maxWidth, '40px');
20-
}, skip: 'See https://github.com/dart-lang/angular/issues/844');
20+
});
2121
test('should update styles specified in an map literal', () async {
2222
var testBed = new NgTestBed<MapUpdateTest>();
2323
var testFixture = await testBed.create();

angular/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
invoke the corresponding `bypassSecurityTrust*()` method, instead of
1717
constructing these types directly.
1818

19+
* Private types can't be used in template collection literals bound to an
20+
input. This is a consequence of fixing a cast warning that is soon to be an
21+
error caused by the code generated for change detecting collection literals
22+
in templates. See https://github.com/dart-lang/angular/issues/844 for more
23+
information.
24+
1925
### Bug fixes
2026

2127
* The view compiler hoists `this.rootEl` as a `final` local variable to help
@@ -24,6 +30,9 @@
2430
and reduce code-size a bit, especially for users of `@HostBinding` or
2531
`@HostListener` (https://github.com/dart-lang/angular/issues/450).
2632

33+
* Fixed a cast warning caused by untyped code generated for change detecting
34+
collection literals in templates.
35+
2736
## 5.0.0-alpha+6
2837

2938
### New features

angular/lib/src/compiler/view_compiler/expression_converter.dart

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import "package:angular/src/facade/exceptions.dart" show BaseException;
1+
import 'package:angular/src/facade/exceptions.dart' show BaseException;
22

33
import '../chars.dart';
44
import '../expression_parser/ast.dart' as compiler_ast;
@@ -24,24 +24,39 @@ abstract class NameResolver {
2424
/// Returns variable declarations for all locals used in this scope.
2525
List<o.Statement> getLocalDeclarations();
2626

27-
o.Expression createLiteralList(List<o.Expression> values);
27+
/// Creates a closure that returns a list of [type] when [values] change.
28+
o.Expression createLiteralList(
29+
List<o.Expression> values, {
30+
o.OutputType type,
31+
});
32+
33+
/// Creates a closure that returns a map of [type] when [values] change.
2834
o.Expression createLiteralMap(
29-
List<List<dynamic /* String | o . Expression */ >> values);
35+
List<List<dynamic /* String | o.Expression */ >> values, {
36+
o.OutputType type,
37+
});
38+
3039
int createUniqueBindIndex();
3140

3241
/// Creates a name resolver with shared state for use in a new method scope.
3342
NameResolver scope();
3443
}
3544

45+
/// Converts a bound [AST] expression to an [Expression].
46+
///
47+
/// If non-null, [boundType] is the type of the input to which [expression] is
48+
/// bound. This is used to support empty expressions for boolean inputs, and to
49+
/// type annotate collection literal bindings.
3650
o.Expression convertCdExpressionToIr(
37-
NameResolver nameResolver,
38-
o.Expression implicitReceiver,
39-
compiler_ast.AST expression,
40-
bool preserveWhitespace,
41-
bool emptyIsTrue) {
51+
NameResolver nameResolver,
52+
o.Expression implicitReceiver,
53+
compiler_ast.AST expression,
54+
bool preserveWhitespace,
55+
o.OutputType boundType,
56+
) {
4257
assert(nameResolver != null);
4358
var visitor = new _AstToIrVisitor(
44-
nameResolver, implicitReceiver, preserveWhitespace, emptyIsTrue);
59+
nameResolver, implicitReceiver, preserveWhitespace, boundType);
4560
return expression.visit(visitor, _Mode.Expression);
4661
}
4762

@@ -52,7 +67,7 @@ List<o.Statement> convertCdStatementToIr(
5267
bool preserveWhitespace) {
5368
assert(nameResolver != null);
5469
var visitor = new _AstToIrVisitor(
55-
nameResolver, implicitReceiver, preserveWhitespace, false);
70+
nameResolver, implicitReceiver, preserveWhitespace, null);
5671
var statements = <o.Statement>[];
5772
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements);
5873
return statements;
@@ -84,19 +99,32 @@ dynamic /* o . Expression | o . Statement */ convertToStatementIfNeeded(
8499
class _AstToIrVisitor implements compiler_ast.AstVisitor {
85100
final NameResolver _nameResolver;
86101
final o.Expression _implicitReceiver;
87-
final bool preserveWhitespace;
88-
final bool emptyIsTrue;
102+
final bool _preserveWhitespace;
103+
104+
/// The type to which this expression is bound.
105+
///
106+
/// This is used to support empty expressions for booleans bindings, and type
107+
/// pure proxy fields for collection literals.
108+
final o.OutputType _boundType;
109+
110+
/// Whether the current AST is the root of the expression.
111+
///
112+
/// This is used to indicate whether [_boundType] can be used to type pure
113+
/// proxy fields for collection literals.
114+
bool _visitingRoot;
89115

90116
_AstToIrVisitor(
91117
this._nameResolver,
92118
this._implicitReceiver,
93-
this.preserveWhitespace,
94-
this.emptyIsTrue,
95-
) {
119+
this._preserveWhitespace,
120+
this._boundType,
121+
)
122+
: _visitingRoot = true {
96123
assert(_nameResolver != null);
97124
}
98125

99126
dynamic visitBinary(compiler_ast.Binary ast, dynamic context) {
127+
_visitingRoot = false;
100128
_Mode mode = context;
101129
var op;
102130
switch (ast.operation) {
@@ -155,12 +183,14 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
155183
}
156184

157185
dynamic visitChain(compiler_ast.Chain ast, dynamic context) {
186+
_visitingRoot = false;
158187
_Mode mode = context;
159188
ensureStatementMode(mode, ast);
160189
return visitAll(ast.expressions as List<compiler_ast.AST>, mode);
161190
}
162191

163192
dynamic visitConditional(compiler_ast.Conditional ast, dynamic context) {
193+
_visitingRoot = false;
164194
_Mode mode = context;
165195
o.Expression value = ast.condition.visit(this, _Mode.Expression);
166196
return convertToStatementIfNeeded(
@@ -171,13 +201,14 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
171201

172202
dynamic visitEmptyExpr(compiler_ast.EmptyExpr ast, dynamic context) {
173203
_Mode mode = context;
174-
o.LiteralExpr value = emptyIsTrue
204+
final value = _isBoolType(_boundType)
175205
? new o.LiteralExpr(true, o.BOOL_TYPE)
176206
: new o.LiteralExpr('', o.STRING_TYPE);
177207
return convertToStatementIfNeeded(mode, value);
178208
}
179209

180210
dynamic visitPipe(compiler_ast.BindingPipe ast, dynamic context) {
211+
_visitingRoot = false;
181212
_Mode mode = context;
182213
var input = ast.exp.visit(this, _Mode.Expression);
183214
var args = visitAll(ast.args as List<compiler_ast.AST>, _Mode.Expression)
@@ -187,6 +218,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
187218
}
188219

189220
dynamic visitFunctionCall(compiler_ast.FunctionCall ast, dynamic context) {
221+
_visitingRoot = false;
190222
_Mode mode = context;
191223
return convertToStatementIfNeeded(
192224
mode,
@@ -195,6 +227,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
195227
}
196228

197229
dynamic visitIfNull(compiler_ast.IfNull ast, dynamic context) {
230+
_visitingRoot = false;
198231
_Mode mode = context;
199232
o.Expression value = ast.condition.visit(this, _Mode.Expression);
200233
return convertToStatementIfNeeded(
@@ -203,6 +236,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
203236

204237
dynamic visitImplicitReceiver(
205238
compiler_ast.ImplicitReceiver ast, dynamic context) {
239+
_visitingRoot = false;
206240
_Mode mode = context;
207241
ensureExpressionMode(mode, ast);
208242
return IMPLICIT_RECEIVER;
@@ -211,7 +245,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
211245
/// Trim text in preserve whitespace mode if it contains \n preceding
212246
/// interpolation.
213247
String compressWhitespacePreceding(String value) {
214-
if (preserveWhitespace ||
248+
if (_preserveWhitespace ||
215249
value.contains('\u00A0') ||
216250
value.contains(ngSpace) ||
217251
!value.contains('\n')) return replaceNgSpace(value);
@@ -221,14 +255,15 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
221255
/// Trim text in preserve whitespace mode if it contains \n following
222256
/// interpolation.
223257
String compressWhitespaceFollowing(String value) {
224-
if (preserveWhitespace ||
258+
if (_preserveWhitespace ||
225259
value.contains('\u00A0') ||
226260
value.contains(ngSpace) ||
227261
!value.contains('\n')) return replaceNgSpace(value);
228262
return replaceNgSpace(value.replaceAll('\n', '').trimRight());
229263
}
230264

231265
dynamic visitInterpolation(compiler_ast.Interpolation ast, dynamic context) {
266+
_visitingRoot = false;
232267
_Mode mode = context;
233268
ensureExpressionMode(mode, ast);
234269

@@ -267,6 +302,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
267302
}
268303

269304
dynamic visitKeyedRead(compiler_ast.KeyedRead ast, dynamic context) {
305+
_visitingRoot = false;
270306
_Mode mode = context;
271307
return convertToStatementIfNeeded(
272308
mode,
@@ -276,6 +312,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
276312
}
277313

278314
dynamic visitKeyedWrite(compiler_ast.KeyedWrite ast, dynamic context) {
315+
_visitingRoot = false;
279316
_Mode mode = context;
280317
o.Expression obj = ast.obj.visit(this, _Mode.Expression);
281318
o.Expression key = ast.key.visit(this, _Mode.Expression);
@@ -284,31 +321,41 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
284321
}
285322

286323
dynamic visitLiteralArray(compiler_ast.LiteralArray ast, dynamic context) {
324+
final isRootExpression = _visitingRoot;
325+
_visitingRoot = false;
287326
_Mode mode = context;
288327
return convertToStatementIfNeeded(
289-
mode,
290-
_nameResolver.createLiteralList(
291-
visitAll(ast.expressions as List<compiler_ast.AST>, mode)
292-
as List<o.Expression>));
328+
mode,
329+
_nameResolver.createLiteralList(
330+
visitAll(ast.expressions as List<compiler_ast.AST>, mode)
331+
as List<o.Expression>,
332+
type: isRootExpression ? _boundType : null),
333+
);
293334
}
294335

295336
dynamic visitLiteralMap(compiler_ast.LiteralMap ast, dynamic context) {
337+
final isRootExpression = _visitingRoot;
338+
_visitingRoot = false;
296339
_Mode mode = context;
297340
var parts = <List>[];
298341
for (var i = 0; i < ast.keys.length; i++) {
299342
parts.add([ast.keys[i], ast.values[i].visit(this, _Mode.Expression)]);
300343
}
301344
return convertToStatementIfNeeded(
302-
mode, _nameResolver.createLiteralMap(parts));
345+
mode,
346+
_nameResolver.createLiteralMap(parts,
347+
type: isRootExpression ? _boundType : null));
303348
}
304349

305350
dynamic visitLiteralPrimitive(
306351
compiler_ast.LiteralPrimitive ast, dynamic context) {
352+
_visitingRoot = false;
307353
_Mode mode = context;
308354
return convertToStatementIfNeeded(mode, o.literal(ast.value));
309355
}
310356

311357
dynamic visitMethodCall(compiler_ast.MethodCall ast, dynamic context) {
358+
_visitingRoot = false;
312359
_Mode mode = context;
313360
var args = visitAll(ast.args as List<compiler_ast.AST>, _Mode.Expression)
314361
as List<o.Expression>;
@@ -328,12 +375,14 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
328375
}
329376

330377
dynamic visitPrefixNot(compiler_ast.PrefixNot ast, dynamic context) {
378+
_visitingRoot = false;
331379
_Mode mode = context;
332380
return convertToStatementIfNeeded(
333381
mode, o.not(ast.expression.visit(this, _Mode.Expression)));
334382
}
335383

336384
dynamic visitPropertyRead(compiler_ast.PropertyRead ast, dynamic context) {
385+
_visitingRoot = false;
337386
_Mode mode = context;
338387
var result;
339388
var receiver = ast.receiver.visit(this, _Mode.Expression);
@@ -348,6 +397,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
348397
}
349398

350399
dynamic visitPropertyWrite(compiler_ast.PropertyWrite ast, dynamic context) {
400+
_visitingRoot = false;
351401
_Mode mode = context;
352402
o.Expression receiver = ast.receiver.visit(this, _Mode.Expression);
353403
if (identical(receiver, IMPLICIT_RECEIVER)) {
@@ -363,6 +413,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
363413

364414
dynamic visitSafePropertyRead(
365415
compiler_ast.SafePropertyRead ast, dynamic context) {
416+
_visitingRoot = false;
366417
_Mode mode = context;
367418
var receiver = ast.receiver.visit(this, _Mode.Expression);
368419
return convertToStatementIfNeeded(mode,
@@ -371,6 +422,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
371422

372423
dynamic visitSafeMethodCall(
373424
compiler_ast.SafeMethodCall ast, dynamic context) {
425+
_visitingRoot = false;
374426
_Mode mode = context;
375427
var receiver = ast.receiver.visit(this, _Mode.Expression);
376428
var args = visitAll(ast.args as List<compiler_ast.AST>, _Mode.Expression);
@@ -382,6 +434,7 @@ class _AstToIrVisitor implements compiler_ast.AstVisitor {
382434
}
383435

384436
dynamic visitStaticRead(compiler_ast.StaticRead ast, dynamic context) {
437+
_visitingRoot = false;
385438
_Mode mode = context;
386439
return convertToStatementIfNeeded(
387440
mode, o.importExpr(ast.id.identifier, isConst: true));
@@ -402,3 +455,12 @@ void flattenStatements(dynamic arg, List<o.Statement> output) {
402455
output.add(arg);
403456
}
404457
}
458+
459+
bool _isBoolType(o.OutputType type) {
460+
if (type == o.BOOL_TYPE) return true;
461+
if (type is o.ExternalType) {
462+
String name = type.value.name;
463+
return 'bool' == name.trim();
464+
}
465+
return false;
466+
}

0 commit comments

Comments
 (0)