Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(111)

Side by Side Diff: third_party/pkg/angular/lib/change_detection/watch_group.dart

Issue 180843004: Revert revision 33053 (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 library angular.watch_group;
2
3 import 'dart:mirrors';
4 import 'package:angular/change_detection/change_detection.dart';
5
6 part 'linked_list.dart';
7 part 'ast.dart';
8 part 'prototype_map.dart';
9
10 typedef ReactionFn(value, previousValue);
11 typedef ChangeLog(String expression, current, previous);
12
13 /**
14 * Extend this class if you wish to pretend to be a function, but you don't know
15 * number of arguments with which the function will get called.
16 */
17 abstract class FunctionApply {
18 // dartbug.com/16401
19 // dynamic call() { throw new StateError('Use apply()'); }
20 dynamic apply(List arguments);
21 }
22
23 /**
24 * [WatchGroup] is a logical grouping of a set of watches. [WatchGroup]s are
25 * organized into a hierarchical tree parent-children configuration.
26 * [WatchGroup] builds upon [ChangeDetector] and adds expression (field chains
27 * as in `a.b.c`) support as well as support function/closure/method (function
28 * invocation as in `a.b()`) watching.
29 */
30 class WatchGroup implements _EvalWatchList, _WatchGroupList {
31 /** A unique ID for the WatchGroup */
32 final String id;
33 /**
34 * A marker to be inserted when a group has no watches. We need the marker to
35 * hold our position information in the linked list of all [Watch]es.
36 */
37 final _EvalWatchRecord _marker = new _EvalWatchRecord.marker();
38
39 /** All Expressions are evaluated against a context object. */
40 final Object context;
41
42 /** [ChangeDetector] used for field watching */
43 final ChangeDetectorGroup<_Handler> _changeDetector;
44 /** A cache for sharing sub expression watching. Watching `a` and `a.b` will
45 * watch `a` only once. */
46 final Map<String, WatchRecord<_Handler>> _cache;
47 final RootWatchGroup _rootGroup;
48
49 /// STATS: Number of field watchers which are in use.
50 int _fieldCost = 0;
51 int _collectionCost = 0;
52 int _evalCost = 0;
53
54 /// STATS: Number of field watchers which are in use including child [WatchGro up]s.
55 int get fieldCost => _fieldCost;
56 int get totalFieldCost {
57 var cost = _fieldCost;
58 WatchGroup group = _watchGroupHead;
59 while(group != null) {
60 cost += group.totalFieldCost;
61 group = group._nextWatchGroup;
62 }
63 return cost;
64 }
65
66 /// STATS: Number of collection watchers which are in use including child [Wat chGroup]s.
67 int get collectionCost => _collectionCost;
68 int get totalCollectionCost {
69 var cost = _collectionCost;
70 WatchGroup group = _watchGroupHead;
71 while(group != null) {
72 cost += group.totalCollectionCost;
73 group = group._nextWatchGroup;
74 }
75 return cost;
76 }
77
78 /// STATS: Number of invocation watchers (closures/methods) which are in use.
79 int get evalCost => _evalCost;
80
81 /// STATS: Number of invocation watchers which are in use including child [Wat chGroup]s.
82 int get totalEvalCost {
83 var cost = _evalCost;
84 WatchGroup group = _watchGroupHead;
85 while(group != null) {
86 cost += group.evalCost;
87 group = group._nextWatchGroup;
88 }
89 return cost;
90 }
91
92 int _nextChildId = 0;
93 _EvalWatchRecord _evalWatchHead, _evalWatchTail;
94 /// Pointer for creating tree of [WatchGroup]s.
95 WatchGroup _watchGroupHead, _watchGroupTail, _previousWatchGroup,
96 _nextWatchGroup;
97 WatchGroup _parentWatchGroup;
98
99 WatchGroup._child(_parentWatchGroup, this._changeDetector, this.context,
100 this._cache, this._rootGroup)
101 : _parentWatchGroup = _parentWatchGroup,
102 id = '${_parentWatchGroup.id}.${_parentWatchGroup._nextChildId++}'
103 {
104 _marker.watchGrp = this;
105 _evalWatchTail = _evalWatchHead = _marker;
106 }
107
108 WatchGroup._root(this._changeDetector, this.context)
109 : id = '',
110 _rootGroup = null,
111 _parentWatchGroup = null,
112 _cache = new Map<String, WatchRecord<_Handler>>()
113 {
114 _marker.watchGrp = this;
115 _evalWatchTail = _evalWatchHead = _marker;
116 }
117
118 get isAttached {
119 var group = this;
120 var root = _rootGroup;
121 while(group != null) {
122 if (group == root){
123 return true;
124 }
125 group = group._parentWatchGroup;
126 }
127 return false;
128 }
129
130 Watch watch(AST expression, ReactionFn reactionFn) {
131 WatchRecord<_Handler> watchRecord =
132 _cache.putIfAbsent(expression.expression,
133 () => expression.setupWatch(this));
134 return watchRecord.handler.addReactionFn(reactionFn);
135 }
136
137 /**
138 * Watch a [name] field on [lhs] represented by [expression].
139 *
140 * - [name] the field to watch.
141 * - [lhs] left-hand-side of the field.
142 */
143 WatchRecord<_Handler> addFieldWatch(AST lhs, String name, String expression) {
144 var fieldHandler = new _FieldHandler(this, expression);
145
146 // Create a ChangeRecord for the current field and assign the change record
147 // to the handler.
148 var watchRecord = _changeDetector.watch(null, name, fieldHandler);
149 _fieldCost++;
150 fieldHandler.watchRecord = watchRecord;
151
152 WatchRecord<_Handler> lhsWR = _cache.putIfAbsent(lhs.expression,
153 () => lhs.setupWatch(this));
154
155 // We set a field forwarding handler on LHS. This will allow the change
156 // objects to propagate to the current WatchRecord.
157 lhsWR.handler.addForwardHandler(fieldHandler);
158
159 // propagate the value from the LHS to here
160 fieldHandler.acceptValue(lhsWR.currentValue);
161 return watchRecord;
162 }
163
164 WatchRecord<_Handler> addCollectionWatch(AST ast) {
165 var collectionHandler = new _CollectionHandler(this, ast.expression);
166 var watchRecord = _changeDetector.watch(null, null, collectionHandler);
167 _collectionCost++;
168 collectionHandler.watchRecord = watchRecord;
169 WatchRecord<_Handler> astWR = _cache.putIfAbsent(ast.expression,
170 () => ast.setupWatch(this));
171
172 // We set a field forwarding handler on LHS. This will allow the change
173 // objects to propagate to the current WatchRecord.
174 astWR.handler.addForwardHandler(collectionHandler);
175
176 // propagate the value from the LHS to here
177 collectionHandler.acceptValue(astWR.currentValue);
178 return watchRecord;
179 }
180
181 /**
182 * Watch a [fn] function represented by an [expression].
183 *
184 * - [fn] function to evaluate.
185 * - [argsAST] list of [AST]es which represent arguments passed to function.
186 * - [expression] normalized expression used for caching.
187 */
188 _EvalWatchRecord addFunctionWatch(/* dartbug.com/16401 Function */ fn, List<AS T> argsAST,
189 String expression) =>
190 _addEvalWatch(null, fn, null, argsAST, expression);
191
192 /**
193 * Watch a method [name]ed represented by an [expression].
194 *
195 * - [lhs] left-hand-side of the method.
196 * - [name] name of the method.
197 * - [argsAST] list of [AST]es which represent arguments passed to method.
198 * - [expression] normalized expression used for caching.
199 */
200 _EvalWatchRecord addMethodWatch(AST lhs, String name, List<AST> argsAST,
201 String expression) =>
202 _addEvalWatch(lhs, null, name, argsAST, expression);
203
204
205
206 _EvalWatchRecord _addEvalWatch(AST lhsAST, /* dartbug.com/16401 Function */ fn , String name,
207 List<AST> argsAST, String expression) {
208 _InvokeHandler invokeHandler = new _InvokeHandler(this, expression);
209 var evalWatchRecord = new _EvalWatchRecord(this, invokeHandler, fn, name,
210 argsAST.length);
211 invokeHandler.watchRecord = evalWatchRecord;
212
213 if (lhsAST != null) {
214 var lhsWR = _cache.putIfAbsent(lhsAST.expression,
215 () => lhsAST.setupWatch(this));
216 lhsWR.handler.addForwardHandler(invokeHandler);
217 invokeHandler.acceptValue(lhsWR.currentValue);
218 }
219
220 // Convert the args from AST to WatchRecords
221 var i = 0;
222 argsAST.
223 map((ast) => _cache.putIfAbsent(ast.expression,
224 () => ast.setupWatch(this))).forEach((WatchRecord<_Handler> record) {
225 var argHandler = new _ArgHandler(this, evalWatchRecord, i++);
226 _ArgHandlerList._add(invokeHandler, argHandler);
227 record.handler.addForwardHandler(argHandler);
228 argHandler.acceptValue(record.currentValue);
229 });
230
231 // Must be done last
232 _EvalWatchList._add(this, evalWatchRecord);
233 _evalCost++;
234
235 return evalWatchRecord;
236 }
237
238 WatchGroup get _childWatchGroupTail {
239 var tail = this, nextTail;
240 while ((nextTail = tail._watchGroupTail) != null) {
241 tail = nextTail;
242 }
243 return tail;
244 }
245
246 /**
247 * Create a new child [WatchGroup].
248 *
249 * - [context] if present the the child [WatchGroup] expressions will evaluate
250 * against the new [context]. If not present than child expressions will
251 * evaluate on same context allowing the reuse of the expression cache.
252 */
253 WatchGroup newGroup([Object context]) {
254 _EvalWatchRecord prev = _childWatchGroupTail._evalWatchTail;
255 _EvalWatchRecord next = prev._nextEvalWatch;
256 var childGroup = new WatchGroup._child(
257 this,
258 _changeDetector.newGroup(),
259 context == null ? this.context : context,
260 context == null ? this._cache: <String, WatchRecord<_Handler>>{},
261 _rootGroup == null ? this : _rootGroup);
262 _WatchGroupList._add(this, childGroup);
263 var marker = childGroup._marker;
264
265 marker._previousEvalWatch = prev;
266 marker._nextEvalWatch = next;
267 if (prev != null) prev._nextEvalWatch = marker;
268 if (next != null) next._previousEvalWatch = marker;
269
270 return childGroup;
271 }
272
273 /**
274 * Remove/destroy [WatchGroup] and all of its [Watches].
275 */
276 void remove() {
277 // TODO:(misko) This code is not right.
278 // 1) It fails to release [ChangeDetector] [WatchRecord]s.
279 // 2) it needs to cleanup caches if the cache is being shared.
280
281 _WatchGroupList._remove(_parentWatchGroup, this);
282 _changeDetector.remove();
283 _rootGroup._removeCount++;
284 _parentWatchGroup = null;
285
286 // Unlink the _watchRecord
287 _EvalWatchRecord firstEvalWatch = _evalWatchHead;
288 _EvalWatchRecord lastEvalWatch =
289 (_watchGroupTail == null ? this : _watchGroupTail)._evalWatchTail;
290 _EvalWatchRecord previous = firstEvalWatch._previousEvalWatch;
291 _EvalWatchRecord next = lastEvalWatch._nextEvalWatch;
292 if (previous != null) previous._nextEvalWatch = next;
293 if (next != null) next._previousEvalWatch = previous;
294 }
295
296 toString() {
297 var lines = [];
298 if (this == _rootGroup) {
299 var allWatches = [];
300 var watch = _evalWatchHead;
301 var prev = null;
302 while (watch != null) {
303 allWatches.add(watch.toString());
304 assert(watch._previousEvalWatch == prev);
305 prev = watch;
306 watch = watch._nextEvalWatch;
307 }
308 lines.add('WATCHES: ${allWatches.join(', ')}');
309 }
310
311 var watches = [];
312 var watch = _evalWatchHead;
313 while (watch != _evalWatchTail) {
314 watches.add(watch.toString());
315 watch = watch._nextEvalWatch;
316 }
317 watches.add(watch.toString());
318
319 lines.add('WatchGroup[$id](watches: ${watches.join(', ')})');
320 var childGroup = _watchGroupHead;
321 while (childGroup != null) {
322 lines.add(' ' + childGroup.toString().split('\n').join('\n '));
323 childGroup = childGroup._nextWatchGroup;
324 }
325 return lines.join('\n');
326 }
327 }
328
329 /**
330 * [RootWatchGroup]
331 */
332 class RootWatchGroup extends WatchGroup {
333 Watch _dirtyWatchHead, _dirtyWatchTail;
334
335 /**
336 * Every time a [WatchGroup] is destroyed we increment the counter. During
337 * [detectChanges] we reset the count. Before calling the reaction function,
338 * we check [_removeCount] and if it is unchanged we can safely call the
339 * reaction function. If it is changed we only call the reaction function
340 * if the [WatchGroup] is still attached.
341 */
342 int _removeCount = 0;
343
344
345 RootWatchGroup(ChangeDetector changeDetector, Object context):
346 super._root(changeDetector, context);
347
348 RootWatchGroup get _rootGroup => this;
349
350 /**
351 * Detect changes and process the [ReactionFn]s.
352 *
353 * Algorithm:
354 * 1) process the [ChangeDetector#collectChanges].
355 * 2) process function/closure/method changes
356 * 3) call an [ReactionFn]s
357 *
358 * Each step is called in sequence. ([ReactionFn]s are not called until all
359 * previous steps are completed).
360 */
361 int detectChanges({ EvalExceptionHandler exceptionHandler,
362 ChangeLog changeLog,
363 AvgStopwatch fieldStopwatch,
364 AvgStopwatch evalStopwatch,
365 AvgStopwatch processStopwatch}) {
366 // Process the ChangeRecords from the change detector
367 ChangeRecord<_Handler> changeRecord =
368 (_changeDetector as ChangeDetector<_Handler>).collectChanges(
369 exceptionHandler:exceptionHandler,
370 stopwatch: fieldStopwatch);
371 if (processStopwatch != null) processStopwatch.start();
372 while (changeRecord != null) {
373 if (changeLog != null) changeLog(changeRecord.handler.expression,
374 changeRecord.currentValue,
375 changeRecord.previousValue);
376 changeRecord.handler.onChange(changeRecord);
377 changeRecord = changeRecord.nextChange;
378 }
379 if (processStopwatch != null) processStopwatch.stop();
380
381 if (evalStopwatch != null) evalStopwatch.start();
382 // Process our own function evaluations
383 _EvalWatchRecord evalRecord = _evalWatchHead;
384 int evalCount = 0;
385 while (evalRecord != null) {
386 try {
387 if (evalStopwatch != null) evalCount++;
388 var change = evalRecord.check();
389 if (change != null && changeLog != null) {
390 changeLog(evalRecord.handler.expression,
391 evalRecord.currentValue,
392 evalRecord.previousValue);
393 }
394 } catch (e, s) {
395 if (exceptionHandler == null) rethrow; else exceptionHandler(e, s);
396 }
397 evalRecord = evalRecord._nextEvalWatch;
398 }
399 if (evalStopwatch != null) evalStopwatch..stop()..increment(evalCount);
400
401 // Because the handler can forward changes between each other synchronously
402 // We need to call reaction functions asynchronously. This processes the
403 // asynchronous reaction function queue.
404 int count = 0;
405 if (processStopwatch != null) processStopwatch.stop();
406 Watch dirtyWatch = _dirtyWatchHead;
407 RootWatchGroup root = _rootGroup;
408 root._removeCount = 0;
409 while(dirtyWatch != null) {
410 count++;
411 try {
412 if (root._removeCount == 0 || dirtyWatch._watchGroup.isAttached) {
413 dirtyWatch.invoke();
414 }
415 } catch (e, s) {
416 if (exceptionHandler == null) rethrow; else exceptionHandler(e, s);
417 }
418 dirtyWatch = dirtyWatch._nextDirtyWatch;
419 }
420 _dirtyWatchHead = _dirtyWatchTail = null;
421 if (processStopwatch != null) processStopwatch..stop()..increment(count);
422 return count;
423 }
424
425 /**
426 * Add Watch into the asynchronous queue for later processing.
427 */
428 Watch _addDirtyWatch(Watch watch) {
429 if (!watch._dirty) {
430 watch._dirty = true;
431 if (_dirtyWatchTail == null) {
432 _dirtyWatchHead = _dirtyWatchTail = watch;
433 } else {
434 _dirtyWatchTail._nextDirtyWatch = watch;
435 _dirtyWatchTail = watch;
436 }
437 watch._nextDirtyWatch = null;
438 }
439 return watch;
440 }
441 }
442
443 /**
444 * [Watch] corresponds to an individual [watch] registration on the watchGrp.
445 */
446 class Watch {
447 Watch _previousWatch, _nextWatch;
448
449 final Record<_Handler> _record;
450 final ReactionFn reactionFn;
451 final WatchGroup _watchGroup;
452
453 bool _dirty = false;
454 bool _deleted = false;
455 Watch _nextDirtyWatch;
456
457 Watch(this._watchGroup, this._record, this.reactionFn);
458
459 get expression => _record.handler.expression;
460 void invoke() {
461 if (_deleted || !_dirty) return;
462 _dirty = false;
463 reactionFn(_record.currentValue, _record.previousValue);
464 }
465
466 void remove() {
467 if (_deleted) throw new StateError('Already deleted!');
468 _deleted = true;
469 var handler = _record.handler;
470 _WatchList._remove(handler, this);
471 handler.release();
472 }
473 }
474
475 /**
476 * This class processes changes from the change detector. The changes are
477 * forwarded onto the next [_Handler] or queued up in case of reaction function.
478 *
479 * Given these two expression: 'a.b.c' => rfn1 and 'a.b' => rfn2
480 * The resulting data structure is:
481 *
482 * _Handler +--> _Handler +--> _Handler
483 * - delegateHandler -+ - delegateHandler -+ - delegateHandler = nul l
484 * - expression: 'a' - expression: 'a.b' - expression: 'a.b.c'
485 * - watchObject: context - watchObject: context.a - watchObject: context. a.b
486 * - watchRecord: 'a' - watchRecord 'b' - watchRecord 'c'
487 * - reactionFn: null - reactionFn: rfn1 - reactionFn: rfn2
488 *
489 * Notice how the [_Handler]s coalesce their watching. Also notice that any
490 * changes detected at one handler are propagated to the next handler.
491 */
492 abstract class _Handler implements _LinkedList, _LinkedListItem, _WatchList {
493 _Handler _head, _tail;
494 _Handler _next, _previous;
495 Watch _watchHead, _watchTail;
496
497 final String expression;
498 final WatchGroup watchGrp;
499
500 WatchRecord<_Handler> watchRecord;
501 _Handler forwardingHandler;
502
503 _Handler(this.watchGrp, this.expression) {
504 assert(watchGrp != null);
505 assert(expression != null);
506 }
507
508 Watch addReactionFn(ReactionFn reactionFn) {
509 assert(_next != this); // verify we are not detached
510 return watchGrp._rootGroup._addDirtyWatch(_WatchList._add(this,
511 new Watch(watchGrp, watchRecord, reactionFn)));
512 }
513
514 void addForwardHandler(_Handler forwardToHandler) {
515 assert(forwardToHandler.forwardingHandler == null);
516 _LinkedList._add(this, forwardToHandler);
517 forwardToHandler.forwardingHandler = this;
518 }
519
520 void release() {
521 if (_WatchList._isEmpty(this) && _LinkedList._isEmpty(this)) {
522 _releaseWatch();
523 // Remove ourselves from cache, or else new registrations will go to us,
524 // but we are dead
525 watchGrp._cache.remove(expression);
526
527 if (forwardingHandler != null) {
528 // TODO(misko): why do we need this check?
529 _LinkedList._remove(forwardingHandler, this);
530 forwardingHandler.release();
531 }
532
533 // We can remove ourselves
534 assert((_next = _previous = this) == this); // mark ourselves as detached
535 }
536 }
537
538 void _releaseWatch() {
539 watchRecord.remove();
540 watchGrp._fieldCost--;
541 }
542 acceptValue(object) => null;
543
544 void onChange(ChangeRecord<_Handler> record) {
545 assert(_next != this); // verify we are not detached
546 // If we have reaction functions than queue them up for asynchronous
547 // processing.
548 Watch watch = _watchHead;
549 while(watch != null) {
550 watchGrp._rootGroup._addDirtyWatch(watch);
551 watch = watch._nextWatch;
552 }
553 // If we have a delegateHandler then forward the new value to it.
554 _Handler delegateHandler = _head;
555 while (delegateHandler != null) {
556 delegateHandler.acceptValue(record.currentValue);
557 delegateHandler = delegateHandler._next;
558 }
559 }
560 }
561
562 class _ConstantHandler extends _Handler {
563 _ConstantHandler(WatchGroup watchGroup, String expression, dynamic constantVal ue)
564 : super(watchGroup, expression)
565 {
566 watchRecord = new _EvalWatchRecord.constant(this, constantValue);
567 }
568 release() => null;
569 }
570
571 class _FieldHandler extends _Handler {
572 _FieldHandler(watchGrp, expression): super(watchGrp, expression);
573
574 /**
575 * This function forwards the watched object to the next [_Handler]
576 * synchronously.
577 */
578 void acceptValue(object) {
579 watchRecord.object = object;
580 var changeRecord = watchRecord.check();
581 if (changeRecord != null) onChange(changeRecord);
582 }
583 }
584
585 class _CollectionHandler extends _Handler {
586 _CollectionHandler(WatchGroup watchGrp, String expression)
587 : super(watchGrp, expression);
588 /**
589 * This function forwards the watched object to the next [_Handler] synchronou sly.
590 */
591 void acceptValue(object) {
592 watchRecord.object = object;
593 var changeRecord = watchRecord.check();
594 if (changeRecord != null) onChange(changeRecord);
595 }
596
597 void _releaseWatch() {
598 watchRecord.remove();
599 watchGrp._collectionCost--;
600 }
601 }
602
603 class _ArgHandler extends _Handler {
604 _ArgHandler _previousArgHandler, _nextArgHandler;
605
606 // TODO(misko): Why do we override parent?
607 final _EvalWatchRecord watchRecord;
608 final int index;
609
610 _releaseWatch() => null;
611
612 _ArgHandler(WatchGroup watchGrp, this.watchRecord, int index)
613 : index = index,
614 super(watchGrp, 'arg[$index]');
615
616 void acceptValue(object) {
617 watchRecord.dirtyArgs = true;
618 watchRecord.args[index] = object;
619 }
620 }
621
622 class _InvokeHandler extends _Handler implements _ArgHandlerList {
623 _ArgHandler _argHandlerHead, _argHandlerTail;
624
625 _InvokeHandler(WatchGroup watchGrp, String expression)
626 : super(watchGrp, expression);
627
628 void acceptValue(object) {
629 watchRecord.object = object;
630 }
631
632 void onChange(ChangeRecord<_Handler> record) {
633 super.onChange(record);
634 }
635
636 void _releaseWatch() {
637 (watchRecord as _EvalWatchRecord).remove();
638 }
639
640 void release() {
641 super.release();
642 _ArgHandler current = _argHandlerHead;
643 while(current != null) {
644 current.release();
645 current = current._nextArgHandler;
646 }
647 }
648 }
649
650
651 class _EvalWatchRecord implements WatchRecord<_Handler>, ChangeRecord<_Handler> {
652 static const int _MODE_DELETED_ = -1;
653 static const int _MODE_MARKER_ = 0;
654 static const int _MODE_FUNCTION_ = 1;
655 static const int _MODE_FUNCTION_APPLY_ = 2;
656 static const int _MODE_NULL_ = 3;
657 static const int _MODE_FIELD_CLOSURE_ = 4;
658 static const int _MODE_MAP_CLOSURE_ = 5;
659 static const int _MODE_METHOD_ = 6;
660 WatchGroup watchGrp;
661 final _Handler handler;
662 final List args;
663 final Symbol symbol;
664 final String name;
665 int mode;
666 /* dartbug.com/16401 Function*/ var fn;
667 InstanceMirror _instanceMirror;
668 bool dirtyArgs = true;
669
670 dynamic currentValue, previousValue, _object;
671 _EvalWatchRecord _previousEvalWatch, _nextEvalWatch;
672
673 _EvalWatchRecord(this.watchGrp, this.handler, this.fn, name, int arity)
674 : args = new List(arity),
675 name = name,
676 symbol = name == null ? null : new Symbol(name) {
677 if (fn is FunctionApply) {
678 mode = _MODE_FUNCTION_APPLY_;
679 } else if (fn is Function) {
680 mode = _MODE_FUNCTION_;
681 } else {
682 mode = _MODE_NULL_;
683 }
684 }
685
686 _EvalWatchRecord.marker()
687 : mode = _MODE_MARKER_,
688 watchGrp = null,
689 handler = null,
690 args = null,
691 fn = null,
692 symbol = null,
693 name = null;
694
695 _EvalWatchRecord.constant(_Handler handler, dynamic constantValue)
696 : mode = _MODE_MARKER_,
697 handler = handler,
698 currentValue = constantValue,
699 watchGrp = null,
700 args = null,
701 fn = null,
702 symbol = null,
703 name = null;
704
705 get field => '()';
706
707 get object => _object;
708
709 set object(value) {
710 assert(mode != _MODE_DELETED_);
711 assert(mode != _MODE_MARKER_);
712 assert(mode != _MODE_FUNCTION_);
713 assert(mode != _MODE_FUNCTION_APPLY_);
714 assert(symbol != null);
715 _object = value;
716
717 if (value == null) {
718 mode = _MODE_NULL_;
719 } else {
720 if (value is Map) {
721 mode = _MODE_MAP_CLOSURE_;
722 } else {
723 _instanceMirror = reflect(value);
724 mode = _hasMethod(_instanceMirror.type, symbol)
725 ? _MODE_METHOD_
726 : _MODE_FIELD_CLOSURE_;
727 }
728 }
729 }
730
731 ChangeRecord<_Handler> check() {
732 var value;
733 switch (mode) {
734 case _MODE_MARKER_:
735 case _MODE_NULL_:
736 return null;
737 case _MODE_FUNCTION_:
738 if (!dirtyArgs) return null;
739 value = Function.apply(fn, args);
740 dirtyArgs = false;
741 break;
742 case _MODE_FUNCTION_APPLY_:
743 if (!dirtyArgs) return null;
744 value = (fn as FunctionApply).apply(args);
745 dirtyArgs = false;
746 break;
747 case _MODE_FIELD_CLOSURE_:
748 var closure = _instanceMirror.getField(symbol).reflectee;
749 value = closure == null ? null : Function.apply(closure, args);
750 break;
751 case _MODE_MAP_CLOSURE_:
752 var closure = object[name];
753 value = closure == null ? null : Function.apply(closure, args);
754 break;
755 case _MODE_METHOD_:
756 value = _instanceMirror.invoke(symbol, args).reflectee;
757 break;
758 default:
759 assert(false);
760 }
761
762 var current = currentValue;
763 if (!identical(current, value)) {
764 if (value is String && current is String && value == current) {
765 // it is really the same, recover and save so next time identity is same
766 current = value;
767 } else {
768 previousValue = current;
769 currentValue = value;
770 handler.onChange(this);
771 return this;
772 }
773 }
774 return null;
775 }
776
777 get nextChange => null;
778
779 void remove() {
780 assert(mode != _MODE_DELETED_);
781 assert((mode = _MODE_DELETED_) == _MODE_DELETED_); // Mark as deleted.
782 watchGrp._evalCost--;
783 _EvalWatchList._remove(watchGrp, this);
784 }
785
786 String toString() {
787 if (mode == _MODE_MARKER_) return 'MARKER[$currentValue]';
788 return '${watchGrp.id}:${handler.expression}';
789 }
790
791 static final Function _hasMethod = (() {
792 var objectClassMirror = reflectClass(Object);
793 Set<Symbol> objectClassInstanceMethods =
794 new Set<Symbol>.from([#toString, #noSuchMethod]);
795 try {
796 // Use ClassMirror.instanceMembers if available. It contains local
797 // as well as inherited members.
798 objectClassMirror.instanceMembers;
799 // For SDK 1.2 we have to use a somewhat complicated helper for this
800 // to work around bugs in the dart2js implementation.
801 return (type, symbol) {
802 // Always allow instance methods found in the Object class. This makes
803 // it easier to work around a few bugs in the dart2js implementation.
804 if (objectClassInstanceMethods.contains(symbol)) return true;
805 // Work around http://dartbug.com/16309 which causes access to the
806 // instance members of certain builtin types to throw exceptions
807 // while traversing the superclass chain.
808 var mirror;
809 try {
810 mirror = type.instanceMembers[symbol];
811 } on UnsupportedError catch (e) {
812 mirror = type.declarations[symbol];
813 }
814 // Work around http://dartbug.com/15760 which causes noSuchMethod
815 // forwarding stubs to be treated as members of all classes. We have
816 // already checked for the real instance methods in Object, so if the
817 // owner of this method is Object we simply filter it out.
818 if (mirror is !MethodMirror) return false;
819 return mirror.owner != objectClassMirror;
820 };
821 } on NoSuchMethodError catch (e) {
822 // For SDK 1.0 we fall back to just using the local members.
823 return (type, symbol) => type.members[symbol] is MethodMirror;
824 } on UnimplementedError catch (e) {
825 // For SDK 1.1 we fall back to just using the local declarations.
826 return (type, symbol) => type.declarations[symbol] is MethodMirror;
827 }
828 return null;
829 })();
830
831 }
OLDNEW
« no previous file with comments | « third_party/pkg/angular/lib/change_detection/prototype_map.dart ('k') | third_party/pkg/angular/lib/core/directive.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698