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 @MirrorsUsed(metaTargets: const [Reflectable, ObservableProperty], |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 75 new Updater(scope).visit(expr); | 75 new Updater(scope).visit(expr); |
| 76 return expr.currentValue; | 76 return expr.currentValue; |
| 77 } | 77 } |
| 78 | 78 |
| 79 /** | 79 /** |
| 80 * Assign [value] to the variable or field referenced by [expr] in the context | 80 * Assign [value] to the variable or field referenced by [expr] in the context |
| 81 * of [scope]. | 81 * of [scope]. |
| 82 * | 82 * |
| 83 * [expr] must be an /assignable/ expression, it must not contain | 83 * [expr] must be an /assignable/ expression, it must not contain |
| 84 * operators or function invocations, and any index operations must use a | 84 * operators or function invocations, and any index operations must use a |
| 85 * literal index. | 85 * literal index. |
|
Jennifer Messerly
2013/11/18 20:28:41
out of curiosity, why have the restriction that th
justinfagnani
2013/11/20 03:50:33
No, I was just being conservative at the time and
Jennifer Messerly
2013/11/20 04:04:56
makes sense. yeah, I think we'd eventually want al
| |
| 86 */ | 86 */ |
| 87 void assign(Expression expr, Object value, Scope scope) { | 87 void assign(Expression expr, Object value, Scope scope) { |
| 88 | 88 |
| 89 notAssignable() => | 89 notAssignable() => |
| 90 throw new EvalException("Expression is not assignable: $expr"); | 90 throw new EvalException("Expression is not assignable: $expr"); |
| 91 | 91 |
| 92 Expression expression; | 92 Expression expression; |
| 93 var property; | 93 var property; |
| 94 bool isIndex = false; | 94 bool isIndex = false; |
| 95 var filters = <Expression>[]; // reversed order for assignment | 95 var filters = <Expression>[]; // reversed order for assignment |
| 96 | 96 |
| 97 while (expr is BinaryOperator) { | 97 while (expr is BinaryOperator) { |
| 98 BinaryOperator op = expr; | 98 BinaryOperator op = expr; |
| 99 if (op.operator != '|') { | 99 if (op.operator != '|') { |
| 100 break; | 100 break; |
| 101 } | 101 } |
| 102 filters.add(op.right); | 102 filters.add(op.right); |
| 103 expr = op.left; | 103 expr = op.left; |
| 104 } | 104 } |
| 105 | 105 |
| 106 if (expr is Identifier) { | 106 if (expr is Identifier) { |
| 107 expression = empty(); | 107 expression = empty(); |
| 108 Identifier ident = expr; | 108 Identifier ident = expr; |
| 109 property = ident.value; | 109 property = ident.value; |
| 110 } else if (expr is Index) { | |
| 111 if (expr.argument is! Literal) notAssignable(); | |
| 112 expression = expr.receiver; | |
| 113 Literal l = expr.argument; | |
| 114 property = l.value; | |
| 115 isIndex = true; | |
| 116 } else if (expr is Getter) { | |
| 117 expression = expr.receiver; | |
| 118 property = expr.name; | |
| 110 } else if (expr is Invoke) { | 119 } else if (expr is Invoke) { |
| 111 Invoke invoke = expr; | 120 expression = expr.receiver; |
| 112 expression = invoke.receiver; | 121 if (expr.method != null) { |
| 113 if (invoke.method == '[]') { | 122 if (expr.arguments != null) notAssignable(); |
| 114 if (invoke.arguments[0] is! Literal) notAssignable(); | 123 property = expr.method; |
| 115 Literal l = invoke.arguments[0]; | |
| 116 property = l.value; | |
| 117 isIndex = true; | |
| 118 } else if (invoke.method != null) { | |
| 119 if (invoke.arguments != null) notAssignable(); | |
| 120 property = invoke.method; | |
| 121 } else { | 124 } else { |
| 122 notAssignable(); | 125 notAssignable(); |
| 123 } | 126 } |
| 124 } else { | 127 } else { |
| 125 notAssignable(); | 128 notAssignable(); |
| 126 } | 129 } |
| 127 | 130 |
| 128 // transform the values backwards through the filters | 131 // transform the values backwards through the filters |
| 129 for (var filterExpr in filters) { | 132 for (var filterExpr in filters) { |
| 130 var filter = eval(filterExpr, scope); | 133 var filter = eval(filterExpr, scope); |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 151 * Scopes can be nested by giving them a [parent]. If a name in not found in a | 154 * Scopes can be nested by giving them a [parent]. If a name in not found in a |
| 152 * Scope, it will look for it in it's parent. | 155 * Scope, it will look for it in it's parent. |
| 153 */ | 156 */ |
| 154 class Scope { | 157 class Scope { |
| 155 final Scope parent; | 158 final Scope parent; |
| 156 final Object model; | 159 final Object model; |
| 157 // TODO(justinfagnani): disallow adding/removing names | 160 // TODO(justinfagnani): disallow adding/removing names |
| 158 final ObservableMap<String, Object> _variables; | 161 final ObservableMap<String, Object> _variables; |
| 159 InstanceMirror __modelMirror; | 162 InstanceMirror __modelMirror; |
| 160 | 163 |
| 161 Scope({this.model, Map<String, Object> variables: const {}, this.parent}) | 164 Scope({this.model, Map<String, Object> variables, this.parent}) |
| 162 : _variables = new ObservableMap.from(variables); | 165 : _variables = new ObservableMap.from(variables == null ? {} : variables); |
| 163 | 166 |
| 164 InstanceMirror get _modelMirror { | 167 InstanceMirror get _modelMirror { |
| 165 if (__modelMirror != null) return __modelMirror; | 168 if (__modelMirror != null) return __modelMirror; |
| 166 __modelMirror = reflect(model); | 169 __modelMirror = reflect(model); |
| 167 return __modelMirror; | 170 return __modelMirror; |
| 168 } | 171 } |
| 169 | 172 |
| 170 Object operator[](String name) { | 173 Object operator[](String name) { |
| 171 if (name == 'this') { | 174 if (name == 'this') { |
| 172 return model; | 175 return model; |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 297 class ObserverBuilder extends Visitor { | 300 class ObserverBuilder extends Visitor { |
| 298 final Scope scope; | 301 final Scope scope; |
| 299 final Queue parents = new Queue(); | 302 final Queue parents = new Queue(); |
| 300 | 303 |
| 301 ObserverBuilder(this.scope); | 304 ObserverBuilder(this.scope); |
| 302 | 305 |
| 303 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); | 306 visitEmptyExpression(EmptyExpression e) => new EmptyObserver(e); |
| 304 | 307 |
| 305 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); | 308 visitParenthesizedExpression(ParenthesizedExpression e) => visit(e.child); |
| 306 | 309 |
| 310 visitGetter(Getter g) { | |
| 311 var receiver = visit(g.receiver); | |
| 312 var getter = new GetterObserver(g, receiver); | |
| 313 receiver._parent = getter; | |
| 314 return getter; | |
| 315 } | |
| 316 | |
| 317 visitIndex(Index i) { | |
| 318 var receiver = visit(i.receiver); | |
| 319 var arg = visit(i.argument); | |
| 320 var index = new IndexObserver(i, receiver, arg); | |
| 321 receiver._parent = index; | |
| 322 arg._parent = index; | |
| 323 return index; | |
| 324 } | |
| 325 | |
| 307 visitInvoke(Invoke i) { | 326 visitInvoke(Invoke i) { |
| 308 var receiver = visit(i.receiver); | 327 var receiver = visit(i.receiver); |
| 309 var args = (i.arguments == null) | 328 var args = (i.arguments == null) |
| 310 ? null | 329 ? null |
| 311 : i.arguments.map(visit).toList(growable: false); | 330 : i.arguments.map(visit).toList(growable: false); |
| 312 var invoke = new InvokeObserver(i, receiver, args); | 331 var invoke = new InvokeObserver(i, receiver, args); |
| 313 receiver._parent = invoke; | 332 receiver._parent = invoke; |
| 314 if (args != null) args.forEach((a) => a._parent = invoke); | 333 if (args != null) args.forEach((a) => a._parent = invoke); |
| 315 return invoke; | 334 return invoke; |
| 316 } | 335 } |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 499 .listen((_) => _invalidate(scope)); | 518 .listen((_) => _invalidate(scope)); |
| 500 } | 519 } |
| 501 _value = f(left._value, right._value); | 520 _value = f(left._value, right._value); |
| 502 } | 521 } |
| 503 } | 522 } |
| 504 | 523 |
| 505 accept(Visitor v) => v.visitBinaryOperator(this); | 524 accept(Visitor v) => v.visitBinaryOperator(this); |
| 506 | 525 |
| 507 } | 526 } |
| 508 | 527 |
| 528 class GetterObserver extends ExpressionObserver<Getter> implements Getter { | |
| 529 final ExpressionObserver receiver; | |
| 530 | |
| 531 GetterObserver(Expression expr, this.receiver) : super(expr); | |
| 532 | |
| 533 String get name => _expr.name; | |
| 534 | |
| 535 _updateSelf(Scope scope) { | |
| 536 var receiverValue = receiver._value; | |
| 537 if (receiverValue == null) { | |
| 538 _value = null; | |
| 539 return; | |
| 540 } | |
| 541 var mirror = reflect(receiverValue); | |
| 542 var symbol = new Symbol(_expr.name); | |
| 543 _value = mirror.getField(symbol).reflectee; | |
| 544 | |
| 545 if (receiverValue is Observable) { | |
| 546 _subscription = (receiverValue as Observable).changes.listen((changes) { | |
| 547 if (changes.any((c) => c is PropertyChangeRecord && c.name == symbol)) { | |
| 548 _invalidate(scope); | |
| 549 } | |
| 550 }); | |
| 551 } | |
| 552 } | |
| 553 | |
| 554 accept(Visitor v) => v.visitGetter(this); | |
| 555 } | |
| 556 | |
| 557 class IndexObserver extends ExpressionObserver<Index> implements Index { | |
| 558 final ExpressionObserver receiver; | |
| 559 final ExpressionObserver argument; | |
| 560 | |
| 561 IndexObserver(Expression expr, this.receiver, this.argument) : super(expr); | |
| 562 | |
| 563 _updateSelf(Scope scope) { | |
| 564 var receiverValue = receiver._value; | |
| 565 if (receiverValue == null) { | |
| 566 _value = null; | |
| 567 return; | |
| 568 } | |
| 569 var key = argument._value; | |
| 570 _value = receiverValue[key]; | |
| 571 | |
| 572 if (receiverValue is Observable) { | |
| 573 _subscription = (receiverValue as Observable).changes.listen((changes) { | |
| 574 if (changes.any((c) => c is MapChangeRecord && c.key == key)) { | |
| 575 _invalidate(scope); | |
| 576 } | |
| 577 }); | |
| 578 } | |
| 579 } | |
| 580 | |
| 581 accept(Visitor v) => v.visitIndex(this); | |
| 582 } | |
| 583 | |
| 509 class InvokeObserver extends ExpressionObserver<Invoke> implements Invoke { | 584 class InvokeObserver extends ExpressionObserver<Invoke> implements Invoke { |
| 510 final ExpressionObserver receiver; | 585 final ExpressionObserver receiver; |
| 511 List<ExpressionObserver> arguments; | 586 final List<ExpressionObserver> arguments; |
| 512 | 587 |
| 513 InvokeObserver(Expression expr, this.receiver, [this.arguments]) | 588 InvokeObserver(Expression expr, this.receiver, this.arguments) |
| 514 : super(expr); | 589 : super(expr) { |
| 515 | 590 assert(arguments != null); |
| 516 bool get isGetter => _expr.isGetter; | 591 } |
| 517 | 592 |
| 518 String get method => _expr.method; | 593 String get method => _expr.method; |
| 519 | 594 |
| 520 _updateSelf(Scope scope) { | 595 _updateSelf(Scope scope) { |
| 521 var args = (arguments == null) | 596 var args = arguments.map((a) => a._value).toList(); |
| 522 ? [] | |
| 523 : arguments.map((a) => a._value) | |
| 524 .toList(growable: false); | |
| 525 var receiverValue = receiver._value; | 597 var receiverValue = receiver._value; |
| 526 if (receiverValue == null) { | 598 if (receiverValue == null) { |
| 527 _value = null; | 599 _value = null; |
| 528 } else if (_expr.method == null) { | 600 return; |
| 529 if (_expr.isGetter) { | 601 } |
| 530 // getter, but not a top-level identifier | 602 if (_expr.method == null) { |
| 531 // TODO(justin): listen to the receiver's owner | 603 // top-level function or model method |
| 532 _value = receiverValue; | 604 // TODO(justin): listen to model changes to see if the method has |
| 533 } else { | 605 // changed? listen to the scope to see if the top-level method has |
| 534 // top-level function or model method | 606 // changed? |
| 535 // TODO(justin): listen to model changes to see if the method has | 607 assert(receiverValue is Function); |
| 536 // changed? listen to the scope to see if the top-level method has | 608 _value = call(receiverValue, args); |
| 537 // changed? | |
| 538 assert(receiverValue is Function); | |
| 539 _value = call(receiverValue, args); | |
| 540 } | |
| 541 } else { | 609 } else { |
| 542 // special case [] because we don't need mirrors | 610 var mirror = reflect(receiverValue); |
| 543 if (_expr.method == '[]') { | 611 var symbol = new Symbol(_expr.method); |
| 544 assert(args.length == 1); | 612 _value = mirror.invoke(symbol, args, null).reflectee; |
| 545 var key = args[0]; | |
| 546 _value = receiverValue[key]; | |
| 547 | 613 |
| 548 if (receiverValue is Observable) { | 614 if (receiverValue is Observable) { |
| 549 _subscription = (receiverValue as Observable).changes.listen( | 615 _subscription = (receiverValue as Observable).changes.listen( |
| 550 (List<ChangeRecord> changes) { | 616 (List<ChangeRecord> changes) { |
| 551 if (changes.any((c) => | 617 if (changes.any( |
| 552 c is MapChangeRecord && c.key == key)) { | 618 (c) => c is PropertyChangeRecord && c.name == symbol)) { |
| 553 _invalidate(scope); | 619 _invalidate(scope); |
| 554 } | 620 } |
| 555 }); | 621 }); |
| 556 } | |
| 557 } else { | |
| 558 var mirror = reflect(receiverValue); | |
| 559 var symbol = new Symbol(_expr.method); | |
| 560 _value = (_expr.isGetter) | |
| 561 ? mirror.getField(symbol).reflectee | |
| 562 : mirror.invoke(symbol, args, null).reflectee; | |
| 563 | |
| 564 if (receiverValue is Observable) { | |
| 565 _subscription = (receiverValue as Observable).changes.listen( | |
| 566 (List<ChangeRecord> changes) { | |
| 567 if (changes.any( | |
| 568 (c) => c is PropertyChangeRecord && c.name == symbol)) { | |
| 569 _invalidate(scope); | |
| 570 } | |
| 571 }); | |
| 572 } | |
| 573 } | 622 } |
| 574 } | 623 } |
| 575 } | 624 } |
| 576 | 625 |
| 577 accept(Visitor v) => v.visitInvoke(this); | 626 accept(Visitor v) => v.visitInvoke(this); |
| 578 } | 627 } |
| 579 | 628 |
| 580 class InObserver extends ExpressionObserver<InExpression> | 629 class InObserver extends ExpressionObserver<InExpression> |
| 581 implements InExpression { | 630 implements InExpression { |
| 582 IdentifierObserver left; | 631 IdentifierObserver left; |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 644 * This does not work for calls that need to pass more than one argument. | 693 * This does not work for calls that need to pass more than one argument. |
| 645 */ | 694 */ |
| 646 call(arg0) => mirror.invoke(symbol, [arg0], null).reflectee; | 695 call(arg0) => mirror.invoke(symbol, [arg0], null).reflectee; |
| 647 } | 696 } |
| 648 | 697 |
| 649 class EvalException implements Exception { | 698 class EvalException implements Exception { |
| 650 final String message; | 699 final String message; |
| 651 EvalException(this.message); | 700 EvalException(this.message); |
| 652 String toString() => "EvalException: $message"; | 701 String toString() => "EvalException: $message"; |
| 653 } | 702 } |
| OLD | NEW |