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