Chromium Code Reviews| 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; | |
| 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 | |
| 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/20 05:05:16
another option here would be to change _MethodClos
| |
| 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 |