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

Side by Side Diff: pkg/observe/lib/src/path_observer.dart

Issue 211763002: Change path-observer to lookup properties aggressively and report errors (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 9 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 | Annotate | Revision Log
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 library observe.src.path_observer; 5 library observe.src.path_observer;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 import 'dart:math' show min; 9 import 'dart:math' show min;
10 10
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
74 bool _check({bool skipChanges: false}) { 74 bool _check({bool skipChanges: false}) {
75 var oldValue = _value; 75 var oldValue = _value;
76 _value = _path.getValueFrom(_object); 76 _value = _path.getValueFrom(_object);
77 if (skipChanges || _value == oldValue) return false; 77 if (skipChanges || _value == oldValue) return false;
78 78
79 _report(_value, oldValue); 79 _report(_value, oldValue);
80 return true; 80 return true;
81 } 81 }
82 } 82 }
83 83
84 /// A dot-delimieted property path such as "foo.bar" or "foo.10.bar". 84 /// A dot-delimieted property path such as "foo.bar" or "foo.10.bar".
Jennifer Messerly 2014/03/25 22:32:44 this was an issue before but: do you mind adding a
Siggi Cherem (dart-lang) 2014/03/26 00:32:07 Done.
85 /// The path specifies how to get a particular value from an object graph, where 85 /// The path specifies how to get a particular value from an object graph, where
86 /// the graph can include arrays. 86 /// the graph can include arrays and maps. Each segment of the path describes
87 /// how to take a single step in the object graph. Properties like 'foo' or
88 /// 'bar' are read as properties on objects, or as keys if the object is a Map
89 /// or an [StringIndexer], while integer values are read as indexes in an array.
Jennifer Messerly 2014/03/25 22:32:44 should be "a [StringIndexer]" also, this was an i
Siggi Cherem (dart-lang) 2014/03/26 00:32:07 Done.
87 // TODO(jmesserly): consider specialized subclasses for: 90 // TODO(jmesserly): consider specialized subclasses for:
88 // * empty path 91 // * empty path
89 // * "value" 92 // * "value"
90 // * single token in path, e.g. "foo" 93 // * single token in path, e.g. "foo"
91 class PropertyPath { 94 class PropertyPath {
92 /// The segments of the path. 95 /// The segments of the path.
93 final List<Object> _segments; 96 final List<Object> _segments;
94 97
95 /// Creates a new [PropertyPath]. These can be stored to avoid excessive 98 /// Creates a new [PropertyPath]. These can be stored to avoid excessive
96 /// parsing of path strings. 99 /// parsing of path strings.
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 for (int i = 0, len = _segments.length; i < len; i++) { 176 for (int i = 0, len = _segments.length; i < len; i++) {
174 hash = 0x1fffffff & (hash + _segments[i].hashCode); 177 hash = 0x1fffffff & (hash + _segments[i].hashCode);
175 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); 178 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
176 hash = hash ^ (hash >> 6); 179 hash = hash ^ (hash >> 6);
177 } 180 }
178 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); 181 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
179 hash = hash ^ (hash >> 11); 182 hash = hash ^ (hash >> 11);
180 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); 183 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
181 } 184 }
182 185
183 /// Returns the current of the path from the provided [obj]ect. 186 /// Returns the current value of the path from the provided [obj]ect.
184 getValueFrom(Object obj) { 187 getValueFrom(Object obj) {
185 if (!isValid) return null; 188 if (!isValid) return null;
186 for (var segment in _segments) { 189 for (var segment in _segments) {
187 if (obj == null) return null; 190 if (obj == null) return null;
188 obj = _getObjectProperty(obj, segment); 191 obj = _getObjectProperty(obj, segment);
189 } 192 }
190 return obj; 193 return obj;
191 } 194 }
192 195
193 /// Attempts to set the [value] of the path from the provided [obj]ect. 196 /// Attempts to set the [value] of the path from the provided [obj]ect.
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 if (record is PropertyChangeRecord) { 229 if (record is PropertyChangeRecord) {
227 return (record as PropertyChangeRecord).name == key; 230 return (record as PropertyChangeRecord).name == key;
228 } 231 }
229 if (record is MapChangeRecord) { 232 if (record is MapChangeRecord) {
230 if (key is Symbol) key = smoke.symbolToName(key); 233 if (key is Symbol) key = smoke.symbolToName(key);
231 return (record as MapChangeRecord).key == key; 234 return (record as MapChangeRecord).key == key;
232 } 235 }
233 return false; 236 return false;
234 } 237 }
235 238
239 /// Properties in [Map] that need to be read as properties and not as keys in
240 /// the map. We exclude methods ('containsValue', 'containsKey', 'putIfAbsent',
241 /// 'addAll', 'remove', 'clear', 'forEach') because there is no use in reading
242 /// them as part of path-observer segments.
243 const _MAP_PROPERTIES =
244 const ['keys', 'values', 'length', 'isEmpty', 'isNotEmpty'];
245
236 _getObjectProperty(object, property) { 246 _getObjectProperty(object, property) {
237 if (object == null) return null; 247 if (object == null) return null;
238 248
239 if (property is int) { 249 if (property is int) {
240 if (object is List && property >= 0 && property < object.length) { 250 if (object is List && property >= 0 && property < object.length) {
241 return object[property]; 251 return object[property];
242 } 252 }
243 } else if (property is Symbol) { 253 } else if (property is Symbol) {
244 final type = object.runtimeType; 254 var name = smoke.symbolToName(property);
Jennifer Messerly 2014/03/25 22:32:44 move this inside the if, below? and make _MAP_PROP
Siggi Cherem (dart-lang) 2014/03/26 00:32:07 Done.
255 // Support indexer if available, e.g. Maps or polymer_expressions Scope.
256 // This is the default syntax used by polymer/nodebind and
257 // polymer/observe-js PathObserver.
258 // TODO(sigmund): should we also support using checking dynamically for
259 // whether the type practically implements the indexer API
260 // (smoke.hasInstanceMethod(type, const Symbol('[]')))?
261 if (object is StringIndexer ||
262 object is Map && !_MAP_PROPERTIES.contains(name)) {
263 return object[name];
264 }
245 try { 265 try {
246 if (smoke.hasGetter(type, property) || smoke.hasNoSuchMethod(type)) { 266 return smoke.read(object, property);
247 return smoke.read(object, property); 267 } on NoSuchMethodError catch (e, s) {
248 }
249 // Support indexer if available, e.g. Maps or polymer_expressions Scope.
250 // This is the default syntax used by polymer/nodebind and
251 // polymer/observe-js PathObserver.
252 if (smoke.hasInstanceMethod(type, const Symbol('[]'))) {
253 return object[smoke.symbolToName(property)];
254 }
255 } on NoSuchMethodError catch (e) {
256 // Rethrow, unless the type implements noSuchMethod, in which case we 268 // Rethrow, unless the type implements noSuchMethod, in which case we
257 // interpret the exception as a signal that the method was not found. 269 // interpret the exception as a signal that the method was not found.
258 if (!smoke.hasNoSuchMethod(type)) rethrow; 270 if (!smoke.hasNoSuchMethod(object.runtimeType)) {
271 new Completer().completeError(e, s);
Jennifer Messerly 2014/03/25 22:32:44 hmmm. Upon second look I'm not sure this is the ri
Siggi Cherem (dart-lang) 2014/03/26 00:32:07 No worries, changed it back to sync. Thanks for fi
272 }
259 } 273 }
260 } 274 }
261 275
262 if (_logger.isLoggable(Level.FINER)) { 276 if (_logger.isLoggable(Level.FINER)) {
263 _logger.finer("can't get $property in $object"); 277 _logger.finer("can't get $property in $object");
264 } 278 }
265 return null; 279 return null;
266 } 280 }
267 281
268 bool _setObjectProperty(object, property, value) { 282 bool _setObjectProperty(object, property, value) {
269 if (object == null) return false; 283 if (object == null) return false;
270 284
271 if (property is int) { 285 if (property is int) {
272 if (object is List && property >= 0 && property < object.length) { 286 if (object is List && property >= 0 && property < object.length) {
273 object[property] = value; 287 object[property] = value;
274 return true; 288 return true;
275 } 289 }
276 } else if (property is Symbol) { 290 } else if (property is Symbol) {
277 final type = object.runtimeType; 291 // Support indexer if available, e.g. Maps or polymer_expressions Scope.
292 var name = smoke.symbolToName(property);
293 if (object is StringIndexer ||
294 object is Map && !_MAP_PROPERTIES.contains(name)) {
295 object[name] = value;
296 return true;
297 }
278 try { 298 try {
279 if (smoke.hasSetter(type, property) || smoke.hasNoSuchMethod(type)) { 299 smoke.write(object, property, value);
280 smoke.write(object, property, value); 300 return true;
281 return true; 301 } on NoSuchMethodError catch (e, s) {
302 if (!smoke.hasNoSuchMethod(object.runtimeType)) {
303 new Completer().completeError(e, s);
282 } 304 }
283 // Support indexer if available, e.g. Maps or polymer_expressions Scope.
284 if (smoke.hasInstanceMethod(type, const Symbol('[]='))) {
285 object[smoke.symbolToName(property)] = value;
286 return true;
287 }
288 } on NoSuchMethodError catch (e) {
289 if (!smoke.hasNoSuchMethod(type)) rethrow;
290 } 305 }
291 } 306 }
292 307
293 if (_logger.isLoggable(Level.FINER)) { 308 if (_logger.isLoggable(Level.FINER)) {
294 _logger.finer("can't set $property in $object"); 309 _logger.finer("can't set $property in $object");
295 } 310 }
296 return false; 311 return false;
297 } 312 }
298 313
299 // From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js 314 // From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 478
464 if (!changed) return false; 479 if (!changed) return false;
465 480
466 // TODO(rafaelw): Having _observed as the third callback arg here is 481 // TODO(rafaelw): Having _observed as the third callback arg here is
467 // pretty lame API. Fix. 482 // pretty lame API. Fix.
468 _report(_value, oldValues, _observed); 483 _report(_value, oldValues, _observed);
469 return true; 484 return true;
470 } 485 }
471 } 486 }
472 487
488 /// An object accepted by [PropertyPath] where properties are read and written
489 /// as indexing operations, just like a [Map].
490 abstract class StringIndexer {
Siggi Cherem (dart-lang) 2014/03/25 21:44:25 I was not sure about the name. Other ideas/suggest
Jennifer Messerly 2014/03/25 22:32:44 Indexable<K, V>? then do the is check for Indexabl
Siggi Cherem (dart-lang) 2014/03/26 00:32:07 Done.
491 operator [](String name);
492 operator []=(index, value);
493 }
494
473 const _observerSentinel = const _ObserverSentinel(); 495 const _observerSentinel = const _ObserverSentinel();
474 class _ObserverSentinel { const _ObserverSentinel(); } 496 class _ObserverSentinel { const _ObserverSentinel(); }
475 497
476 // A base class for the shared API implemented by PathObserver and 498 // A base class for the shared API implemented by PathObserver and
477 // CompoundObserver and used in _ObservedSet. 499 // CompoundObserver and used in _ObservedSet.
478 abstract class _Observer extends Bindable { 500 abstract class _Observer extends Bindable {
479 static int _nextBirthId = 0; 501 static int _nextBirthId = 0;
480 502
481 /// A number indicating when the object was created. 503 /// A number indicating when the object was created.
482 final int _birthId = _nextBirthId++; 504 final int _birthId = _nextBirthId++;
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
652 for (var observer in _observers.values.toList(growable: false)) { 674 for (var observer in _observers.values.toList(growable: false)) {
653 if (observer._isOpen) observer._check(); 675 if (observer._isOpen) observer._check();
654 } 676 }
655 677
656 _resetNeeded = true; 678 _resetNeeded = true;
657 scheduleMicrotask(reset); 679 scheduleMicrotask(reset);
658 } 680 }
659 } 681 }
660 682
661 const int _MAX_DIRTY_CHECK_CYCLES = 1000; 683 const int _MAX_DIRTY_CHECK_CYCLES = 1000;
OLDNEW
« no previous file with comments | « no previous file | pkg/observe/test/path_observer_test.dart » ('j') | pkg/observe/test/path_observer_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698