OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library polymer_expressions.eval; | 5 library polymer_expressions.eval; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 | 9 |
10 @MirrorsUsed(metaTargets: const [Reflectable, ObservableProperty], | 10 @unused.MirrorsUsed( |
11 override: 'polymer_expressions.eval') | 11 metaTargets: const [Reflectable, ObservableProperty], |
12 import 'dart:mirrors'; | 12 override: 'smoke.mirrors') |
13 import 'dart:mirrors' as unused; | |
blois
2014/02/20 17:29:25
just show MirrorsUsed?
Jennifer Messerly
2014/02/20 21:15:41
yeah, unused is sort of odd since it is actually u
Siggi Cherem (dart-lang)
2014/02/21 03:55:13
good idea. Done.
| |
13 | 14 |
14 import 'package:observe/observe.dart'; | 15 import 'package:observe/observe.dart'; |
16 import 'package:smoke/smoke.dart' as smoke; | |
15 | 17 |
16 import 'async.dart'; | 18 import 'async.dart'; |
17 import 'expression.dart'; | 19 import 'expression.dart'; |
18 import 'filter.dart'; | 20 import 'filter.dart'; |
19 import 'visitor.dart'; | 21 import 'visitor.dart'; |
20 import 'src/mirrors.dart'; | 22 |
23 _unused() => unused.MirrorSystem; // to prevent analyzer warnings | |
blois
2014/02/20 17:29:25
Should this be removed as well?
Siggi Cherem (dart-lang)
2014/02/21 03:55:13
yeah, I fixed it elsewhere, but forgot to do so he
| |
21 | 24 |
22 final _BINARY_OPERATORS = { | 25 final _BINARY_OPERATORS = { |
23 '+': (a, b) => a + b, | 26 '+': (a, b) => a + b, |
24 '-': (a, b) => a - b, | 27 '-': (a, b) => a - b, |
25 '*': (a, b) => a * b, | 28 '*': (a, b) => a * b, |
26 '/': (a, b) => a / b, | 29 '/': (a, b) => a / b, |
27 '==': (a, b) => a == b, | 30 '==': (a, b) => a == b, |
28 '!=': (a, b) => a != b, | 31 '!=': (a, b) => a != b, |
29 '>': (a, b) => a > b, | 32 '>': (a, b) => a > b, |
30 '>=': (a, b) => a >= b, | 33 '>=': (a, b) => a >= b, |
31 '<': (a, b) => a < b, | 34 '<': (a, b) => a < b, |
32 '<=': (a, b) => a <= b, | 35 '<=': (a, b) => a <= b, |
33 '||': (a, b) => a || b, | 36 '||': (a, b) => a || b, |
34 '&&': (a, b) => a && b, | 37 '&&': (a, b) => a && b, |
35 '|': (a, f) { | 38 '|': (a, f) { |
36 if (f is Transformer) return f.forward(a); | 39 if (f is Transformer) return f.forward(a); |
37 if (f is Filter) return f(a); | 40 if (f is Filter) return f(a); |
38 throw new EvalException("Filters must be a one-argument function."); | 41 // TODO(sigmund): uncomment the throw statement. Because of |
42 // dartbug.com/13002 smoke gives us an f that is not comparable to Filter. | |
43 // throw new EvalException("Filters must be a one-argument function."); | |
44 return f(a); | |
Siggi Cherem (dart-lang)
2014/02/21 03:55:13
BTW - I removed this workaround and instead hid th
| |
39 } | 45 } |
40 }; | 46 }; |
41 | 47 |
42 final _UNARY_OPERATORS = { | 48 final _UNARY_OPERATORS = { |
43 '+': (a) => a, | 49 '+': (a) => a, |
44 '-': (a) => -a, | 50 '-': (a) => -a, |
45 '!': (a) => !a, | 51 '!': (a) => !a, |
46 }; | 52 }; |
47 | 53 |
48 final _BOOLEAN_OPERATORS = ['!', '||', '&&']; | 54 final _BOOLEAN_OPERATORS = ['!', '||', '&&']; |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
135 throw new EvalException("filter must implement Transformer: $filterExpr"); | 141 throw new EvalException("filter must implement Transformer: $filterExpr"); |
136 } | 142 } |
137 value = filter.reverse(value); | 143 value = filter.reverse(value); |
138 } | 144 } |
139 // make the assignment | 145 // make the assignment |
140 var o = eval(expression, scope); | 146 var o = eval(expression, scope); |
141 if (o == null) throw new EvalException("Can't assign to null: $expression"); | 147 if (o == null) throw new EvalException("Can't assign to null: $expression"); |
142 if (isIndex) { | 148 if (isIndex) { |
143 o[property] = value; | 149 o[property] = value; |
144 } else { | 150 } else { |
145 reflect(o).setField(new Symbol(property), value); | 151 smoke.write(o, smoke.nameToSymbol(property), value); |
146 } | 152 } |
147 } | 153 } |
148 | 154 |
149 /** | 155 /** |
150 * A mapping of names to objects. Scopes contain a set of named [variables] and | 156 * A mapping of names to objects. Scopes contain a set of named [variables] and |
151 * a single [model] object (which can be thought of as the "this" reference). | 157 * a single [model] object (which can be thought of as the "this" reference). |
152 * Names are currently looked up in [variables] first, then the [model]. | 158 * Names are currently looked up in [variables] first, then the [model]. |
153 * | 159 * |
154 * Scopes can be nested by giving them a [parent]. If a name in not found in a | 160 * Scopes can be nested by giving them a [parent]. If a name in not found in a |
155 * Scope, it will look for it in it's parent. | 161 * Scope, it will look for it in it's parent. |
156 */ | 162 */ |
157 class Scope { | 163 class Scope { |
158 final Scope parent; | 164 final Scope parent; |
159 final Object model; | 165 final Object model; |
160 // TODO(justinfagnani): disallow adding/removing names | 166 // TODO(justinfagnani): disallow adding/removing names |
161 final ObservableMap<String, Object> _variables; | 167 final ObservableMap<String, Object> _variables; |
162 InstanceMirror __modelMirror; | |
163 | 168 |
164 Scope({this.model, Map<String, Object> variables, this.parent}) | 169 Scope({this.model, Map<String, Object> variables, this.parent}) |
165 : _variables = new ObservableMap.from(variables == null ? {} : variables); | 170 : _variables = new ObservableMap.from(variables == null ? {} : variables); |
166 | 171 |
167 InstanceMirror get _modelMirror { | |
168 if (__modelMirror != null) return __modelMirror; | |
169 __modelMirror = reflect(model); | |
170 return __modelMirror; | |
171 } | |
172 | |
173 Object operator[](String name) { | 172 Object operator[](String name) { |
174 if (name == 'this') { | 173 if (name == 'this') { |
175 return model; | 174 return model; |
176 } else if (_variables.containsKey(name)) { | 175 } else if (_variables.containsKey(name)) { |
177 return _convert(_variables[name]); | 176 return _convert(_variables[name]); |
178 } else if (model != null) { | 177 } else { |
179 var symbol = new Symbol(name); | 178 var symbol = smoke.nameToSymbol(name); |
180 var classMirror = _modelMirror.type; | 179 if (model != null && smoke.hasGetter(model.runtimeType, symbol)) { |
181 var memberMirror = getMemberMirror(classMirror, symbol); | 180 return _convert(smoke.read(model, symbol)); |
182 // TODO(jmesserly): simplify once dartbug.com/13002 is fixed. | |
183 // This can just be "if memberMirror != null" and delete the Method class. | |
184 if (memberMirror is VariableMirror || | |
185 (memberMirror is MethodMirror && memberMirror.isGetter)) { | |
186 return _convert(_modelMirror.getField(symbol).reflectee); | |
187 } else if (memberMirror is MethodMirror) { | |
188 return new Method(_modelMirror, symbol); | |
189 } | 181 } |
190 } | 182 } |
191 if (parent != null) { | 183 if (parent != null) { |
192 return _convert(parent[name]); | 184 return _convert(parent[name]); |
193 } else { | 185 } else { |
194 throw new EvalException("variable '$name' not found"); | 186 throw new EvalException("variable '$name' not found"); |
195 } | 187 } |
196 } | 188 } |
197 | 189 |
198 Object ownerOf(String name) { | 190 Object ownerOf(String name) { |
199 if (name == 'this') { | 191 if (name == 'this') { |
200 // we could return the Scope if it were Observable, but since assigning | 192 // we could return the Scope if it were Observable, but since assigning |
201 // a model to a template destroys and recreates the instance, it doesn't | 193 // a model to a template destroys and recreates the instance, it doesn't |
202 // seem neccessary | 194 // seem neccessary |
203 return null; | 195 return null; |
204 } else if (_variables.containsKey(name)) { | 196 } else if (_variables.containsKey(name)) { |
205 return _variables; | 197 return _variables; |
206 } else { | 198 } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { |
207 var symbol = new Symbol(name); | 199 return model; |
208 var classMirror = _modelMirror.type; | |
209 if (getMemberMirror(classMirror, symbol) != null) { | |
210 return model; | |
211 } | |
212 } | 200 } |
213 if (parent != null) { | 201 if (parent != null) { |
214 return parent.ownerOf(name); | 202 return parent.ownerOf(name); |
215 } | 203 } |
216 } | 204 } |
217 | 205 |
218 bool contains(String name) { | 206 bool contains(String name) { |
219 if (_variables.containsKey(name)) { | 207 if (_variables.containsKey(name) || |
208 smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { | |
220 return true; | 209 return true; |
221 } else { | |
222 var symbol = new Symbol(name); | |
223 var classMirror = _modelMirror.type; | |
224 if (getMemberMirror(classMirror, symbol) != null) { | |
225 return true; | |
226 } | |
227 } | 210 } |
228 if (parent != null) { | 211 if (parent != null) { |
229 return parent.contains(name); | 212 return parent.contains(name); |
230 } | 213 } |
231 return false; | 214 return false; |
232 } | 215 } |
233 } | 216 } |
234 | 217 |
235 Object _convert(v) { | 218 Object _convert(v) { |
236 if (v is Stream) return new StreamBinding(v); | 219 if (v is Stream) return new StreamBinding(v); |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
473 | 456 |
474 IdentifierObserver(Identifier value) : super(value); | 457 IdentifierObserver(Identifier value) : super(value); |
475 | 458 |
476 String get value => _expr.value; | 459 String get value => _expr.value; |
477 | 460 |
478 _updateSelf(Scope scope) { | 461 _updateSelf(Scope scope) { |
479 _value = scope[value]; | 462 _value = scope[value]; |
480 | 463 |
481 var owner = scope.ownerOf(value); | 464 var owner = scope.ownerOf(value); |
482 if (owner is Observable) { | 465 if (owner is Observable) { |
483 var symbol = new Symbol(value); | 466 var symbol = smoke.nameToSymbol(value); |
484 _subscription = (owner as Observable).changes.listen((changes) { | 467 _subscription = (owner as Observable).changes.listen((changes) { |
485 if (changes.any( | 468 if (changes.any( |
486 (c) => c is PropertyChangeRecord && c.name == symbol)) { | 469 (c) => c is PropertyChangeRecord && c.name == symbol)) { |
487 _invalidate(scope); | 470 _invalidate(scope); |
488 } | 471 } |
489 }); | 472 }); |
490 } | 473 } |
491 } | 474 } |
492 | 475 |
493 accept(Visitor v) => v.visitIdentifier(this); | 476 accept(Visitor v) => v.visitIdentifier(this); |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
583 GetterObserver(Expression expr, this.receiver) : super(expr); | 566 GetterObserver(Expression expr, this.receiver) : super(expr); |
584 | 567 |
585 String get name => _expr.name; | 568 String get name => _expr.name; |
586 | 569 |
587 _updateSelf(Scope scope) { | 570 _updateSelf(Scope scope) { |
588 var receiverValue = receiver._value; | 571 var receiverValue = receiver._value; |
589 if (receiverValue == null) { | 572 if (receiverValue == null) { |
590 _value = null; | 573 _value = null; |
591 return; | 574 return; |
592 } | 575 } |
593 var mirror = reflect(receiverValue); | 576 var symbol = smoke.nameToSymbol(_expr.name); |
594 var symbol = new Symbol(_expr.name); | 577 _value = smoke.read(receiverValue, symbol); |
595 _value = mirror.getField(symbol).reflectee; | |
596 | 578 |
597 if (receiverValue is Observable) { | 579 if (receiverValue is Observable) { |
598 _subscription = (receiverValue as Observable).changes.listen((changes) { | 580 _subscription = (receiverValue as Observable).changes.listen((changes) { |
599 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { | 581 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { |
600 _invalidate(scope); | 582 _invalidate(scope); |
601 } | 583 } |
602 }); | 584 }); |
603 } | 585 } |
604 } | 586 } |
605 | 587 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
650 if (receiverValue == null) { | 632 if (receiverValue == null) { |
651 _value = null; | 633 _value = null; |
652 return; | 634 return; |
653 } | 635 } |
654 if (_expr.method == null) { | 636 if (_expr.method == null) { |
655 // top-level function or model method | 637 // top-level function or model method |
656 // TODO(justin): listen to model changes to see if the method has | 638 // TODO(justin): listen to model changes to see if the method has |
657 // changed? listen to the scope to see if the top-level method has | 639 // changed? listen to the scope to see if the top-level method has |
658 // changed? | 640 // changed? |
659 assert(receiverValue is Function); | 641 assert(receiverValue is Function); |
660 _value = call(receiverValue, args); | 642 _value = _convert(Function.apply(receiverValue, args)); |
661 } else { | 643 } else { |
662 var mirror = reflect(receiverValue); | 644 var symbol = smoke.nameToSymbol(_expr.method); |
663 var symbol = new Symbol(_expr.method); | 645 _value = smoke.invoke(receiverValue, symbol, args); |
664 _value = mirror.invoke(symbol, args, null).reflectee; | |
665 | 646 |
666 if (receiverValue is Observable) { | 647 if (receiverValue is Observable) { |
667 _subscription = (receiverValue as Observable).changes.listen( | 648 _subscription = (receiverValue as Observable).changes.listen( |
668 (List<ChangeRecord> changes) { | 649 (List<ChangeRecord> changes) { |
669 if (changes.any( | 650 if (changes.any( |
670 (c) => c is PropertyChangeRecord && c.name == symbol)) { | 651 (c) => c is PropertyChangeRecord && c.name == symbol)) { |
671 _invalidate(scope); | 652 _invalidate(scope); |
672 } | 653 } |
673 }); | 654 }); |
674 } | 655 } |
(...skipping 24 matching lines...) Expand all Loading... | |
699 | 680 |
700 // TODO: make Comprehension observable and update it | 681 // TODO: make Comprehension observable and update it |
701 _value = new Comprehension(identifier.value, iterable); | 682 _value = new Comprehension(identifier.value, iterable); |
702 } | 683 } |
703 | 684 |
704 accept(Visitor v) => v.visitInExpression(this); | 685 accept(Visitor v) => v.visitInExpression(this); |
705 } | 686 } |
706 | 687 |
707 _toBool(v) => (v == null) ? false : v; | 688 _toBool(v) => (v == null) ? false : v; |
708 | 689 |
709 /** Call a [Function] or a [Method]. */ | |
710 // TODO(jmesserly): remove this once dartbug.com/13002 is fixed. | |
711 // Just inline `_convert(Function.apply(...))` to the call site. | |
712 Object call(Object receiver, List args) { | |
713 var result; | |
714 if (receiver is Method) { | |
715 Method method = receiver; | |
716 result = method.mirror.invoke(method.symbol, args, null).reflectee; | |
717 } else { | |
718 result = Function.apply(receiver, args, null); | |
719 } | |
720 return _convert(result); | |
721 } | |
722 | |
723 /** | 690 /** |
724 * A comprehension declaration ("a in b"). [identifier] is the loop variable | 691 * A comprehension declaration ("a in b"). [identifier] is the loop variable |
725 * that's added to the scope during iteration. [iterable] is the set of | 692 * that's added to the scope during iteration. [iterable] is the set of |
726 * objects to iterate over. | 693 * objects to iterate over. |
727 */ | 694 */ |
728 class Comprehension { | 695 class Comprehension { |
729 final String identifier; | 696 final String identifier; |
730 final Iterable iterable; | 697 final Iterable iterable; |
731 | 698 |
732 Comprehension(this.identifier, Iterable iterable) | 699 Comprehension(this.identifier, Iterable iterable) |
733 : iterable = (iterable != null) ? iterable : const []; | 700 : iterable = (iterable != null) ? iterable : const []; |
734 } | 701 } |
735 | 702 |
736 /** A method on a model object in a [Scope]. */ | |
737 class Method { | |
738 final InstanceMirror mirror; | |
739 final Symbol symbol; | |
740 | |
741 Method(this.mirror, this.symbol); | |
742 | |
743 /** | |
744 * Support for calling single argument methods like [Filter]s. | |
745 * This does not work for calls that need to pass more than one argument. | |
746 */ | |
747 call(arg0) => mirror.invoke(symbol, [arg0], null).reflectee; | |
748 } | |
749 | |
750 class EvalException implements Exception { | 703 class EvalException implements Exception { |
751 final String message; | 704 final String message; |
752 EvalException(this.message); | 705 EvalException(this.message); |
753 String toString() => "EvalException: $message"; | 706 String toString() => "EvalException: $message"; |
754 } | 707 } |
OLD | NEW |