| Index: third_party/pkg/angular/lib/change_detection/watch_group.dart
|
| diff --git a/third_party/pkg/angular/lib/change_detection/watch_group.dart b/third_party/pkg/angular/lib/change_detection/watch_group.dart
|
| index f1427b9ba9934f4d510ce844610ad07a7890ea1a..b0dbd05f0f160f267c5e4413767aaa7c720c9eff 100644
|
| --- a/third_party/pkg/angular/lib/change_detection/watch_group.dart
|
| +++ b/third_party/pkg/angular/lib/change_detection/watch_group.dart
|
| @@ -1,18 +1,29 @@
|
| library angular.watch_group;
|
|
|
| -import 'dart:mirrors';
|
| import 'package:angular/change_detection/change_detection.dart';
|
|
|
| part 'linked_list.dart';
|
| part 'ast.dart';
|
| part 'prototype_map.dart';
|
|
|
| -typedef ReactionFn(value, previousValue);
|
| -typedef ChangeLog(String expression, current, previous);
|
| +/**
|
| + * A function that is notified of changes to the model.
|
| + *
|
| + * ReactionFn is a function implemented by the developer that executes when a change is detected
|
| + * in a watched expression.
|
| + *
|
| + * * [value]: The current value of the watched expression.
|
| + * * [previousValue]: The previous value of the watched expression.
|
| + *
|
| + * If the expression is watching a collection (a list or a map), then [value] is wrapped in
|
| + * a [CollectionChangeItem] that lists all the changes.
|
| + */
|
| +typedef void ReactionFn(value, previousValue);
|
| +typedef void ChangeLog(String expression, current, previous);
|
|
|
| /**
|
| * Extend this class if you wish to pretend to be a function, but you don't know
|
| - * number of arguments with which the function will get called.
|
| + * number of arguments with which the function will get called with.
|
| */
|
| abstract class FunctionApply {
|
| // dartbug.com/16401
|
| @@ -56,7 +67,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| int get totalFieldCost {
|
| var cost = _fieldCost;
|
| WatchGroup group = _watchGroupHead;
|
| - while(group != null) {
|
| + while (group != null) {
|
| cost += group.totalFieldCost;
|
| group = group._nextWatchGroup;
|
| }
|
| @@ -68,7 +79,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| int get totalCollectionCost {
|
| var cost = _collectionCost;
|
| WatchGroup group = _watchGroupHead;
|
| - while(group != null) {
|
| + while (group != null) {
|
| cost += group.totalCollectionCost;
|
| group = group._nextWatchGroup;
|
| }
|
| @@ -82,7 +93,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| int get totalEvalCost {
|
| var cost = _evalCost;
|
| WatchGroup group = _watchGroupHead;
|
| - while(group != null) {
|
| + while (group != null) {
|
| cost += group.evalCost;
|
| group = group._nextWatchGroup;
|
| }
|
| @@ -92,9 +103,9 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| int _nextChildId = 0;
|
| _EvalWatchRecord _evalWatchHead, _evalWatchTail;
|
| /// Pointer for creating tree of [WatchGroup]s.
|
| - WatchGroup _watchGroupHead, _watchGroupTail, _previousWatchGroup,
|
| - _nextWatchGroup;
|
| WatchGroup _parentWatchGroup;
|
| + WatchGroup _watchGroupHead, _watchGroupTail;
|
| + WatchGroup _prevWatchGroup, _nextWatchGroup;
|
|
|
| WatchGroup._child(_parentWatchGroup, this._changeDetector, this.context,
|
| this._cache, this._rootGroup)
|
| @@ -118,7 +129,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| get isAttached {
|
| var group = this;
|
| var root = _rootGroup;
|
| - while(group != null) {
|
| + while (group != null) {
|
| if (group == root){
|
| return true;
|
| }
|
| @@ -143,7 +154,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| WatchRecord<_Handler> addFieldWatch(AST lhs, String name, String expression) {
|
| var fieldHandler = new _FieldHandler(this, expression);
|
|
|
| - // Create a ChangeRecord for the current field and assign the change record
|
| + // Create a Record for the current field and assign the change record
|
| // to the handler.
|
| var watchRecord = _changeDetector.watch(null, name, fieldHandler);
|
| _fieldCost++;
|
| @@ -184,10 +195,13 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| * - [fn] function to evaluate.
|
| * - [argsAST] list of [AST]es which represent arguments passed to function.
|
| * - [expression] normalized expression used for caching.
|
| + * - [isPure] A pure function is one which holds no internal state. This implies that the
|
| + * function is idempotent.
|
| */
|
| _EvalWatchRecord addFunctionWatch(/* dartbug.com/16401 Function */ fn, List<AST> argsAST,
|
| - String expression) =>
|
| - _addEvalWatch(null, fn, null, argsAST, expression);
|
| + Map<Symbol, AST> namedArgsAST,
|
| + String expression, bool isPure) =>
|
| + _addEvalWatch(null, fn, null, argsAST, namedArgsAST, expression, isPure);
|
|
|
| /**
|
| * Watch a method [name]ed represented by an [expression].
|
| @@ -198,16 +212,20 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| * - [expression] normalized expression used for caching.
|
| */
|
| _EvalWatchRecord addMethodWatch(AST lhs, String name, List<AST> argsAST,
|
| + Map<Symbol, AST> namedArgsAST,
|
| String expression) =>
|
| - _addEvalWatch(lhs, null, name, argsAST, expression);
|
| + _addEvalWatch(lhs, null, name, argsAST, namedArgsAST, expression, false);
|
|
|
|
|
|
|
| _EvalWatchRecord _addEvalWatch(AST lhsAST, /* dartbug.com/16401 Function */ fn, String name,
|
| - List<AST> argsAST, String expression) {
|
| + List<AST> argsAST,
|
| + Map<Symbol, AST> namedArgsAST,
|
| + String expression, bool isPure) {
|
| _InvokeHandler invokeHandler = new _InvokeHandler(this, expression);
|
| - var evalWatchRecord = new _EvalWatchRecord(this, invokeHandler, fn, name,
|
| - argsAST.length);
|
| + var evalWatchRecord = new _EvalWatchRecord(
|
| + _rootGroup._fieldGetterFactory, this, invokeHandler, fn, name,
|
| + argsAST.length, isPure);
|
| invokeHandler.watchRecord = evalWatchRecord;
|
|
|
| if (lhsAST != null) {
|
| @@ -218,20 +236,35 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| }
|
|
|
| // Convert the args from AST to WatchRecords
|
| - var i = 0;
|
| - argsAST.
|
| - map((ast) => _cache.putIfAbsent(ast.expression,
|
| - () => ast.setupWatch(this))).forEach((WatchRecord<_Handler> record) {
|
| - var argHandler = new _ArgHandler(this, evalWatchRecord, i++);
|
| - _ArgHandlerList._add(invokeHandler, argHandler);
|
| - record.handler.addForwardHandler(argHandler);
|
| - argHandler.acceptValue(record.currentValue);
|
| + Iterable<WatchRecord<_Handler>> records = argsAST.map((ast) =>
|
| + _cache.putIfAbsent(ast.expression, () => ast.setupWatch(this)));
|
| + int i = 0;
|
| + records.forEach((WatchRecord<_Handler> record) {
|
| + _ArgHandler handler = new _PositionalArgHandler(this, evalWatchRecord, i++);
|
| + _ArgHandlerList._add(invokeHandler, handler);
|
| + record.handler.addForwardHandler(handler);
|
| + handler.acceptValue(record.currentValue);
|
| + });
|
| +
|
| + namedArgsAST.forEach((Symbol name, AST ast) {
|
| + WatchRecord<_Handler> record = _cache.putIfAbsent(ast.expression,
|
| + () => ast.setupWatch(this));
|
| + _ArgHandler handler = new _NamedArgHandler(this, evalWatchRecord, name);
|
| + _ArgHandlerList._add(invokeHandler, handler);
|
| + record.handler.addForwardHandler(handler);
|
| + handler.acceptValue(record.currentValue);
|
| });
|
|
|
| // Must be done last
|
| _EvalWatchList._add(this, evalWatchRecord);
|
| _evalCost++;
|
| -
|
| + if (_rootGroup.isInsideInvokeDirty) {
|
| + // This check means that we are inside invoke reaction function.
|
| + // Registering a new EvalWatch at this point will not run the
|
| + // .check() on it which means it will not be processed, but its
|
| + // reaction function will be run with null. So we process it manually.
|
| + evalWatchRecord.check();
|
| + }
|
| return evalWatchRecord;
|
| }
|
|
|
| @@ -257,15 +290,15 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| this,
|
| _changeDetector.newGroup(),
|
| context == null ? this.context : context,
|
| - context == null ? this._cache: <String, WatchRecord<_Handler>>{},
|
| + <String, WatchRecord<_Handler>>{},
|
| _rootGroup == null ? this : _rootGroup);
|
| _WatchGroupList._add(this, childGroup);
|
| var marker = childGroup._marker;
|
|
|
| - marker._previousEvalWatch = prev;
|
| + marker._prevEvalWatch = prev;
|
| marker._nextEvalWatch = next;
|
| - if (prev != null) prev._nextEvalWatch = marker;
|
| - if (next != null) next._previousEvalWatch = marker;
|
| + prev._nextEvalWatch = marker;
|
| + if (next != null) next._prevEvalWatch = marker;
|
|
|
| return childGroup;
|
| }
|
| @@ -276,21 +309,23 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| void remove() {
|
| // TODO:(misko) This code is not right.
|
| // 1) It fails to release [ChangeDetector] [WatchRecord]s.
|
| - // 2) it needs to cleanup caches if the cache is being shared.
|
|
|
| _WatchGroupList._remove(_parentWatchGroup, this);
|
| + _nextWatchGroup = _prevWatchGroup = null;
|
| _changeDetector.remove();
|
| _rootGroup._removeCount++;
|
| _parentWatchGroup = null;
|
|
|
| // Unlink the _watchRecord
|
| _EvalWatchRecord firstEvalWatch = _evalWatchHead;
|
| - _EvalWatchRecord lastEvalWatch =
|
| - (_watchGroupTail == null ? this : _watchGroupTail)._evalWatchTail;
|
| - _EvalWatchRecord previous = firstEvalWatch._previousEvalWatch;
|
| + _EvalWatchRecord lastEvalWatch = _childWatchGroupTail._evalWatchTail;
|
| + _EvalWatchRecord previous = firstEvalWatch._prevEvalWatch;
|
| _EvalWatchRecord next = lastEvalWatch._nextEvalWatch;
|
| if (previous != null) previous._nextEvalWatch = next;
|
| - if (next != null) next._previousEvalWatch = previous;
|
| + if (next != null) next._prevEvalWatch = previous;
|
| + _evalWatchHead._prevEvalWatch = null;
|
| + _evalWatchTail._nextEvalWatch = null;
|
| + _evalWatchHead = _evalWatchTail = null;
|
| }
|
|
|
| toString() {
|
| @@ -301,7 +336,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| var prev = null;
|
| while (watch != null) {
|
| allWatches.add(watch.toString());
|
| - assert(watch._previousEvalWatch == prev);
|
| + assert(watch._prevEvalWatch == prev);
|
| prev = watch;
|
| watch = watch._nextEvalWatch;
|
| }
|
| @@ -319,7 +354,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| lines.add('WatchGroup[$id](watches: ${watches.join(', ')})');
|
| var childGroup = _watchGroupHead;
|
| while (childGroup != null) {
|
| - lines.add(' ' + childGroup.toString().split('\n').join('\n '));
|
| + lines.add(' ' + childGroup.toString().replace('\n', '\n '));
|
| childGroup = childGroup._nextWatchGroup;
|
| }
|
| return lines.join('\n');
|
| @@ -330,6 +365,7 @@ class WatchGroup implements _EvalWatchList, _WatchGroupList {
|
| * [RootWatchGroup]
|
| */
|
| class RootWatchGroup extends WatchGroup {
|
| + final FieldGetterFactory _fieldGetterFactory;
|
| Watch _dirtyWatchHead, _dirtyWatchTail;
|
|
|
| /**
|
| @@ -342,8 +378,10 @@ class RootWatchGroup extends WatchGroup {
|
| int _removeCount = 0;
|
|
|
|
|
| - RootWatchGroup(ChangeDetector changeDetector, Object context):
|
| - super._root(changeDetector, context);
|
| + RootWatchGroup(this._fieldGetterFactory,
|
| + ChangeDetector changeDetector,
|
| + Object context)
|
| + : super._root(changeDetector, context);
|
|
|
| RootWatchGroup get _rootGroup => this;
|
|
|
| @@ -359,22 +397,22 @@ class RootWatchGroup extends WatchGroup {
|
| * previous steps are completed).
|
| */
|
| int detectChanges({ EvalExceptionHandler exceptionHandler,
|
| - ChangeLog changeLog,
|
| + ChangeLog changeLog,
|
| AvgStopwatch fieldStopwatch,
|
| AvgStopwatch evalStopwatch,
|
| AvgStopwatch processStopwatch}) {
|
| - // Process the ChangeRecords from the change detector
|
| - ChangeRecord<_Handler> changeRecord =
|
| + // Process the Records from the change detector
|
| + Iterator<Record<_Handler>> changedRecordIterator =
|
| (_changeDetector as ChangeDetector<_Handler>).collectChanges(
|
| - exceptionHandler:exceptionHandler,
|
| + exceptionHandler:exceptionHandler,
|
| stopwatch: fieldStopwatch);
|
| if (processStopwatch != null) processStopwatch.start();
|
| - while (changeRecord != null) {
|
| - if (changeLog != null) changeLog(changeRecord.handler.expression,
|
| - changeRecord.currentValue,
|
| - changeRecord.previousValue);
|
| - changeRecord.handler.onChange(changeRecord);
|
| - changeRecord = changeRecord.nextChange;
|
| + while (changedRecordIterator.moveNext()) {
|
| + var record = changedRecordIterator.current;
|
| + if (changeLog != null) changeLog(record.handler.expression,
|
| + record.currentValue,
|
| + record.previousValue);
|
| + record.handler.onChange(record);
|
| }
|
| if (processStopwatch != null) processStopwatch.stop();
|
|
|
| @@ -385,8 +423,7 @@ class RootWatchGroup extends WatchGroup {
|
| while (evalRecord != null) {
|
| try {
|
| if (evalStopwatch != null) evalCount++;
|
| - var change = evalRecord.check();
|
| - if (change != null && changeLog != null) {
|
| + if (evalRecord.check() && changeLog != null) {
|
| changeLog(evalRecord.handler.expression,
|
| evalRecord.currentValue,
|
| evalRecord.previousValue);
|
| @@ -402,26 +439,35 @@ class RootWatchGroup extends WatchGroup {
|
| // We need to call reaction functions asynchronously. This processes the
|
| // asynchronous reaction function queue.
|
| int count = 0;
|
| - if (processStopwatch != null) processStopwatch.stop();
|
| + if (processStopwatch != null) processStopwatch.start();
|
| Watch dirtyWatch = _dirtyWatchHead;
|
| + _dirtyWatchHead = null;
|
| RootWatchGroup root = _rootGroup;
|
| - root._removeCount = 0;
|
| - while(dirtyWatch != null) {
|
| - count++;
|
| - try {
|
| - if (root._removeCount == 0 || dirtyWatch._watchGroup.isAttached) {
|
| - dirtyWatch.invoke();
|
| + try {
|
| + while (dirtyWatch != null) {
|
| + count++;
|
| + try {
|
| + if (root._removeCount == 0 || dirtyWatch._watchGroup.isAttached) {
|
| + dirtyWatch.invoke();
|
| + }
|
| + } catch (e, s) {
|
| + if (exceptionHandler == null) rethrow; else exceptionHandler(e, s);
|
| }
|
| - } catch (e, s) {
|
| - if (exceptionHandler == null) rethrow; else exceptionHandler(e, s);
|
| + var nextDirtyWatch = dirtyWatch._nextDirtyWatch;
|
| + dirtyWatch._nextDirtyWatch = null;
|
| + dirtyWatch = nextDirtyWatch;
|
| }
|
| - dirtyWatch = dirtyWatch._nextDirtyWatch;
|
| + } finally {
|
| + _dirtyWatchTail = null;
|
| + root._removeCount = 0;
|
| }
|
| - _dirtyWatchHead = _dirtyWatchTail = null;
|
| if (processStopwatch != null) processStopwatch..stop()..increment(count);
|
| return count;
|
| }
|
|
|
| + bool get isInsideInvokeDirty =>
|
| + _dirtyWatchHead == null && _dirtyWatchTail != null;
|
| +
|
| /**
|
| * Add Watch into the asynchronous queue for later processing.
|
| */
|
| @@ -490,6 +536,7 @@ class Watch {
|
| * changes detected at one handler are propagated to the next handler.
|
| */
|
| abstract class _Handler implements _LinkedList, _LinkedListItem, _WatchList {
|
| + // Used for forwarding changes to delegates
|
| _Handler _head, _tail;
|
| _Handler _next, _previous;
|
| Watch _watchHead, _watchTail;
|
| @@ -517,7 +564,8 @@ abstract class _Handler implements _LinkedList, _LinkedListItem, _WatchList {
|
| forwardToHandler.forwardingHandler = this;
|
| }
|
|
|
| - void release() {
|
| + /// Return true if release has happened
|
| + bool release() {
|
| if (_WatchList._isEmpty(this) && _LinkedList._isEmpty(this)) {
|
| _releaseWatch();
|
| // Remove ourselves from cache, or else new registrations will go to us,
|
| @@ -532,6 +580,9 @@ abstract class _Handler implements _LinkedList, _LinkedListItem, _WatchList {
|
|
|
| // We can remove ourselves
|
| assert((_next = _previous = this) == this); // mark ourselves as detached
|
| + return true;
|
| + } else {
|
| + return false;
|
| }
|
| }
|
|
|
| @@ -541,12 +592,12 @@ abstract class _Handler implements _LinkedList, _LinkedListItem, _WatchList {
|
| }
|
| acceptValue(object) => null;
|
|
|
| - void onChange(ChangeRecord<_Handler> record) {
|
| + void onChange(Record<_Handler> record) {
|
| assert(_next != this); // verify we are not detached
|
| // If we have reaction functions than queue them up for asynchronous
|
| // processing.
|
| Watch watch = _watchHead;
|
| - while(watch != null) {
|
| + while (watch != null) {
|
| watchGrp._rootGroup._addDirtyWatch(watch);
|
| watch = watch._nextWatch;
|
| }
|
| @@ -560,7 +611,7 @@ abstract class _Handler implements _LinkedList, _LinkedListItem, _WatchList {
|
| }
|
|
|
| class _ConstantHandler extends _Handler {
|
| - _ConstantHandler(WatchGroup watchGroup, String expression, dynamic constantValue)
|
| + _ConstantHandler(WatchGroup watchGroup, String expression, constantValue)
|
| : super(watchGroup, expression)
|
| {
|
| watchRecord = new _EvalWatchRecord.constant(this, constantValue);
|
| @@ -577,8 +628,7 @@ class _FieldHandler extends _Handler {
|
| */
|
| void acceptValue(object) {
|
| watchRecord.object = object;
|
| - var changeRecord = watchRecord.check();
|
| - if (changeRecord != null) onChange(changeRecord);
|
| + if (watchRecord.check()) onChange(watchRecord);
|
| }
|
| }
|
|
|
| @@ -590,8 +640,7 @@ class _CollectionHandler extends _Handler {
|
| */
|
| void acceptValue(object) {
|
| watchRecord.object = object;
|
| - var changeRecord = watchRecord.check();
|
| - if (changeRecord != null) onChange(changeRecord);
|
| + if (watchRecord.check()) onChange(watchRecord);
|
| }
|
|
|
| void _releaseWatch() {
|
| @@ -600,18 +649,22 @@ class _CollectionHandler extends _Handler {
|
| }
|
| }
|
|
|
| -class _ArgHandler extends _Handler {
|
| +abstract class _ArgHandler extends _Handler {
|
| _ArgHandler _previousArgHandler, _nextArgHandler;
|
|
|
| // TODO(misko): Why do we override parent?
|
| final _EvalWatchRecord watchRecord;
|
| - final int index;
|
| + _ArgHandler(WatchGroup watchGrp, String expression, this.watchRecord)
|
| + : super(watchGrp, expression);
|
|
|
| _releaseWatch() => null;
|
| +}
|
|
|
| - _ArgHandler(WatchGroup watchGrp, this.watchRecord, int index)
|
| - : index = index,
|
| - super(watchGrp, 'arg[$index]');
|
| +class _PositionalArgHandler extends _ArgHandler {
|
| + final int index;
|
| + _PositionalArgHandler(WatchGroup watchGrp, _EvalWatchRecord record, int index)
|
| + : this.index = index,
|
| + super(watchGrp, 'arg[$index]', record);
|
|
|
| void acceptValue(object) {
|
| watchRecord.dirtyArgs = true;
|
| @@ -619,6 +672,19 @@ class _ArgHandler extends _Handler {
|
| }
|
| }
|
|
|
| +class _NamedArgHandler extends _ArgHandler {
|
| + final Symbol name;
|
| +
|
| + _NamedArgHandler(WatchGroup watchGrp, _EvalWatchRecord record, Symbol name)
|
| + : this.name = name,
|
| + super(watchGrp, 'namedArg[$name]', record);
|
| +
|
| + void acceptValue(object) {
|
| + watchRecord.dirtyArgs = true;
|
| + watchRecord.namedArgs[name] = object;
|
| + }
|
| +}
|
| +
|
| class _InvokeHandler extends _Handler implements _ArgHandlerList {
|
| _ArgHandler _argHandlerHead, _argHandlerTail;
|
|
|
| @@ -629,55 +695,58 @@ class _InvokeHandler extends _Handler implements _ArgHandlerList {
|
| watchRecord.object = object;
|
| }
|
|
|
| - void onChange(ChangeRecord<_Handler> record) {
|
| - super.onChange(record);
|
| - }
|
| -
|
| void _releaseWatch() {
|
| (watchRecord as _EvalWatchRecord).remove();
|
| }
|
|
|
| - void release() {
|
| - super.release();
|
| - _ArgHandler current = _argHandlerHead;
|
| - while(current != null) {
|
| - current.release();
|
| - current = current._nextArgHandler;
|
| + bool release() {
|
| + if (super.release()) {
|
| + _ArgHandler current = _argHandlerHead;
|
| + while (current != null) {
|
| + current.release();
|
| + current = current._nextArgHandler;
|
| + }
|
| + return true;
|
| + } else {
|
| + return false;
|
| }
|
| }
|
| }
|
|
|
|
|
| -class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler> {
|
| - static const int _MODE_DELETED_ = -1;
|
| - static const int _MODE_MARKER_ = 0;
|
| - static const int _MODE_FUNCTION_ = 1;
|
| - static const int _MODE_FUNCTION_APPLY_ = 2;
|
| - static const int _MODE_NULL_ = 3;
|
| - static const int _MODE_FIELD_CLOSURE_ = 4;
|
| - static const int _MODE_MAP_CLOSURE_ = 5;
|
| - static const int _MODE_METHOD_ = 6;
|
| +class _EvalWatchRecord implements WatchRecord<_Handler> {
|
| + static const int _MODE_INVALID_ = -2;
|
| + static const int _MODE_DELETED_ = -1;
|
| + static const int _MODE_MARKER_ = 0;
|
| + static const int _MODE_PURE_FUNCTION_ = 1;
|
| + static const int _MODE_FUNCTION_ = 2;
|
| + static const int _MODE_PURE_FUNCTION_APPLY_ = 3;
|
| + static const int _MODE_NULL_ = 4;
|
| + static const int _MODE_FIELD_CLOSURE_ = 5;
|
| + static const int _MODE_MAP_CLOSURE_ = 6;
|
| + static const int _MODE_METHOD_ = 7;
|
| + static const int _MODE_METHOD_INVOKE_ = 8;
|
| WatchGroup watchGrp;
|
| final _Handler handler;
|
| final List args;
|
| - final Symbol symbol;
|
| + final Map<Symbol, dynamic> namedArgs = new Map<Symbol, dynamic>();
|
| final String name;
|
| int mode;
|
| /* dartbug.com/16401 Function*/ var fn;
|
| - InstanceMirror _instanceMirror;
|
| + FieldGetterFactory _fieldGetterFactory;
|
| bool dirtyArgs = true;
|
|
|
| dynamic currentValue, previousValue, _object;
|
| - _EvalWatchRecord _previousEvalWatch, _nextEvalWatch;
|
| + _EvalWatchRecord _prevEvalWatch, _nextEvalWatch;
|
|
|
| - _EvalWatchRecord(this.watchGrp, this.handler, this.fn, name, int arity)
|
| - : args = new List(arity),
|
| - name = name,
|
| - symbol = name == null ? null : new Symbol(name) {
|
| + _EvalWatchRecord(this._fieldGetterFactory, this.watchGrp, this.handler,
|
| + this.fn, this.name, int arity, bool pure)
|
| + : args = new List(arity)
|
| + {
|
| if (fn is FunctionApply) {
|
| - mode = _MODE_FUNCTION_APPLY_;
|
| + mode = pure ? _MODE_PURE_FUNCTION_APPLY_: _MODE_INVALID_;
|
| } else if (fn is Function) {
|
| - mode = _MODE_FUNCTION_;
|
| + mode = pure ? _MODE_PURE_FUNCTION_ : _MODE_FUNCTION_;
|
| } else {
|
| mode = _MODE_NULL_;
|
| }
|
| @@ -685,21 +754,21 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler>
|
|
|
| _EvalWatchRecord.marker()
|
| : mode = _MODE_MARKER_,
|
| + _fieldGetterFactory = null,
|
| watchGrp = null,
|
| handler = null,
|
| args = null,
|
| fn = null,
|
| - symbol = null,
|
| name = null;
|
|
|
| _EvalWatchRecord.constant(_Handler handler, dynamic constantValue)
|
| : mode = _MODE_MARKER_,
|
| + _fieldGetterFactory = null,
|
| handler = handler,
|
| currentValue = constantValue,
|
| watchGrp = null,
|
| args = null,
|
| fn = null,
|
| - symbol = null,
|
| name = null;
|
|
|
| get field => '()';
|
| @@ -710,8 +779,8 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler>
|
| assert(mode != _MODE_DELETED_);
|
| assert(mode != _MODE_MARKER_);
|
| assert(mode != _MODE_FUNCTION_);
|
| - assert(mode != _MODE_FUNCTION_APPLY_);
|
| - assert(symbol != null);
|
| + assert(mode != _MODE_PURE_FUNCTION_);
|
| + assert(mode != _MODE_PURE_FUNCTION_APPLY_);
|
| _object = value;
|
|
|
| if (value == null) {
|
| @@ -720,40 +789,50 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler>
|
| if (value is Map) {
|
| mode = _MODE_MAP_CLOSURE_;
|
| } else {
|
| - _instanceMirror = reflect(value);
|
| - mode = _hasMethod(_instanceMirror.type, symbol)
|
| - ? _MODE_METHOD_
|
| - : _MODE_FIELD_CLOSURE_;
|
| + if (_fieldGetterFactory.isMethod(value, name)) {
|
| + mode = _fieldGetterFactory.isMethodInvoke ? _MODE_METHOD_INVOKE_ : _MODE_METHOD_;
|
| + fn = _fieldGetterFactory.method(value, name);
|
| + } else {
|
| + mode = _MODE_FIELD_CLOSURE_;
|
| + fn = _fieldGetterFactory.getter(value, name);
|
| + }
|
| }
|
| }
|
| }
|
|
|
| - ChangeRecord<_Handler> check() {
|
| + bool check() {
|
| var value;
|
| switch (mode) {
|
| case _MODE_MARKER_:
|
| case _MODE_NULL_:
|
| - return null;
|
| + return false;
|
| + case _MODE_PURE_FUNCTION_:
|
| + if (!dirtyArgs) return false;
|
| + value = Function.apply(fn, args, namedArgs);
|
| + dirtyArgs = false;
|
| + break;
|
| case _MODE_FUNCTION_:
|
| - if (!dirtyArgs) return null;
|
| - value = Function.apply(fn, args);
|
| + value = Function.apply(fn, args, namedArgs);
|
| dirtyArgs = false;
|
| break;
|
| - case _MODE_FUNCTION_APPLY_:
|
| - if (!dirtyArgs) return null;
|
| + case _MODE_PURE_FUNCTION_APPLY_:
|
| + if (!dirtyArgs) return false;
|
| value = (fn as FunctionApply).apply(args);
|
| dirtyArgs = false;
|
| break;
|
| case _MODE_FIELD_CLOSURE_:
|
| - var closure = _instanceMirror.getField(symbol).reflectee;
|
| - value = closure == null ? null : Function.apply(closure, args);
|
| + var closure = fn(_object);
|
| + value = closure == null ? null : Function.apply(closure, args, namedArgs);
|
| break;
|
| case _MODE_MAP_CLOSURE_:
|
| var closure = object[name];
|
| - value = closure == null ? null : Function.apply(closure, args);
|
| + value = closure == null ? null : Function.apply(closure, args, namedArgs);
|
| break;
|
| case _MODE_METHOD_:
|
| - value = _instanceMirror.invoke(symbol, args).reflectee;
|
| + value = Function.apply(fn, args, namedArgs);
|
| + break;
|
| + case _MODE_METHOD_INVOKE_:
|
| + value = fn(args, namedArgs);
|
| break;
|
| default:
|
| assert(false);
|
| @@ -768,10 +847,10 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler>
|
| previousValue = current;
|
| currentValue = value;
|
| handler.onChange(this);
|
| - return this;
|
| + return true;
|
| }
|
| }
|
| - return null;
|
| + return false;
|
| }
|
|
|
| get nextChange => null;
|
| @@ -787,45 +866,4 @@ class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler>
|
| if (mode == _MODE_MARKER_) return 'MARKER[$currentValue]';
|
| return '${watchGrp.id}:${handler.expression}';
|
| }
|
| -
|
| - static final Function _hasMethod = (() {
|
| - var objectClassMirror = reflectClass(Object);
|
| - Set<Symbol> objectClassInstanceMethods =
|
| - new Set<Symbol>.from([#toString, #noSuchMethod]);
|
| - try {
|
| - // Use ClassMirror.instanceMembers if available. It contains local
|
| - // as well as inherited members.
|
| - objectClassMirror.instanceMembers;
|
| - // For SDK 1.2 we have to use a somewhat complicated helper for this
|
| - // to work around bugs in the dart2js implementation.
|
| - return (type, symbol) {
|
| - // Always allow instance methods found in the Object class. This makes
|
| - // it easier to work around a few bugs in the dart2js implementation.
|
| - if (objectClassInstanceMethods.contains(symbol)) return true;
|
| - // Work around http://dartbug.com/16309 which causes access to the
|
| - // instance members of certain builtin types to throw exceptions
|
| - // while traversing the superclass chain.
|
| - var mirror;
|
| - try {
|
| - mirror = type.instanceMembers[symbol];
|
| - } on UnsupportedError catch (e) {
|
| - mirror = type.declarations[symbol];
|
| - }
|
| - // Work around http://dartbug.com/15760 which causes noSuchMethod
|
| - // forwarding stubs to be treated as members of all classes. We have
|
| - // already checked for the real instance methods in Object, so if the
|
| - // owner of this method is Object we simply filter it out.
|
| - if (mirror is !MethodMirror) return false;
|
| - return mirror.owner != objectClassMirror;
|
| - };
|
| - } on NoSuchMethodError catch (e) {
|
| - // For SDK 1.0 we fall back to just using the local members.
|
| - return (type, symbol) => type.members[symbol] is MethodMirror;
|
| - } on UnimplementedError catch (e) {
|
| - // For SDK 1.1 we fall back to just using the local declarations.
|
| - return (type, symbol) => type.declarations[symbol] is MethodMirror;
|
| - }
|
| - return null;
|
| - })();
|
| -
|
| }
|
|
|