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

Unified 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, 11 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/observable_transform.dart ('k') | lib/src/refactor.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/observe/impl.dart
diff --git a/lib/src/observe/impl.dart b/lib/src/observe/impl.dart
new file mode 100644
index 0000000000000000000000000000000000000000..753fe352cacad3db409691ae6cda1d498598fe60
--- /dev/null
+++ b/lib/src/observe/impl.dart
@@ -0,0 +1,149 @@
+// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Runtime implementation for the observe library.
+ * Note that this is a runtime library; it is not part of the compiler.
+ */
+library web_ui.src.observe.impl;
+
+import 'package:web_ui/observe/expression.dart';
+import 'package:web_ui/observe/observable.dart';
+
+ExpressionObserverImpl activeObserver;
+
+
+class ReadInfo {
+ /** See [ChangeRecord.type]. */
+ final int type;
+
+ /** See [ChangeRecord.name]. */
+ final name;
+
+ ReadInfo(this.type, this.name);
+
+ bool operator==(other) =>
+ other is ReadInfo && other.type == type && other.name == name;
+
+ int get hashCode => type * 31 + name.hashCode;
+}
+
+class ExpressionObserverImpl {
+ final ObservableExpression _expression;
+ final ExpressionObserver _observer;
+
+ // TODO(jmesserly): should we provide the old value? This will keep it
+ // alive until we have a new value. Watchers needed to keep it alive anyway,
+ // but we don't need it in observers, except to pass it to the
+ // ExpressionObserver.
+ Object _value;
+
+ Observable _deliverHelper;
+
+ final Map<Observable, Map<Object, int>> _reads = new Map();
+ final List<ChangeUnobserver> _unobservers = [];
+
+ ExpressionObserverImpl(this._expression, this._observer);
+
+ void observe() {
+ // If an observe call starts another observation, we need to make sure that
+ // the outer observe is tracked correctly.
+ var parent = activeObserver;
+ activeObserver = this;
+ try {
+ _value = _expression();
+ } catch (e, trace) {
+ onObserveUnhandledError(e, trace, _expression);
+ _value = null;
+ }
+
+ _reads.forEach(_watchForChange);
+ _reads.clear();
+
+ // TODO(jmesserly): should we add our changes to the parent?
+ assert(activeObserver == this);
+ activeObserver = parent;
+
+ _observeValue();
+ }
+
+ void _observeValue() {
+ if (_value is! Observable) return;
+
+ _unobservers.add((_value as Observable).observe((_) {
+ _observer(new ExpressionChange(_value, _value));
+ }));
+ }
+
+ void addRead(Observable target, int type, name) {
+ var reads = _reads.putIfAbsent(target, () => new Map());
+ // We would like to easily match against the name and the type. So use a
+ // mask.
+ int mask = reads[name];
+ if (mask == null) mask = 0;
+ reads[name] = mask | type;
+ }
+
+ void _watchForChange(Observable target, Map<Object, int> reads) {
+ _unobservers.add(target.observe((changes) {
+ if (_deliverHelper != null) return;
+ for (var change in changes) {
+ int mask = reads[change.name];
+ if (mask != null && (mask & change.type) != 0) {
+ // Rather than hook directly into [deliverChangesSync], we use this
+ // object to trigger a ChangeRecord that is handled in the same
+ // deliverChangesSync batch.
+ _deliverHelper = new Observable()
+ ..observe(_deliver)
+ ..notifyChange(ChangeRecord.FIELD, '', 0, 1);
+ break;
+ }
+ }
+ }));
+ }
+
+ void unobserve() {
+ for (var unobserver in _unobservers) {
+ unobserver();
+ }
+ _unobservers.clear();
+ _deliverHelper = null;
+ }
+
+ // _deliver does two things:
+ // 1. Evaluate the expression to compute the new value.
+ // 2. Invoke observer for this expression.
+ //
+ // Note: if you mutate a shared value from one observer, future
+ // observers will see the updated value. Essentially, we collapse
+ // the two change notifications into one.
+ //
+ // We could try someting else, but the current order has benefits:
+ // it preserves the invariant that ExpressionChange.newValue equals the
+ // current value of the expression.
+ void _deliver(_) {
+ var oldValue = _value;
+
+ // Call the expression again to compute the new value, and to get the new
+ // list of dependencies.
+ unobserve();
+ observe();
+
+ bool equal;
+ try {
+ equal = oldValue == _value;
+ } catch (e, trace) {
+ onObserveUnhandledError(e, trace, null);
+ return;
+ }
+
+ if (!equal) {
+ try {
+ _observer(new ExpressionChange(oldValue, _value));
+ } catch (e, trace) {
+ onObserveUnhandledError(e, trace, _observer);
+ }
+ }
+ }
+}
« 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