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

Unified Diff: pkg/observe/lib/src/path_observer.dart

Issue 51483002: fix PathObserver to avoid try+catch (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: rm meta Created 7 years, 2 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 | « pkg/observe/lib/src/observable_map.dart ('k') | pkg/observe/lib/src/to_observable.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/observe/lib/src/path_observer.dart
diff --git a/pkg/observe/lib/src/path_observer.dart b/pkg/observe/lib/src/path_observer.dart
index 1b6e1a34dc1a80b7638bf918a0897b763616e81a..b3aece1f3e9c0b75dbca8c749a84458e4b3808a5 100644
--- a/pkg/observe/lib/src/path_observer.dart
+++ b/pkg/observe/lib/src/path_observer.dart
@@ -2,7 +2,15 @@
// 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.
-part of observe;
+library observe.src.path_observer;
+
+import 'dart:async';
+@MirrorsUsed(metaTargets: const [Reflectable, ObservableProperty],
+ override: 'observe.src.path_observer')
+import 'dart:mirrors';
+import 'package:logging/logging.dart' show Logger, Level;
+import 'package:observe/observe.dart';
+import 'package:observe/src/observable.dart' show objectType;
// This code is inspired by ChangeSummary:
// https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
@@ -75,7 +83,7 @@ class PathObserver extends ChangeNotifier {
// TODO(jmesserly): throw if property cannot be set?
// MDV seems tolerant of these errors.
if (len == 0) return;
- if (!hasObservers) _updateValues();
+ if (!hasObservers) _updateValues(end: len - 1);
if (_setObjectProperty(_values[len - 1], _segments[len - 1], value)) {
// Technically, this would get updated asynchronously via a change record.
@@ -96,29 +104,31 @@ class PathObserver extends ChangeNotifier {
return result;
}
- void _observed() {
- super._observed();
+ void observed() {
+ super.observed();
_updateValues();
_observePath();
}
- void _unobserved() {
+ void unobserved() {
for (int i = 0; i < _subs.length; i++) {
if (_subs[i] != null) {
_subs[i].cancel();
_subs[i] = null;
}
}
+ super.unobserved();
}
// TODO(jmesserly): should we be caching these values if not observing?
- void _updateValues() {
- for (int i = 0; i < _segments.length; i++) {
+ void _updateValues({int end}) {
+ if (end == null) end = _segments.length;
+ for (int i = 0; i < end; i++) {
_values[i + 1] = _getObjectProperty(_values[i], _segments[i]);
}
}
- void _updateObservedValues([int start = 0]) {
+ void _updateObservedValues({int start: 0}) {
var oldValue, newValue;
for (int i = start; i < _segments.length; i++) {
oldValue = _values[i + 1];
@@ -159,7 +169,7 @@ class PathObserver extends ChangeNotifier {
for (var record in records) {
if (_changeRecordMatches(record, _segments[i])) {
- _updateObservedValues(i);
+ _updateObservedValues(start: i);
return;
}
}
@@ -183,85 +193,76 @@ bool _changeRecordMatches(record, key) {
}
_getObjectProperty(object, property) {
- if (object == null) {
- return null;
- }
+ if (object == null) return null;
- if (object is List && property is int) {
- if (property >= 0 && property < object.length) {
+ if (property is int) {
+ if (object is List && property >= 0 && property < object.length) {
return object[property];
- } else {
- return null;
}
- }
-
- if (property is Symbol) {
+ } else if (property is Symbol) {
var mirror = reflect(object);
- var result = _tryGetField(mirror, property);
- if (result != null) return result.reflectee;
+ final type = mirror.type;
+ try {
+ if (_canGetProperty(type, property)) {
+ return mirror.getField(property).reflectee;
+ }
+ // Support indexer if available, e.g. Maps or polymer_expressions Scope.
+ if (_hasMethod(type, const Symbol('[]'))) {
Siggi Cherem (dart-lang) 2013/10/29 21:48:38 does the # grammar support `#[]`?
Jennifer Messerly 2013/10/29 22:03:30 crazy. apparently yes. changed.
+ return object[MirrorSystem.getName(property)];
Siggi Cherem (dart-lang) 2013/10/29 21:48:38 interesting, this will let people use: $.id.foo
Jennifer Messerly 2013/10/29 22:03:30 yes. This has always been the case with PathObserv
Jennifer Messerly 2013/10/29 22:10:33 added a comment
+ }
+ } on NoSuchMethodError catch (e) {
+ // Rethrow, unless the type implements noSuchMethod, in which case we
+ // interpret the exception as a signal that the method was not found.
+ if (!_hasMethod(type, #noSuchMethod)) rethrow;
Siggi Cherem (dart-lang) 2013/10/29 21:48:38 given your earlier check with _canGetProperty, wha
Jennifer Messerly 2013/10/29 22:03:30 consider the following: * the object has noSuchMet
Jennifer Messerly 2013/10/29 22:10:33 renamed to _maybeHasGetter/_maybeHasSetter
+ }
}
- if (object is Map) {
- if (property is Symbol) property = MirrorSystem.getName(property);
- return object[property];
+ if (_logger.isLoggable(Level.FINER)) {
+ _logger.log("can't get $property in $object");
}
-
return null;
}
bool _setObjectProperty(object, property, value) {
- if (object is List && property is int) {
- if (property >= 0 && property < object.length) {
+ if (object == null) return false;
+
+ if (property is int) {
+ if (object is List && property >= 0 && property < object.length) {
object[property] = value;
return true;
- } else {
- return false;
}
- }
-
- if (property is Symbol) {
+ } else if (property is Symbol) {
var mirror = reflect(object);
- if (_trySetField(mirror, property, value)) return true;
+ final type = mirror.type;
+ try {
+ if (_canSetProperty(type, property)) {
+ mirror.setField(property, value);
+ return true;
+ }
+ // Support indexer if available, e.g. Maps or polymer_expressions Scope.
+ if (_hasMethod(type, const Symbol('[]='))) {
+ object[MirrorSystem.getName(property)] = value;
+ return true;
+ }
+ } on NoSuchMethodError catch (e) {
+ if (!_hasMethod(type, #noSuchMethod)) rethrow;
+ }
}
- if (object is Map) {
- if (property is Symbol) property = MirrorSystem.getName(property);
- object[property] = value;
- return true;
+ if (_logger.isLoggable(Level.FINER)) {
+ _logger.log("can't set $property in $object");
}
-
return false;
}
-InstanceMirror _tryGetField(InstanceMirror mirror, Symbol name) {
- try {
- return mirror.getField(name);
- } on NoSuchMethodError catch (e) {
- if (_hasMember(mirror, name, (m) =>
- m is VariableMirror || m is MethodMirror && m.isGetter)) {
- // The field/getter is there but threw a NoSuchMethod exception.
- // This is a legitimate error in the code so rethrow.
- rethrow;
- }
- // The field isn't there. PathObserver does not treat this as an error.
- return null;
- }
-}
-
-bool _trySetField(InstanceMirror mirror, Symbol name, Object value) {
- try {
- mirror.setField(name, value);
- return true;
- } on NoSuchMethodError catch (e) {
- if (_hasMember(mirror, name, (m) => m is VariableMirror) ||
- _hasMember(mirror, _setterName(name))) {
- // The field/setter is there but threw a NoSuchMethod exception.
- // This is a legitimate error in the code so rethrow.
- rethrow;
- }
- // The field isn't there. PathObserver does not treat this as an error.
- return false;
+bool _canGetProperty(ClassMirror type, Symbol name) {
+ while (type != objectType) {
+ final members = type.members;
+ if (members.containsKey(name)) return true;
+ if (members.containsKey(#noSuchMethod)) return true;
+ type = _safeSuperclass(type);
}
+ return false;
}
// TODO(jmesserly): workaround for:
@@ -269,23 +270,41 @@ bool _trySetField(InstanceMirror mirror, Symbol name, Object value) {
Symbol _setterName(Symbol getter) =>
new Symbol('${MirrorSystem.getName(getter)}=');
-bool _hasMember(InstanceMirror mirror, Symbol name, [bool test(member)]) {
- var type = mirror.type;
- while (type != null) {
- final member = type.members[name];
- if (member != null && (test == null || test(member))) return true;
+bool _canSetProperty(ClassMirror type, Symbol name) {
+ var setterName = _setterName(name);
+ while (type != objectType) {
+ final members = type.members;
+ if (members[name] is VariableMirror) return true;
+ if (members.containsKey(setterName)) return true;
+ if (members.containsKey(#noSuchMethod)) return true;
+ type = _safeSuperclass(type);
+ }
+ return false;
+}
- try {
- type = type.superclass;
- } on UnsupportedError catch (e) {
- // TODO(jmesserly): dart2js throws this error when the type is not
- // reflectable.
- return false;
- }
+/**
+ * True if the type has a method, other than on Object.
+ * Doesn't consider noSuchMethod, unless [name] is `#noSuchMethod`.
+ */
+bool _hasMethod(ClassMirror type, Symbol name) {
+ while (type != objectType) {
+ final member = type.members[name];
+ if (member is MethodMirror && member.isRegularMethod) return true;
+ type = _safeSuperclass(type);
}
return false;
}
+ClassMirror _safeSuperclass(ClassMirror type) {
+ try {
+ return type.superclass;
+ } on UnsupportedError catch (e) {
+ // TODO(jmesserly): dart2js throws this error when the type is not
+ // reflectable.
+ return objectType;
+ }
+}
+
// From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
final _pathRegExp = () {
@@ -307,3 +326,5 @@ bool _isPathValid(String s) {
if (s[0] == '.') return false;
return _pathRegExp.hasMatch(s);
}
+
+final _logger = new Logger('observe.PathObserver');
« no previous file with comments | « pkg/observe/lib/src/observable_map.dart ('k') | pkg/observe/lib/src/to_observable.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698