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

Side by Side Diff: lib/src/observe/impl.dart

Issue 12096106: work in progress: observable implementation using detailed change records (Closed) Base URL: https://github.com/dart-lang/web-ui.git@master
Patch Set: Created 7 years, 10 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 | « lib/src/observable_transform.dart ('k') | lib/src/refactor.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
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.
4
5 /**
6 * Runtime implementation for the observe library.
7 * Note that this is a runtime library; it is not part of the compiler.
8 */
9 library web_ui.src.observe.impl;
10
11 import 'package:web_ui/observe/expression.dart';
12 import 'package:web_ui/observe/observable.dart';
13
14 ExpressionObserverImpl activeObserver;
15
16
17 class ReadInfo {
18 /** See [ChangeRecord.type]. */
19 final int type;
20
21 /** See [ChangeRecord.name]. */
22 final name;
23
24 ReadInfo(this.type, this.name);
25
26 bool operator==(other) =>
27 other is ReadInfo && other.type == type && other.name == name;
28
29 int get hashCode => type * 31 + name.hashCode;
30 }
31
32 class ExpressionObserverImpl {
33 final ObservableExpression _expression;
34 final ExpressionObserver _observer;
35
36 // TODO(jmesserly): should we provide the old value? This will keep it
37 // alive until we have a new value. Watchers needed to keep it alive anyway,
38 // but we don't need it in observers, except to pass it to the
39 // ExpressionObserver.
40 Object _value;
41
42 Observable _deliverHelper;
43
44 final Map<Observable, Map<Object, int>> _reads = new Map();
45 final List<ChangeUnobserver> _unobservers = [];
46
47 ExpressionObserverImpl(this._expression, this._observer);
48
49 void observe() {
50 // If an observe call starts another observation, we need to make sure that
51 // the outer observe is tracked correctly.
52 var parent = activeObserver;
53 activeObserver = this;
54 try {
55 _value = _expression();
56 } catch (e, trace) {
57 onObserveUnhandledError(e, trace, _expression);
58 _value = null;
59 }
60
61 _reads.forEach(_watchForChange);
62 _reads.clear();
63
64 // TODO(jmesserly): should we add our changes to the parent?
65 assert(activeObserver == this);
66 activeObserver = parent;
67
68 _observeValue();
69 }
70
71 void _observeValue() {
72 if (_value is! Observable) return;
73
74 _unobservers.add((_value as Observable).observe((_) {
75 _observer(new ExpressionChange(_value, _value));
76 }));
77 }
78
79 void addRead(Observable target, int type, name) {
80 var reads = _reads.putIfAbsent(target, () => new Map());
81 // We would like to easily match against the name and the type. So use a
82 // mask.
83 int mask = reads[name];
84 if (mask == null) mask = 0;
85 reads[name] = mask | type;
86 }
87
88 void _watchForChange(Observable target, Map<Object, int> reads) {
89 _unobservers.add(target.observe((changes) {
90 if (_deliverHelper != null) return;
91 for (var change in changes) {
92 int mask = reads[change.name];
93 if (mask != null && (mask & change.type) != 0) {
94 // Rather than hook directly into [deliverChangesSync], we use this
95 // object to trigger a ChangeRecord that is handled in the same
96 // deliverChangesSync batch.
97 _deliverHelper = new Observable()
98 ..observe(_deliver)
99 ..notifyChange(ChangeRecord.FIELD, '', 0, 1);
100 break;
101 }
102 }
103 }));
104 }
105
106 void unobserve() {
107 for (var unobserver in _unobservers) {
108 unobserver();
109 }
110 _unobservers.clear();
111 _deliverHelper = null;
112 }
113
114 // _deliver does two things:
115 // 1. Evaluate the expression to compute the new value.
116 // 2. Invoke observer for this expression.
117 //
118 // Note: if you mutate a shared value from one observer, future
119 // observers will see the updated value. Essentially, we collapse
120 // the two change notifications into one.
121 //
122 // We could try someting else, but the current order has benefits:
123 // it preserves the invariant that ExpressionChange.newValue equals the
124 // current value of the expression.
125 void _deliver(_) {
126 var oldValue = _value;
127
128 // Call the expression again to compute the new value, and to get the new
129 // list of dependencies.
130 unobserve();
131 observe();
132
133 bool equal;
134 try {
135 equal = oldValue == _value;
136 } catch (e, trace) {
137 onObserveUnhandledError(e, trace, null);
138 return;
139 }
140
141 if (!equal) {
142 try {
143 _observer(new ExpressionChange(oldValue, _value));
144 } catch (e, trace) {
145 onObserveUnhandledError(e, trace, _observer);
146 }
147 }
148 }
149 }
OLDNEW
« no previous file with comments | « lib/src/observable_transform.dart ('k') | lib/src/refactor.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698