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 @MirrorsUsed( |
11 override: 'polymer_expressions.eval') | 11 metaTargets: const [Reflectable, ObservableProperty], |
12 import 'dart:mirrors'; | 12 override: 'smoke.mirrors') |
| 13 import 'dart:mirrors' show MirrorsUsed; |
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'; | |
21 | 22 |
22 final _BINARY_OPERATORS = { | 23 final _BINARY_OPERATORS = { |
23 '+': (a, b) => a + b, | 24 '+': (a, b) => a + b, |
24 '-': (a, b) => a - b, | 25 '-': (a, b) => a - b, |
25 '*': (a, b) => a * b, | 26 '*': (a, b) => a * b, |
26 '/': (a, b) => a / b, | 27 '/': (a, b) => a / b, |
27 '==': (a, b) => a == b, | 28 '==': (a, b) => a == b, |
28 '!=': (a, b) => a != b, | 29 '!=': (a, b) => a != b, |
29 '>': (a, b) => a > b, | 30 '>': (a, b) => a > b, |
30 '>=': (a, b) => a >= b, | 31 '>=': (a, b) => a >= b, |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 throw new EvalException("filter must implement Transformer: $filterExpr"); | 136 throw new EvalException("filter must implement Transformer: $filterExpr"); |
136 } | 137 } |
137 value = filter.reverse(value); | 138 value = filter.reverse(value); |
138 } | 139 } |
139 // make the assignment | 140 // make the assignment |
140 var o = eval(expression, scope); | 141 var o = eval(expression, scope); |
141 if (o == null) throw new EvalException("Can't assign to null: $expression"); | 142 if (o == null) throw new EvalException("Can't assign to null: $expression"); |
142 if (isIndex) { | 143 if (isIndex) { |
143 o[property] = value; | 144 o[property] = value; |
144 } else { | 145 } else { |
145 reflect(o).setField(new Symbol(property), value); | 146 smoke.write(o, smoke.nameToSymbol(property), value); |
146 } | 147 } |
147 } | 148 } |
148 | 149 |
149 /** | 150 /** |
150 * A mapping of names to objects. Scopes contain a set of named [variables] and | 151 * 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). | 152 * 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]. | 153 * Names are currently looked up in [variables] first, then the [model]. |
153 * | 154 * |
154 * Scopes can be nested by giving them a [parent]. If a name in not found in a | 155 * 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. | 156 * Scope, it will look for it in it's parent. |
156 */ | 157 */ |
157 class Scope { | 158 class Scope { |
158 final Scope parent; | 159 final Scope parent; |
159 final Object model; | 160 final Object model; |
160 // TODO(justinfagnani): disallow adding/removing names | 161 // TODO(justinfagnani): disallow adding/removing names |
161 final ObservableMap<String, Object> _variables; | 162 final ObservableMap<String, Object> _variables; |
162 InstanceMirror __modelMirror; | |
163 | 163 |
164 Scope({this.model, Map<String, Object> variables, this.parent}) | 164 Scope({this.model, Map<String, Object> variables, this.parent}) |
165 : _variables = new ObservableMap.from(variables == null ? {} : variables); | 165 : _variables = new ObservableMap.from(variables == null ? {} : variables); |
166 | 166 |
167 InstanceMirror get _modelMirror { | |
168 if (__modelMirror != null) return __modelMirror; | |
169 __modelMirror = reflect(model); | |
170 return __modelMirror; | |
171 } | |
172 | |
173 Object operator[](String name) { | 167 Object operator[](String name) { |
174 if (name == 'this') { | 168 if (name == 'this') { |
175 return model; | 169 return model; |
176 } else if (_variables.containsKey(name)) { | 170 } else if (_variables.containsKey(name)) { |
177 return _convert(_variables[name]); | 171 return _convert(_variables[name]); |
178 } else if (model != null) { | 172 } else { |
179 var symbol = new Symbol(name); | 173 var symbol = smoke.nameToSymbol(name); |
180 var classMirror = _modelMirror.type; | 174 if (model != null && smoke.hasGetter(model.runtimeType, symbol)) { |
181 var memberMirror = getMemberMirror(classMirror, symbol); | 175 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 } | 176 } |
190 } | 177 } |
191 if (parent != null) { | 178 if (parent != null) { |
192 return _convert(parent[name]); | 179 return _convert(parent[name]); |
193 } else { | 180 } else { |
194 throw new EvalException("variable '$name' not found"); | 181 throw new EvalException("variable '$name' not found"); |
195 } | 182 } |
196 } | 183 } |
197 | 184 |
198 Object ownerOf(String name) { | 185 Object ownerOf(String name) { |
199 if (name == 'this') { | 186 if (name == 'this') { |
200 // we could return the Scope if it were Observable, but since assigning | 187 // 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 | 188 // a model to a template destroys and recreates the instance, it doesn't |
202 // seem neccessary | 189 // seem neccessary |
203 return null; | 190 return null; |
204 } else if (_variables.containsKey(name)) { | 191 } else if (_variables.containsKey(name)) { |
205 return _variables; | 192 return _variables; |
206 } else { | 193 } else if (smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { |
207 var symbol = new Symbol(name); | 194 return model; |
208 var classMirror = _modelMirror.type; | |
209 if (getMemberMirror(classMirror, symbol) != null) { | |
210 return model; | |
211 } | |
212 } | 195 } |
213 if (parent != null) { | 196 if (parent != null) { |
214 return parent.ownerOf(name); | 197 return parent.ownerOf(name); |
215 } | 198 } |
216 } | 199 } |
217 | 200 |
218 bool contains(String name) { | 201 bool contains(String name) { |
219 if (_variables.containsKey(name)) { | 202 if (_variables.containsKey(name) || |
| 203 smoke.hasGetter(model.runtimeType, smoke.nameToSymbol(name))) { |
220 return true; | 204 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 } | 205 } |
228 if (parent != null) { | 206 if (parent != null) { |
229 return parent.contains(name); | 207 return parent.contains(name); |
230 } | 208 } |
231 return false; | 209 return false; |
232 } | 210 } |
233 } | 211 } |
234 | 212 |
235 Object _convert(v) { | 213 Object _convert(v) { |
236 if (v is Stream) return new StreamBinding(v); | 214 if (v is Stream) return new StreamBinding(v); |
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 | 451 |
474 IdentifierObserver(Identifier value) : super(value); | 452 IdentifierObserver(Identifier value) : super(value); |
475 | 453 |
476 String get value => _expr.value; | 454 String get value => _expr.value; |
477 | 455 |
478 _updateSelf(Scope scope) { | 456 _updateSelf(Scope scope) { |
479 _value = scope[value]; | 457 _value = scope[value]; |
480 | 458 |
481 var owner = scope.ownerOf(value); | 459 var owner = scope.ownerOf(value); |
482 if (owner is Observable) { | 460 if (owner is Observable) { |
483 var symbol = new Symbol(value); | 461 var symbol = smoke.nameToSymbol(value); |
484 _subscription = (owner as Observable).changes.listen((changes) { | 462 _subscription = (owner as Observable).changes.listen((changes) { |
485 if (changes.any( | 463 if (changes.any( |
486 (c) => c is PropertyChangeRecord && c.name == symbol)) { | 464 (c) => c is PropertyChangeRecord && c.name == symbol)) { |
487 _invalidate(scope); | 465 _invalidate(scope); |
488 } | 466 } |
489 }); | 467 }); |
490 } | 468 } |
491 } | 469 } |
492 | 470 |
493 accept(Visitor v) => v.visitIdentifier(this); | 471 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); | 561 GetterObserver(Expression expr, this.receiver) : super(expr); |
584 | 562 |
585 String get name => _expr.name; | 563 String get name => _expr.name; |
586 | 564 |
587 _updateSelf(Scope scope) { | 565 _updateSelf(Scope scope) { |
588 var receiverValue = receiver._value; | 566 var receiverValue = receiver._value; |
589 if (receiverValue == null) { | 567 if (receiverValue == null) { |
590 _value = null; | 568 _value = null; |
591 return; | 569 return; |
592 } | 570 } |
593 var mirror = reflect(receiverValue); | 571 var symbol = smoke.nameToSymbol(_expr.name); |
594 var symbol = new Symbol(_expr.name); | 572 _value = smoke.read(receiverValue, symbol); |
595 _value = mirror.getField(symbol).reflectee; | |
596 | 573 |
597 if (receiverValue is Observable) { | 574 if (receiverValue is Observable) { |
598 _subscription = (receiverValue as Observable).changes.listen((changes) { | 575 _subscription = (receiverValue as Observable).changes.listen((changes) { |
599 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { | 576 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { |
600 _invalidate(scope); | 577 _invalidate(scope); |
601 } | 578 } |
602 }); | 579 }); |
603 } | 580 } |
604 } | 581 } |
605 | 582 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 if (receiverValue == null) { | 627 if (receiverValue == null) { |
651 _value = null; | 628 _value = null; |
652 return; | 629 return; |
653 } | 630 } |
654 if (_expr.method == null) { | 631 if (_expr.method == null) { |
655 // top-level function or model method | 632 // top-level function or model method |
656 // TODO(justin): listen to model changes to see if the method has | 633 // 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 | 634 // changed? listen to the scope to see if the top-level method has |
658 // changed? | 635 // changed? |
659 assert(receiverValue is Function); | 636 assert(receiverValue is Function); |
660 _value = call(receiverValue, args); | 637 _value = _convert(Function.apply(receiverValue, args)); |
661 } else { | 638 } else { |
662 var mirror = reflect(receiverValue); | 639 var symbol = smoke.nameToSymbol(_expr.method); |
663 var symbol = new Symbol(_expr.method); | 640 _value = smoke.invoke(receiverValue, symbol, args); |
664 _value = mirror.invoke(symbol, args, null).reflectee; | |
665 | 641 |
666 if (receiverValue is Observable) { | 642 if (receiverValue is Observable) { |
667 _subscription = (receiverValue as Observable).changes.listen( | 643 _subscription = (receiverValue as Observable).changes.listen( |
668 (List<ChangeRecord> changes) { | 644 (List<ChangeRecord> changes) { |
669 if (changes.any( | 645 if (changes.any( |
670 (c) => c is PropertyChangeRecord && c.name == symbol)) { | 646 (c) => c is PropertyChangeRecord && c.name == symbol)) { |
671 _invalidate(scope); | 647 _invalidate(scope); |
672 } | 648 } |
673 }); | 649 }); |
674 } | 650 } |
(...skipping 24 matching lines...) Expand all Loading... |
699 | 675 |
700 // TODO: make Comprehension observable and update it | 676 // TODO: make Comprehension observable and update it |
701 _value = new Comprehension(identifier.value, iterable); | 677 _value = new Comprehension(identifier.value, iterable); |
702 } | 678 } |
703 | 679 |
704 accept(Visitor v) => v.visitInExpression(this); | 680 accept(Visitor v) => v.visitInExpression(this); |
705 } | 681 } |
706 | 682 |
707 _toBool(v) => (v == null) ? false : v; | 683 _toBool(v) => (v == null) ? false : v; |
708 | 684 |
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 /** | 685 /** |
724 * A comprehension declaration ("a in b"). [identifier] is the loop variable | 686 * 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 | 687 * that's added to the scope during iteration. [iterable] is the set of |
726 * objects to iterate over. | 688 * objects to iterate over. |
727 */ | 689 */ |
728 class Comprehension { | 690 class Comprehension { |
729 final String identifier; | 691 final String identifier; |
730 final Iterable iterable; | 692 final Iterable iterable; |
731 | 693 |
732 Comprehension(this.identifier, Iterable iterable) | 694 Comprehension(this.identifier, Iterable iterable) |
733 : iterable = (iterable != null) ? iterable : const []; | 695 : iterable = (iterable != null) ? iterable : const []; |
734 } | 696 } |
735 | 697 |
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 { | 698 class EvalException implements Exception { |
751 final String message; | 699 final String message; |
752 EvalException(this.message); | 700 EvalException(this.message); |
753 String toString() => "EvalException: $message"; | 701 String toString() => "EvalException: $message"; |
754 } | 702 } |
OLD | NEW |