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

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

Issue 210823005: Revert "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, 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/observe/test/path_observer_test.dart » ('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 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 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
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".
85 ///
86 /// 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
87 /// the graph can include arrays and maps. Each segment of the path describes 86 /// the graph can include arrays.
88 /// how to take a single step in the object graph. Properties like 'foo' or
89 /// 'bar' are read as properties on objects, or as keys if the object is a [Map]
90 /// or a [Indexable], while integer values are read as indexes in a [List].
91 // TODO(jmesserly): consider specialized subclasses for: 87 // TODO(jmesserly): consider specialized subclasses for:
92 // * empty path 88 // * empty path
93 // * "value" 89 // * "value"
94 // * single token in path, e.g. "foo" 90 // * single token in path, e.g. "foo"
95 class PropertyPath { 91 class PropertyPath {
96 /// The segments of the path. 92 /// The segments of the path.
97 final List<Object> _segments; 93 final List<Object> _segments;
98 94
99 /// Creates a new [PropertyPath]. These can be stored to avoid excessive 95 /// Creates a new [PropertyPath]. These can be stored to avoid excessive
100 /// parsing of path strings. 96 /// parsing of path strings.
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 for (int i = 0, len = _segments.length; i < len; i++) { 173 for (int i = 0, len = _segments.length; i < len; i++) {
178 hash = 0x1fffffff & (hash + _segments[i].hashCode); 174 hash = 0x1fffffff & (hash + _segments[i].hashCode);
179 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); 175 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
180 hash = hash ^ (hash >> 6); 176 hash = hash ^ (hash >> 6);
181 } 177 }
182 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); 178 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
183 hash = hash ^ (hash >> 11); 179 hash = hash ^ (hash >> 11);
184 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); 180 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
185 } 181 }
186 182
187 /// Returns the current value of the path from the provided [obj]ect. 183 /// Returns the current of the path from the provided [obj]ect.
188 getValueFrom(Object obj) { 184 getValueFrom(Object obj) {
189 if (!isValid) return null; 185 if (!isValid) return null;
190 for (var segment in _segments) { 186 for (var segment in _segments) {
191 if (obj == null) return null; 187 if (obj == null) return null;
192 obj = _getObjectProperty(obj, segment); 188 obj = _getObjectProperty(obj, segment);
193 } 189 }
194 return obj; 190 return obj;
195 } 191 }
196 192
197 /// Attempts to set the [value] of the path from the provided [obj]ect. 193 /// 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
230 if (record is PropertyChangeRecord) { 226 if (record is PropertyChangeRecord) {
231 return (record as PropertyChangeRecord).name == key; 227 return (record as PropertyChangeRecord).name == key;
232 } 228 }
233 if (record is MapChangeRecord) { 229 if (record is MapChangeRecord) {
234 if (key is Symbol) key = smoke.symbolToName(key); 230 if (key is Symbol) key = smoke.symbolToName(key);
235 return (record as MapChangeRecord).key == key; 231 return (record as MapChangeRecord).key == key;
236 } 232 }
237 return false; 233 return false;
238 } 234 }
239 235
240 /// Properties in [Map] that need to be read as properties and not as keys in
241 /// the map. We exclude methods ('containsValue', 'containsKey', 'putIfAbsent',
242 /// 'addAll', 'remove', 'clear', 'forEach') because there is no use in reading
243 /// them as part of path-observer segments.
244 const _MAP_PROPERTIES = const [#keys, #values, #length, #isEmpty, #isNotEmpty];
245
246 _getObjectProperty(object, property) { 236 _getObjectProperty(object, property) {
247 if (object == null) return null; 237 if (object == null) return null;
248 238
249 if (property is int) { 239 if (property is int) {
250 if (object is List && property >= 0 && property < object.length) { 240 if (object is List && property >= 0 && property < object.length) {
251 return object[property]; 241 return object[property];
252 } 242 }
253 } else if (property is Symbol) { 243 } else if (property is Symbol) {
254 // Support indexer if available, e.g. Maps or polymer_expressions Scope. 244 final type = object.runtimeType;
255 // This is the default syntax used by polymer/nodebind and
256 // polymer/observe-js PathObserver.
257 // TODO(sigmund): should we also support using checking dynamically for
258 // whether the type practically implements the indexer API
259 // (smoke.hasInstanceMethod(type, const Symbol('[]')))?
260 if (object is Indexable<String, dynamic> ||
261 object is Map<String, dynamic> && !_MAP_PROPERTIES.contains(property)) {
262 return object[smoke.symbolToName(property)];
263 }
264 try { 245 try {
265 return smoke.read(object, property); 246 if (smoke.hasGetter(type, property) || smoke.hasNoSuchMethod(type)) {
247 return smoke.read(object, property);
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 }
266 } on NoSuchMethodError catch (e) { 255 } on NoSuchMethodError catch (e) {
267 // Rethrow, unless the type implements noSuchMethod, in which case we 256 // Rethrow, unless the type implements noSuchMethod, in which case we
268 // interpret the exception as a signal that the method was not found. 257 // interpret the exception as a signal that the method was not found.
269 // Dart note: getting invalid properties is an error, unlike in JS where 258 if (!smoke.hasNoSuchMethod(type)) rethrow;
270 // it returns undefined.
271 if (!smoke.hasNoSuchMethod(object.runtimeType)) rethrow;
272 } 259 }
273 } 260 }
274 261
275 if (_logger.isLoggable(Level.FINER)) { 262 if (_logger.isLoggable(Level.FINER)) {
276 _logger.finer("can't get $property in $object"); 263 _logger.finer("can't get $property in $object");
277 } 264 }
278 return null; 265 return null;
279 } 266 }
280 267
281 bool _setObjectProperty(object, property, value) { 268 bool _setObjectProperty(object, property, value) {
282 if (object == null) return false; 269 if (object == null) return false;
283 270
284 if (property is int) { 271 if (property is int) {
285 if (object is List && property >= 0 && property < object.length) { 272 if (object is List && property >= 0 && property < object.length) {
286 object[property] = value; 273 object[property] = value;
287 return true; 274 return true;
288 } 275 }
289 } else if (property is Symbol) { 276 } else if (property is Symbol) {
290 // Support indexer if available, e.g. Maps or polymer_expressions Scope. 277 final type = object.runtimeType;
291 if (object is Indexable<String, dynamic> ||
292 object is Map<String, dynamic> && !_MAP_PROPERTIES.contains(property)) {
293 object[smoke.symbolToName(property)] = value;
294 return true;
295 }
296 try { 278 try {
297 smoke.write(object, property, value); 279 if (smoke.hasSetter(type, property) || smoke.hasNoSuchMethod(type)) {
298 return true; 280 smoke.write(object, property, value);
299 } on NoSuchMethodError catch (e, s) { 281 return true;
300 if (!smoke.hasNoSuchMethod(object.runtimeType)) rethrow; 282 }
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;
301 } 290 }
302 } 291 }
303 292
304 if (_logger.isLoggable(Level.FINER)) { 293 if (_logger.isLoggable(Level.FINER)) {
305 _logger.finer("can't set $property in $object"); 294 _logger.finer("can't set $property in $object");
306 } 295 }
307 return false; 296 return false;
308 } 297 }
309 298
310 // From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js 299 // From: https://github.com/rafaelw/ChangeSummary/blob/master/change_summary.js
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
474 463
475 if (!changed) return false; 464 if (!changed) return false;
476 465
477 // TODO(rafaelw): Having _observed as the third callback arg here is 466 // TODO(rafaelw): Having _observed as the third callback arg here is
478 // pretty lame API. Fix. 467 // pretty lame API. Fix.
479 _report(_value, oldValues, _observed); 468 _report(_value, oldValues, _observed);
480 return true; 469 return true;
481 } 470 }
482 } 471 }
483 472
484 /// An object accepted by [PropertyPath] where properties are read and written
485 /// as indexing operations, just like a [Map].
486 abstract class Indexable<K, V> {
487 V operator [](K key);
488 operator []=(K key, V value);
489 }
490
491 const _observerSentinel = const _ObserverSentinel(); 473 const _observerSentinel = const _ObserverSentinel();
492 class _ObserverSentinel { const _ObserverSentinel(); } 474 class _ObserverSentinel { const _ObserverSentinel(); }
493 475
494 // A base class for the shared API implemented by PathObserver and 476 // A base class for the shared API implemented by PathObserver and
495 // CompoundObserver and used in _ObservedSet. 477 // CompoundObserver and used in _ObservedSet.
496 abstract class _Observer extends Bindable { 478 abstract class _Observer extends Bindable {
497 static int _nextBirthId = 0; 479 static int _nextBirthId = 0;
498 480
499 /// A number indicating when the object was created. 481 /// A number indicating when the object was created.
500 final int _birthId = _nextBirthId++; 482 final int _birthId = _nextBirthId++;
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
670 for (var observer in _observers.values.toList(growable: false)) { 652 for (var observer in _observers.values.toList(growable: false)) {
671 if (observer._isOpen) observer._check(); 653 if (observer._isOpen) observer._check();
672 } 654 }
673 655
674 _resetNeeded = true; 656 _resetNeeded = true;
675 scheduleMicrotask(reset); 657 scheduleMicrotask(reset);
676 } 658 }
677 } 659 }
678 660
679 const int _MAX_DIRTY_CHECK_CYCLES = 1000; 661 const int _MAX_DIRTY_CHECK_CYCLES = 1000;
OLDNEW
« no previous file with comments | « no previous file | pkg/observe/test/path_observer_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698