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

Side by Side Diff: lib/observe/observable.dart

Issue 13652007: workaround various mixin bugs (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/data/expected/checked_mode_test.html.txt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /** 5 /**
6 * This library is used to implement [Observable] types. 6 * This library is used to implement [Observable] types.
7 * 7 *
8 * It exposes lower level functionality such as [hasObservers], [observeReads] 8 * It exposes lower level functionality such as [hasObservers], [observeReads]
9 * [notifyChange] and [notifyRead]. 9 * [notifyChange] and [notifyRead].
10 * 10 *
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 * Returns a function that can be used to stop observation. 218 * Returns a function that can be used to stop observation.
219 * Calling this makes it possible for the garbage collector to reclaim memory 219 * Calling this makes it possible for the garbage collector to reclaim memory
220 * associated with the observation and prevents further calls to [callback]. 220 * associated with the observation and prevents further calls to [callback].
221 * 221 *
222 * You can force a synchronous change delivery at any time by calling 222 * You can force a synchronous change delivery at any time by calling
223 * [deliverChangesSync]. Calling this method if there are no changes has no 223 * [deliverChangesSync]. Calling this method if there are no changes has no
224 * effect. If changes are delivered by deliverChangesSync, they will not be 224 * effect. If changes are delivered by deliverChangesSync, they will not be
225 * delivered again asynchronously, unless the value is changed again. 225 * delivered again asynchronously, unless the value is changed again.
226 */ 226 */
227 ChangeUnobserver observeChanges(Observable obj, ChangeRecordObserver observer) { 227 ChangeUnobserver observeChanges(Observable obj, ChangeRecordObserver observer) {
228 if (obj._observers == null) obj._observers = new LinkedList(); 228 if (obj.$_observers == null) obj.$_observers = new LinkedList();
229 var node = obj._observers.add(observer); 229 var node = obj.$_observers.add(observer);
230 return node.remove; 230 return node.remove;
231 } 231 }
232 232
233 233
234 /** 234 /**
235 * Converts the [Iterable], [Set] or [Map] to an [ObservableList], 235 * Converts the [Iterable], [Set] or [Map] to an [ObservableList],
236 * [ObservableSet] or [ObservableMap] respectively. 236 * [ObservableSet] or [ObservableMap] respectively.
237 * 237 *
238 * The resulting object will contain a shallow copy of the data. 238 * The resulting object will contain a shallow copy of the data.
239 * If [value] is not one of those collection types, it will be returned 239 * If [value] is not one of those collection types, it will be returned
(...skipping 19 matching lines...) Expand all
259 259
260 /** 260 /**
261 * An observable object. This is used by data in model-view architectures 261 * An observable object. This is used by data in model-view architectures
262 * to notify interested parties of changes. 262 * to notify interested parties of changes.
263 * 263 *
264 * Most of the methods for observation are static methods to keep them 264 * Most of the methods for observation are static methods to keep them
265 * stratified from the objects being observed. This is a similar to the design 265 * stratified from the objects being observed. This is a similar to the design
266 * of Mirrors. 266 * of Mirrors.
267 */ 267 */
268 class Observable { 268 class Observable {
269 // TODO(jmesserly): make these fields private once we have mixins in Dart VM.
270
271 /** Observers for this object. Uses a linked-list for fast removal. */ 269 /** Observers for this object. Uses a linked-list for fast removal. */
272 LinkedList<ChangeRecordObserver> _observers; 270 // TODO(jmesserly): make these fields private again once dart2js bugs around
271 // mixins and private fields are fixed.
272 // TODO(jmesserly): removed type annotation here to workaround a VM checked
273 // mode bug. It should be: LinkedList<ChangeRecordObserver>
274 var $_observers;
273 275
274 /** Changes to this object since last batch was delivered. */ 276 /** Changes to this object since last batch was delivered. */
275 List<ChangeRecord> _changes; 277 List<ChangeRecord> $_changes;
276 278
277 final int hashCode = ++Observable._nextHashCode; 279 final int hashCode = ++Observable.$_nextHashCode;
278 280
279 // TODO(jmessery): workaround for VM bug http://dartbug.com/5746 281 // TODO(jmessery): workaround for VM bug http://dartbug.com/5746
280 // We need hashCode to be fast for _ExpressionObserver to work. 282 // We need hashCode to be fast for _ExpressionObserver to work.
281 static int _nextHashCode = 0; 283 static int $_nextHashCode = 0;
282 } 284 }
283 285
284 // Note: these are not instance methods of Observable, to make it clear that 286 // Note: these are not instance methods of Observable, to make it clear that
285 // they aren't themselves being observed. It is the same reason that mirrors and 287 // they aren't themselves being observed. It is the same reason that mirrors and
286 // EcmaScript's Object.observe are stratified. 288 // EcmaScript's Object.observe are stratified.
287 // TODO(jmesserly): this makes it impossible to proxy an Observable. Is that an 289 // TODO(jmesserly): this makes it impossible to proxy an Observable. Is that an
288 // acceptable restriction? 290 // acceptable restriction?
289 291
290 /** 292 /**
291 * True if [self] has any observers, and should call [notifyChange] for 293 * True if [self] has any observers, and should call [notifyChange] for
292 * changes. 294 * changes.
293 * 295 *
294 * Note: this is used by objects implementing [Observable]. 296 * Note: this is used by objects implementing [Observable].
295 * You should not need it if your type is marked `@observable`. 297 * You should not need it if your type is marked `@observable`.
296 */ 298 */
297 bool hasObservers(Observable self) => 299 bool hasObservers(Observable self) =>
298 self._observers != null && self._observers.head != null; 300 self.$_observers != null && self.$_observers.head != null;
299 301
300 /** 302 /**
301 * True if we are observing reads. This should be checked before calling 303 * True if we are observing reads. This should be checked before calling
302 * [notifyRead]. 304 * [notifyRead].
303 * 305 *
304 * Note: this is used by objects implementing [Observable]. 306 * Note: this is used by objects implementing [Observable].
305 * You should not need it if your type is marked `@observable`. 307 * You should not need it if your type is marked `@observable`.
306 */ 308 */
307 bool get observeReads => _activeObserver != null; 309 bool get observeReads => _activeObserver != null;
308 310
(...skipping 28 matching lines...) Expand all
337 // the value actually changed. If not don't signal a change event. 339 // the value actually changed. If not don't signal a change event.
338 // This helps programmers avoid some common cases of cycles in their code. 340 // This helps programmers avoid some common cases of cycles in their code.
339 if ((type & (ChangeRecord.INSERT | ChangeRecord.REMOVE)) == 0) { 341 if ((type & (ChangeRecord.INSERT | ChangeRecord.REMOVE)) == 0) {
340 if (oldValue == newValue) return; 342 if (oldValue == newValue) return;
341 } 343 }
342 344
343 if (_changedObjects == null) { 345 if (_changedObjects == null) {
344 _changedObjects = []; 346 _changedObjects = [];
345 setImmediate(deliverChangesSync); 347 setImmediate(deliverChangesSync);
346 } 348 }
347 if (self._changes == null) { 349 if (self.$_changes == null) {
348 self._changes = []; 350 self.$_changes = [];
349 _changedObjects.add(self); 351 _changedObjects.add(self);
350 } 352 }
351 self._changes.add(new ChangeRecord(type, key, oldValue, newValue)); 353 self.$_changes.add(new ChangeRecord(type, key, oldValue, newValue));
352 } 354 }
353 355
354 // Optimizations to avoid extra work if observing const/final data. 356 // Optimizations to avoid extra work if observing const/final data.
355 void _doNothing() {} 357 void _doNothing() {}
356 358
357 /** 359 /**
358 * The current observer that is tracking reads, or null if we aren't tracking 360 * The current observer that is tracking reads, or null if we aren't tracking
359 * reads. Reads are tracked when executing [_ExpressionObserver._observe]. 361 * reads. Reads are tracked when executing [_ExpressionObserver._observe].
360 */ 362 */
361 _ExpressionObserver _activeObserver; 363 _ExpressionObserver _activeObserver;
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
405 return; 407 return;
406 } 408 }
407 409
408 if (changedObjects != null) { 410 if (changedObjects != null) {
409 for (var observable in changedObjects) { 411 for (var observable in changedObjects) {
410 // TODO(jmesserly): freeze the "changes" list? 412 // TODO(jmesserly): freeze the "changes" list?
411 // If one observer incorrectly mutates it, it will affect what future 413 // If one observer incorrectly mutates it, it will affect what future
412 // observers see, possibly leading to subtle bugs. 414 // observers see, possibly leading to subtle bugs.
413 // OTOH, I don't want to add a defensive copy here. Maybe a wrapper that 415 // OTOH, I don't want to add a defensive copy here. Maybe a wrapper that
414 // prevents mutation, or a ListBuilder of some sort than can be frozen. 416 // prevents mutation, or a ListBuilder of some sort than can be frozen.
415 var changes = observable._changes; 417 var changes = observable.$_changes;
416 observable._changes = null; 418 observable.$_changes = null;
417 419
418 for (var n = observable._observers.head; n != null; n = n.next) { 420 for (var n = observable.$_observers.head; n != null; n = n.next) {
419 var observer = n.value; 421 var observer = n.value;
420 try { 422 try {
421 observer(changes); 423 observer(changes);
422 } catch (error, trace) { 424 } catch (error, trace) {
423 onObserveUnhandledError(error, trace, observer, 'from $observable'); 425 onObserveUnhandledError(error, trace, observer, 'from $observable');
424 } 426 }
425 } 427 }
426 } 428 }
427 } 429 }
428 430
(...skipping 16 matching lines...) Expand all
445 */ 447 */
446 void _diagnoseCircularLimit(List<Observable> changedObjects, 448 void _diagnoseCircularLimit(List<Observable> changedObjects,
447 Map<int, _ExpressionObserver> changedExpressions) { 449 Map<int, _ExpressionObserver> changedExpressions) {
448 // TODO(jmesserly,sigmund): we could do purity checks when running "observe" 450 // TODO(jmesserly,sigmund): we could do purity checks when running "observe"
449 // itself, to detect if it causes writes to happen. I think that case is less 451 // itself, to detect if it causes writes to happen. I think that case is less
450 // common than cycles caused by the notifications though. 452 // common than cycles caused by the notifications though.
451 453
452 var trace = []; 454 var trace = [];
453 if (changedObjects != null) { 455 if (changedObjects != null) {
454 for (var observable in changedObjects) { 456 for (var observable in changedObjects) {
455 var changes = observable._changes; 457 var changes = observable.$_changes;
456 trace.add('$observable $changes'); 458 trace.add('$observable $changes');
457 } 459 }
458 } 460 }
459 461
460 if (changedExpressions != null) { 462 if (changedExpressions != null) {
461 for (var exprObserver in changedExpressions.values) { 463 for (var exprObserver in changedExpressions.values) {
462 var change = exprObserver._deliver(); 464 var change = exprObserver._deliver();
463 if (change != null) trace.add('$exprObserver $change'); 465 if (change != null) trace.add('$exprObserver $change');
464 } 466 }
465 } 467 }
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
684 /** 686 /**
685 * The type of the `@observable` annotation. 687 * The type of the `@observable` annotation.
686 * 688 *
687 * Library private because you should be able to use the [observable] field 689 * Library private because you should be able to use the [observable] field
688 * to get the one and only instance. We could make it public though, if anyone 690 * to get the one and only instance. We could make it public though, if anyone
689 * needs it for some reason. 691 * needs it for some reason.
690 */ 692 */
691 class _ObservableAnnotation { 693 class _ObservableAnnotation {
692 const _ObservableAnnotation(); 694 const _ObservableAnnotation();
693 } 695 }
OLDNEW
« no previous file with comments | « no previous file | test/data/expected/checked_mode_test.html.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698