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