Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(454)

Side by Side Diff: packages/polymer_expressions/lib/eval.dart

Issue 2312183003: Removed Polymer from Observatory deps (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library polymer_expressions.eval;
6
7 import 'dart:async';
8 import 'dart:collection';
9
10 import 'package:observe/observe.dart';
11 import 'package:smoke/smoke.dart' as smoke;
12
13 import 'async.dart';
14 import 'expression.dart';
15 import 'filter.dart';
16 import 'visitor.dart';
17
18 final _BINARY_OPERATORS = {
19 '+': (a, b) => a + b,
20 '-': (a, b) => a - b,
21 '*': (a, b) => a * b,
22 '/': (a, b) => a / b,
23 '%': (a, b) => a % b,
24 '==': (a, b) => a == b,
25 '!=': (a, b) => a != b,
26 '===': (a, b) => identical(a, b),
27 '!==': (a, b) => !identical(a, b),
28 '>': (a, b) => a > b,
29 '>=': (a, b) => a >= b,
30 '<': (a, b) => a < b,
31 '<=': (a, b) => a <= b,
32 '||': (a, b) => a || b,
33 '&&': (a, b) => a && b,
34 '|': (a, f) {
35 if (f is Transformer) return f.forward(a);
36 if (f is Filter) return f(a);
37 throw new EvalException("Filters must be a one-argument function.");
38 }
39 };
40
41 final _UNARY_OPERATORS = {
42 '+': (a) => a,
43 '-': (a) => -a,
44 '!': (a) => !a,
45 };
46
47 final _BOOLEAN_OPERATORS = ['!', '||', '&&'];
48
49 /**
50 * Evaluation [expr] in the context of [scope].
51 */
52 Object eval(Expression expr, Scope scope) => new EvalVisitor(scope).visit(expr);
53
54 /**
55 * Returns an [ExpressionObserver] that evaluates [expr] in the context of
56 * scope] and listens for any changes on [Observable] values that are
57 * returned from sub-expressions. When a value changes the expression is
58 * reevaluated and the new result is sent to the [onUpdate] stream of the
59 * [ExpressionObsserver].
60 */
61 ExpressionObserver observe(Expression expr, Scope scope) {
62 var observer = new ObserverBuilder().visit(expr);
63 return observer;
64 }
65
66 /**
67 * Causes [expr] to be reevaluated a returns it's value.
68 */
69 Object update(ExpressionObserver expr, Scope scope, {skipChanges: false}) {
70 new Updater(scope, skipChanges).visit(expr);
71 return expr.currentValue;
72 }
73
74 /**
75 * Assign [value] to the variable or field referenced by [expr] in the context
76 * of [scope].
77 *
78 * [expr] must be an /assignable/ expression, it must not contain
79 * operators or function invocations, and any index operations must use a
80 * literal index.
81 */
82 Object assign(Expression expr, Object value, Scope scope,
83 {bool checkAssignability: true}) {
84
85 Expression expression;
86 var property;
87 bool isIndex = false;
88 var filters = <Expression>[]; // reversed order for assignment
89
90 while (expr is BinaryOperator) {
91 BinaryOperator op = expr;
92 if (op.operator != '|') {
93 break;
94 }
95 filters.add(op.right);
96 expr = op.left;
97 }
98
99 if (expr is Identifier) {
100 expression = empty();
101 property = expr.value;
102 } else if (expr is Index) {
103 expression = expr.receiver;
104 property = expr.argument;
105 isIndex = true;
106 } else if (expr is Getter) {
107 expression = expr.receiver;
108 property = expr.name;
109 } else {
110 if (checkAssignability) {
111 throw new EvalException("Expression is not assignable: $expr");
112 }
113 return null;
114 }
115
116 // transform the values backwards through the filters
117 for (var filterExpr in filters) {
118 var filter = eval(filterExpr, scope);
119 if (filter is! Transformer) {
120 if (checkAssignability) {
121 throw new EvalException("filter must implement Transformer to be "
122 "assignable: $filterExpr");
123 } else {
124 return null;
125 }
126 }
127 value = filter.reverse(value);
128 }
129 // evaluate the receiver
130 var o = eval(expression, scope);
131
132 // can't assign to a property on a null LHS object. Silently fail.
133 if (o == null) return null;
134
135 if (isIndex) {
136 var index = eval(property, scope);
137 o[index] = value;
138 } else {
139 smoke.write(o, smoke.nameToSymbol(property), value);
140 }
141 return value;
142 }
143
144
145 /**
146 * A scope in polymer expressions that can map names to objects. Scopes contain
147 * a set of named variables and a unique model object. The scope structure
148 * is then used to lookup names using the `[]` operator. The lookup first
149 * searches for the name in local variables, then in global variables,
150 * and then finally looks up the name as a property in the model.
151 */
152 abstract class Scope implements Indexable<String, Object> {
153 Scope._();
154
155 /** Create a scope containing a [model] and all of [variables]. */
156 factory Scope({Object model, Map<String, Object> variables}) {
157 var scope = new _ModelScope(model);
158 return variables == null ? scope
159 : new _GlobalsScope(new Map<String, Object>.from(variables), scope);
160 }
161
162 /** Return the unique model in this scope. */
163 Object get model;
164
165 /**
166 * Lookup the value of [name] in the current scope. If [name] is 'this', then
167 * we return the [model]. For any other name, this finds the first variable
168 * matching [name] or, if none exists, the property [name] in the [model].
169 */
170 Object operator [](String name);
171
172 operator []=(String name, Object value) {
173 throw new UnsupportedError('[]= is not supported in Scope.');
174 }
175
176 /**
177 * Returns whether [name] is defined in [model], that is, a lookup
178 * would not find a variable with that name, but there is a non-null model
179 * where we can look it up as a property.
180 */
181 bool _isModelProperty(String name);
182
183 /** Create a new scope extending this scope with an additional variable. */
184 Scope childScope(String name, Object value) =>
185 new _LocalVariableScope(name, value, this);
186 }
187
188 /**
189 * A scope that looks up names in a model object. This kind of scope has no
190 * parent scope because all our lookup operations stop when we reach the model
191 * object. Any variables added in scope or global variables are added as child
192 * scopes.
193 */
194 class _ModelScope extends Scope {
195 final Object model;
196
197 _ModelScope(this.model) : super._();
198
199 Object operator[](String name) {
200 if (name == 'this') return model;
201 var symbol = smoke.nameToSymbol(name);
202 if (model == null || symbol == null) {
203 throw new EvalException("variable '$name' not found");
204 }
205 return _convert(smoke.read(model, symbol));
206 }
207
208 Object _isModelProperty(String name) => name != 'this';
209
210 String toString() => "[model: $model]";
211 }
212
213 /**
214 * A scope that holds a reference to a single variable. Polymer expressions
215 * introduce variables to the scope one at a time. Each time a variable is
216 * added, a new [_LocalVariableScope] is created.
217 */
218 class _LocalVariableScope extends Scope {
219 final Scope parent;
220 final String varName;
221 // TODO(sigmund,justinfagnani): make this @observable?
222 final Object value;
223
224 _LocalVariableScope(this.varName, this.value, this.parent) : super._() {
225 if (varName == 'this') {
226 throw new EvalException("'this' cannot be used as a variable name.");
227 }
228 }
229
230 Object get model => parent != null ? parent.model : null;
231
232 Object operator[](String name) {
233 if (varName == name) return _convert(value);
234 if (parent != null) return parent[name];
235 throw new EvalException("variable '$name' not found");
236 }
237
238 bool _isModelProperty(String name) {
239 if (varName == name) return false;
240 return parent == null ? false : parent._isModelProperty(name);
241 }
242
243 String toString() => "$parent > [local: $varName]";
244 }
245
246 /** A scope that holds a reference to a global variables. */
247 class _GlobalsScope extends Scope {
248 final _ModelScope parent;
249 final Map<String, Object> variables;
250
251 _GlobalsScope(this.variables, this.parent) : super._() {
252 if (variables.containsKey('this')) {
253 throw new EvalException("'this' cannot be used as a variable name.");
254 }
255 }
256
257 Object get model => parent != null ? parent.model : null;
258
259 Object operator[](String name) {
260 if (variables.containsKey(name)) return _convert(variables[name]);
261 if (parent != null) return parent[name];
262 throw new EvalException("variable '$name' not found");
263 }
264
265 bool _isModelProperty(String name) {
266 if (variables.containsKey(name)) return false;
267 return parent == null ? false : parent._isModelProperty(name);
268 }
269
270 String toString() => "$parent > [global: ${variables.keys}]";
271 }
272
273 Object _convert(v) => v is Stream ? new StreamBinding(v) : v;
274
275 abstract class ExpressionObserver<E extends Expression> implements Expression {
276 final E _expr;
277 ExpressionObserver _parent;
278
279 StreamSubscription _subscription;
280 Object _value;
281
282 StreamController _controller = new StreamController.broadcast();
283 Stream get onUpdate => _controller.stream;
284
285 ExpressionObserver(this._expr);
286
287 Expression get expression => _expr;
288
289 Object get currentValue => _value;
290
291 update(Scope scope) => _updateSelf(scope);
292
293 _updateSelf(Scope scope) {}
294
295 _invalidate(Scope scope) {
296 _observe(scope, false);
297 if (_parent != null) {
298 _parent._invalidate(scope);
299 }
300 }
301
302 _unobserve() {
303 if (_subscription != null) {
304 _subscription.cancel();
305 _subscription = null;
306 }
307 }
308
309 _observe(Scope scope, skipChanges) {
310 _unobserve();
311
312 var _oldValue = _value;
313
314 // evaluate
315 _updateSelf(scope);
316
317 if (!skipChanges && !identical(_value, _oldValue)) {
318 _controller.add(_value);
319 }
320 }
321
322 String toString() => _expr.toString();
323 }
324
325 class Updater extends RecursiveVisitor {
326 final Scope scope;
327 final bool skipChanges;
328
329 Updater(this.scope, [this.skipChanges = false]);
330
331 visitExpression(ExpressionObserver e) {
332 e._observe(scope, skipChanges);
333 }
334 }
335
336 class Closer extends RecursiveVisitor {
337 static final _instance = new Closer._();
338 factory Closer() => _instance;
339 Closer._();
340
341 visitExpression(ExpressionObserver e) {
342 e._unobserve();
343 }
344 }
345
346 class EvalVisitor extends Visitor {
347 final Scope scope;
348
349 EvalVisitor(this.scope);
350
351 visitEmptyExpression(EmptyExpression e) => scope.model;
352
353 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
354
355 visitGetter(Getter g) {
356 var receiver = visit(g.receiver);
357 if (receiver == null) return null;
358 var symbol = smoke.nameToSymbol(g.name);
359 return smoke.read(receiver, symbol);
360 }
361
362 visitIndex(Index i) {
363 var receiver = visit(i.receiver);
364 if (receiver == null) return null;
365 var key = visit(i.argument);
366 return receiver[key];
367 }
368
369 visitInvoke(Invoke i) {
370 var receiver = visit(i.receiver);
371 if (receiver == null) return null;
372 var args = (i.arguments == null)
373 ? null
374 : i.arguments.map(visit).toList(growable: false);
375
376 if (i.method == null) {
377 assert(receiver is Function);
378 return Function.apply(receiver, args);
379 }
380
381 var symbol = smoke.nameToSymbol(i.method);
382 return smoke.invoke(receiver, symbol, args);
383 }
384
385 visitLiteral(Literal l) => l.value;
386
387 visitListLiteral(ListLiteral l) => l.items.map(visit).toList();
388
389 visitMapLiteral(MapLiteral l) {
390 var map = {};
391 for (var entry in l.entries) {
392 var key = visit(entry.key);
393 var value = visit(entry.entryValue);
394 map[key] = value;
395 }
396 return map;
397 }
398
399 visitMapLiteralEntry(MapLiteralEntry e) =>
400 throw new UnsupportedError("should never be called");
401
402 visitIdentifier(Identifier i) => scope[i.value];
403
404 visitBinaryOperator(BinaryOperator o) {
405 var operator = o.operator;
406 var left = visit(o.left);
407 var right = visit(o.right);
408
409 var f = _BINARY_OPERATORS[operator];
410 if (operator == '&&' || operator == '||') {
411 // TODO: short-circuit
412 return f(_toBool(left), _toBool(right));
413 } else if (operator == '==' || operator == '!=') {
414 return f(left, right);
415 } else if (left == null || right == null) {
416 return null;
417 }
418 return f(left, right);
419 }
420
421 visitUnaryOperator(UnaryOperator o) {
422 var expr = visit(o.child);
423 var f = _UNARY_OPERATORS[o.operator];
424 if (o.operator == '!') {
425 return f(_toBool(expr));
426 }
427 return (expr == null) ? null : f(expr);
428 }
429
430 visitTernaryOperator(TernaryOperator o) =>
431 visit(o.condition) == true ? visit(o.trueExpr) : visit(o.falseExpr);
432
433 visitInExpression(InExpression i) =>
434 throw new UnsupportedError("can't eval an 'in' expression");
435
436 visitAsExpression(AsExpression i) =>
437 throw new UnsupportedError("can't eval an 'as' expression");
438 }
439
440 class ObserverBuilder extends Visitor {
441 final Queue parents = new Queue();
442
443 ObserverBuilder();
444
445 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e);
446
447 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child);
448
449 visitGetter(Getter g) {
450 var receiver = visit(g.receiver);
451 var getter = new GetterObserver(g, receiver);
452 receiver._parent = getter;
453 return getter;
454 }
455
456 visitIndex(Index i) {
457 var receiver = visit(i.receiver);
458 var arg = visit(i.argument);
459 var index = new IndexObserver(i, receiver, arg);
460 receiver._parent = index;
461 arg._parent = index;
462 return index;
463 }
464
465 visitInvoke(Invoke i) {
466 var receiver = visit(i.receiver);
467 var args = (i.arguments == null)
468 ? null
469 : i.arguments.map(visit).toList(growable: false);
470 var invoke = new InvokeObserver(i, receiver, args);
471 receiver._parent = invoke;
472 if (args != null) args.forEach((a) => a._parent = invoke);
473 return invoke;
474 }
475
476 visitLiteral(Literal l) => new LiteralObserver(l);
477
478 visitListLiteral(ListLiteral l) {
479 var items = l.items.map(visit).toList(growable: false);
480 var list = new ListLiteralObserver(l, items);
481 items.forEach((e) => e._parent = list);
482 return list;
483 }
484
485 visitMapLiteral(MapLiteral l) {
486 var entries = l.entries.map(visit).toList(growable: false);
487 var map = new MapLiteralObserver(l, entries);
488 entries.forEach((e) => e._parent = map);
489 return map;
490 }
491
492 visitMapLiteralEntry(MapLiteralEntry e) {
493 var key = visit(e.key);
494 var value = visit(e.entryValue);
495 var entry = new MapLiteralEntryObserver(e, key, value);
496 key._parent = entry;
497 value._parent = entry;
498 return entry;
499 }
500
501 visitIdentifier(Identifier i) => new IdentifierObserver(i);
502
503 visitBinaryOperator(BinaryOperator o) {
504 var left = visit(o.left);
505 var right = visit(o.right);
506 var binary = new BinaryObserver(o, left, right);
507 left._parent = binary;
508 right._parent = binary;
509 return binary;
510 }
511
512 visitUnaryOperator(UnaryOperator o) {
513 var expr = visit(o.child);
514 var unary = new UnaryObserver(o, expr);
515 expr._parent = unary;
516 return unary;
517 }
518
519 visitTernaryOperator(TernaryOperator o) {
520 var condition = visit(o.condition);
521 var trueExpr = visit(o.trueExpr);
522 var falseExpr = visit(o.falseExpr);
523 var ternary = new TernaryObserver(o, condition, trueExpr, falseExpr);
524 condition._parent = ternary;
525 trueExpr._parent = ternary;
526 falseExpr._parent = ternary;
527 return ternary;
528 }
529
530 visitInExpression(InExpression i) {
531 throw new UnsupportedError("can't eval an 'in' expression");
532 }
533
534 visitAsExpression(AsExpression i) {
535 throw new UnsupportedError("can't eval an 'as' expression");
536 }
537 }
538
539 class EmptyObserver extends ExpressionObserver<EmptyExpression>
540 implements EmptyExpression {
541
542 EmptyObserver(EmptyExpression value) : super(value);
543
544 _updateSelf(Scope scope) {
545 _value = scope.model;
546 // TODO(justin): listen for scope.model changes?
547 }
548
549 accept(Visitor v) => v.visitEmptyExpression(this);
550 }
551
552 class LiteralObserver extends ExpressionObserver<Literal> implements Literal {
553
554 LiteralObserver(Literal value) : super(value);
555
556 dynamic get value => _expr.value;
557
558 _updateSelf(Scope scope) {
559 _value = _expr.value;
560 }
561
562 accept(Visitor v) => v.visitLiteral(this);
563 }
564
565 class ListLiteralObserver extends ExpressionObserver<ListLiteral>
566 implements ListLiteral {
567
568 final List<ExpressionObserver> items;
569
570 ListLiteralObserver(ListLiteral value, this.items) : super(value);
571
572 _updateSelf(Scope scope) {
573 _value = items.map((i) => i._value).toList();
574 }
575
576 accept(Visitor v) => v.visitListLiteral(this);
577 }
578
579 class MapLiteralObserver extends ExpressionObserver<MapLiteral>
580 implements MapLiteral {
581
582 final List<MapLiteralEntryObserver> entries;
583
584 MapLiteralObserver(MapLiteral value, this.entries) : super(value);
585
586 _updateSelf(Scope scope) {
587 _value = entries.fold(new Map(),
588 (m, e) => m..[e.key._value] = e.entryValue._value);
589 }
590
591 accept(Visitor v) => v.visitMapLiteral(this);
592 }
593
594 class MapLiteralEntryObserver extends ExpressionObserver<MapLiteralEntry>
595 implements MapLiteralEntry {
596
597 final LiteralObserver key;
598 final ExpressionObserver entryValue;
599
600 MapLiteralEntryObserver(MapLiteralEntry value, this.key, this.entryValue)
601 : super(value);
602
603 accept(Visitor v) => v.visitMapLiteralEntry(this);
604 }
605
606 class IdentifierObserver extends ExpressionObserver<Identifier>
607 implements Identifier {
608
609 IdentifierObserver(Identifier value) : super(value);
610
611 String get value => _expr.value;
612
613 _updateSelf(Scope scope) {
614 _value = scope[value];
615 if (!scope._isModelProperty(value)) return;
616 var model = scope.model;
617 if (model is! Observable) return;
618 var symbol = smoke.nameToSymbol(value);
619 _subscription = (model as Observable).changes.listen((changes) {
620 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
621 _invalidate(scope);
622 }
623 });
624 }
625
626 accept(Visitor v) => v.visitIdentifier(this);
627 }
628
629 class ParenthesizedObserver extends ExpressionObserver<ParenthesizedExpression>
630 implements ParenthesizedExpression {
631 final ExpressionObserver child;
632
633 ParenthesizedObserver(ParenthesizedExpression expr, this.child) : super(expr);
634
635
636 _updateSelf(Scope scope) {
637 _value = child._value;
638 }
639
640 accept(Visitor v) => v.visitParenthesizedExpression(this);
641 }
642
643 class UnaryObserver extends ExpressionObserver<UnaryOperator>
644 implements UnaryOperator {
645 final ExpressionObserver child;
646
647 UnaryObserver(UnaryOperator expr, this.child) : super(expr);
648
649 String get operator => _expr.operator;
650
651 _updateSelf(Scope scope) {
652 var f = _UNARY_OPERATORS[_expr.operator];
653 if (operator == '!') {
654 _value = f(_toBool(child._value));
655 } else {
656 _value = (child._value == null) ? null : f(child._value);
657 }
658 }
659
660 accept(Visitor v) => v.visitUnaryOperator(this);
661 }
662
663 class BinaryObserver extends ExpressionObserver<BinaryOperator>
664 implements BinaryOperator {
665
666 final ExpressionObserver left;
667 final ExpressionObserver right;
668
669 BinaryObserver(BinaryOperator expr, this.left, this.right)
670 : super(expr);
671
672 String get operator => _expr.operator;
673
674 _updateSelf(Scope scope) {
675 var f = _BINARY_OPERATORS[operator];
676 if (operator == '&&' || operator == '||') {
677 _value = f(_toBool(left._value), _toBool(right._value));
678 } else if (operator == '==' || operator == '!=') {
679 _value = f(left._value, right._value);
680 } else if (left._value == null || right._value == null) {
681 _value = null;
682 } else {
683 if (operator == '|' && left._value is ObservableList) {
684 _subscription = (left._value as ObservableList).listChanges
685 .listen((_) => _invalidate(scope));
686 }
687 _value = f(left._value, right._value);
688 }
689 }
690
691 accept(Visitor v) => v.visitBinaryOperator(this);
692
693 }
694
695 class TernaryObserver extends ExpressionObserver<TernaryOperator>
696 implements TernaryOperator {
697
698 final ExpressionObserver condition;
699 final ExpressionObserver trueExpr;
700 final ExpressionObserver falseExpr;
701
702 TernaryObserver(TernaryOperator expr, this.condition, this.trueExpr,
703 this.falseExpr) : super(expr);
704
705 _updateSelf(Scope scope) {
706 _value = _toBool(condition._value) ? trueExpr._value : falseExpr._value;
707 }
708
709 accept(Visitor v) => v.visitTernaryOperator(this);
710
711 }
712
713 class GetterObserver extends ExpressionObserver<Getter> implements Getter {
714 final ExpressionObserver receiver;
715
716 GetterObserver(Expression expr, this.receiver) : super(expr);
717
718 String get name => _expr.name;
719
720 _updateSelf(Scope scope) {
721 var receiverValue = receiver._value;
722 if (receiverValue == null) {
723 _value = null;
724 return;
725 }
726 var symbol = smoke.nameToSymbol(_expr.name);
727 _value = smoke.read(receiverValue, symbol);
728
729 if (receiverValue is Observable) {
730 _subscription = (receiverValue as Observable).changes.listen((changes) {
731 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) {
732 _invalidate(scope);
733 }
734 });
735 }
736 }
737
738 accept(Visitor v) => v.visitGetter(this);
739 }
740
741 class IndexObserver extends ExpressionObserver<Index> implements Index {
742 final ExpressionObserver receiver;
743 final ExpressionObserver argument;
744
745 IndexObserver(Expression expr, this.receiver, this.argument) : super(expr);
746
747 _updateSelf(Scope scope) {
748 var receiverValue = receiver._value;
749 if (receiverValue == null) {
750 _value = null;
751 return;
752 }
753 var key = argument._value;
754 _value = receiverValue[key];
755
756 if (receiverValue is ObservableList) {
757 _subscription = (receiverValue as ObservableList).listChanges
758 .listen((changes) {
759 if (changes.any((c) => c.indexChanged(key))) _invalidate(scope);
760 });
761 } else if (receiverValue is Observable) {
762 _subscription = (receiverValue as Observable).changes.listen((changes) {
763 if (changes.any((c) => c is MapChangeRecord && c.key == key)) {
764 _invalidate(scope);
765 }
766 });
767 }
768 }
769
770 accept(Visitor v) => v.visitIndex(this);
771 }
772
773 class InvokeObserver extends ExpressionObserver<Invoke> implements Invoke {
774 final ExpressionObserver receiver;
775 final List<ExpressionObserver> arguments;
776
777 InvokeObserver(Expression expr, this.receiver, this.arguments)
778 : super(expr) {
779 assert(arguments != null);
780 }
781
782 String get method => _expr.method;
783
784 _updateSelf(Scope scope) {
785 var args = arguments.map((a) => a._value).toList();
786 var receiverValue = receiver._value;
787 if (receiverValue == null) {
788 _value = null;
789 return;
790 }
791 if (_expr.method == null) {
792 // top-level function or model method
793 // TODO(justin): listen to model changes to see if the method has
794 // changed? listen to the scope to see if the top-level method has
795 // changed?
796 assert(receiverValue is Function);
797 _value = _convert(Function.apply(receiverValue, args));
798 } else {
799 var symbol = smoke.nameToSymbol(_expr.method);
800 _value = smoke.invoke(receiverValue, symbol, args);
801
802 if (receiverValue is Observable) {
803 _subscription = (receiverValue as Observable).changes.listen(
804 (List<ChangeRecord> changes) {
805 if (changes.any(
806 (c) => c is PropertyChangeRecord && c.name == symbol)) {
807 _invalidate(scope);
808 }
809 });
810 }
811 }
812 }
813
814 accept(Visitor v) => v.visitInvoke(this);
815 }
816
817 _toBool(v) => (v == null) ? false : v;
818
819 class EvalException implements Exception {
820 final String message;
821 EvalException(this.message);
822 String toString() => "EvalException: $message";
823 }
OLDNEW
« no previous file with comments | « packages/polymer_expressions/lib/async.dart ('k') | packages/polymer_expressions/lib/expression.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698