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 |