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

Side by Side Diff: src/object-observe.js

Issue 19541010: Implement optimized objectInfo structure (Closed) Base URL: https://github.com/v8/v8.git@bleeding_edge
Patch Set: added comments Created 7 years, 5 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 14 matching lines...) Expand all
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 27
28 "use strict"; 28 "use strict";
29 29
30 var observationState = %GetObservationState(); 30 var observationState = %GetObservationState();
31 if (IS_UNDEFINED(observationState.callbackInfoMap)) { 31 if (IS_UNDEFINED(observationState.callbackInfoMap)) {
32 observationState.callbackInfoMap = %ObservationWeakMapCreate(); 32 observationState.callbackInfoMap = %ObservationWeakMapCreate();
33 observationState.objectInfoMap = %ObservationWeakMapCreate(); 33 observationState.objectInfoMap = %ObservationWeakMapCreate();
34 observationState.notifierTargetMap = %ObservationWeakMapCreate(); 34 observationState.notifierTargetMap = %ObservationWeakMapCreate();
35 observationState.pendingObservers = new InternalArray; 35 observationState.pendingObservers = { __proto__: null };
rafaelw 2013/07/19 22:21:41 Note that this patch removes all uses of InternalA
36 observationState.anyPendingObservers = false;
36 observationState.nextCallbackPriority = 0; 37 observationState.nextCallbackPriority = 0;
37 } 38 }
38 39
39 function ObservationWeakMap(map) { 40 function ObservationWeakMap(map) {
40 this.map_ = map; 41 this.map_ = map;
41 } 42 }
42 43
43 ObservationWeakMap.prototype = { 44 ObservationWeakMap.prototype = {
44 get: function(key) { 45 get: function(key) {
45 key = %UnwrapGlobalProxy(key); 46 key = %UnwrapGlobalProxy(key);
(...skipping 11 matching lines...) Expand all
57 }; 58 };
58 59
59 var callbackInfoMap = 60 var callbackInfoMap =
60 new ObservationWeakMap(observationState.callbackInfoMap); 61 new ObservationWeakMap(observationState.callbackInfoMap);
61 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap); 62 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap);
62 var notifierTargetMap = 63 var notifierTargetMap =
63 new ObservationWeakMap(observationState.notifierTargetMap); 64 new ObservationWeakMap(observationState.notifierTargetMap);
64 65
65 function CreateObjectInfo(object) { 66 function CreateObjectInfo(object) {
66 var info = { 67 var info = {
67 changeObservers: new InternalArray, 68 changeObserver: null,
69 changeObservers: null,
68 notifier: null, 70 notifier: null,
69 inactiveObservers: new InternalArray, 71 performing: null,
70 performing: { __proto__: null },
71 performingCount: 0, 72 performingCount: 0,
72 }; 73 };
73 objectInfoMap.set(object, info); 74 objectInfoMap.set(object, info);
74 return info; 75 return info;
75 } 76 }
76 77
77 var defaultAcceptTypes = { 78 function CreateTypeMap() {
78 __proto__: null, 79 return { __proto__: null };
adamk 2013/07/23 21:48:08 Have you thought about making this a proper class?
rafaelw 2013/08/02 20:10:32 I have a follow-on patch that optimizes TypeMap to
79 'new': true,
80 'updated': true,
81 'deleted': true,
82 'prototype': true,
83 'reconfigured': true
84 };
85
86 function CreateObserver(callback, accept) {
87 var observer = {
88 __proto__: null,
89 callback: callback,
90 accept: defaultAcceptTypes
91 };
92
93 if (IS_UNDEFINED(accept))
94 return observer;
95
96 var acceptMap = { __proto__: null };
97 for (var i = 0; i < accept.length; i++)
98 acceptMap[accept[i]] = true;
99
100 observer.accept = acceptMap;
101 return observer;
102 } 80 }
103 81
104 function ObserverIsActive(observer, objectInfo) { 82 function AddToTypeMap(typeMap, type, ignoreDuplicate) {
105 if (objectInfo.performingCount === 0) 83 typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1;;
84 }
85
86 function RemoveFromTypeMap(typeMap, type) {
87 typeMap[type]--;
88 }
89
90 function CreateTypeMapFromList(typeList) {
91 var typeMap = CreateTypeMap();
92 for (var i = 0; i < typeList.length; i++) {
93 AddToTypeMap(typeMap, typeList[i], true);
adamk 2013/07/23 21:48:08 Not sure it's worth calling AddToTypeMap here sinc
rafaelw 2013/08/02 20:10:32 I'd like to keep the method calls for all "classes
94 }
95 return typeMap;
96 }
97
98 function TypeMapHasType(typeMap, type) {
99 return typeMap[type];
100 }
101
102 function TypeMapsAreDisjoint(typeMap1, typeMap2) {
103 if (!typeMap1 || !typeMap2)
106 return true; 104 return true;
107 105
108 var performing = objectInfo.performing; 106 for (var type in typeMap1) {
109 for (var type in performing) { 107 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type))
110 if (performing[type] > 0 && observer.accept[type])
111 return false; 108 return false;
112 } 109 }
113 110
114 return true; 111 return true;
115 } 112 }
116 113
117 function ObserverIsInactive(observer, objectInfo) { 114 var defaultAccept = CreateTypeMapFromList(new InternalArray(
adamk 2013/07/23 21:48:08 No need to use an InternalArray here, you can just
rafaelw 2013/08/02 20:10:32 Both done. On 2013/07/23 21:48:08, adamk wrote:
118 return !ObserverIsActive(observer, objectInfo); 115 'new',
116 'updated',
117 'deleted',
118 'prototype',
119 'reconfigured'
120 ));
121
122 // An Observer is a registration to observe an object by a callback with a
123 // a given set of accept types. If the set of accept types is the default
124 // set for Object.observe, the observer is represented as a direct reference
125 // to the callback. An observer never changes its accept types and thus never
126 // needs to "normalize".
127 function CreateObserver(callback, acceptList) {
128 return IS_UNDEFINED(acceptList) ? callback : {
129 __proto__: null,
130 callback: callback,
131 accept: CreateTypeMapFromList(acceptList)
132 };
119 } 133 }
120 134
121 function RemoveNullElements(from) { 135 function ObserverGetCallback(observer) {
122 var i = 0; 136 return IS_SPEC_FUNCTION(observer) ? observer : observer.callback;
123 var j = 0; 137 }
124 for (; i < from.length; i++) { 138
125 if (from[i] === null) 139 function ObserverGetAcceptTypes(observer) {
126 continue; 140 return IS_SPEC_FUNCTION(observer) ? defaultAccept : observer.accept;
127 if (j < i) 141 }
128 from[j] = from[i]; 142
129 j++; 143 function ObserverIsActive(observer, objectInfo) {
144 return TypeMapsAreDisjoint(ObjectGetPerformingTypes(objectInfo),
adamk 2013/07/23 21:48:08 This is the only call to TypeMapsAreDisjoint(); I
rafaelw 2013/08/02 20:10:32 Again, I want to keep TypeMap methods encapsulated
145 ObserverGetAcceptTypes(observer));
146 }
147
148 // The set of observers on an object is called 'changeObservers'. The first
149 // observer is referenced directly via objectInfo.changeObserver. When a second
150 // is added, changeObservers "normalizes" to become a mapping of callback
151 // priority -> observer and is then stored on objectInfo.changeObservers.
152 function NormalizeChangeObservers(objectInfo) {
153 var observer = objectInfo.changeObserver;
154 objectInfo.changeObserver = null;
155 objectInfo.changeObservers = { __proto__: null };
156 var priority = GetCallbackPriority(ObserverGetCallback(observer));
157 objectInfo.changeObservers[priority] = observer;
158 }
159
160 function ObjectAddObserver(objectInfo, observer, priority) {
161 if (!objectInfo.changeObserver && !objectInfo.changeObservers) {
162 objectInfo.changeObserver = observer;
163 return;
130 } 164 }
131 165
132 if (i !== j) 166 if (objectInfo.changeObserver)
133 from.length = from.length - (i - j); 167 NormalizeChangeObservers(objectInfo);
168
169 objectInfo.changeObservers[priority] = observer;
134 } 170 }
135 171
136 function RepartitionObservers(conditionFn, from, to, objectInfo) { 172 function ObjectRemoveObserver(objectInfo, priority) {
137 var anyRemoved = false; 173 if (objectInfo.changeObserver) {
138 for (var i = 0; i < from.length; i++) { 174 var callback = ObserverGetCallback(objectInfo.changeObserver);
139 var observer = from[i]; 175 if (GetCallbackPriority(callback) === priority)
140 if (conditionFn(observer, objectInfo)) { 176 objectInfo.changeObserver = null;
141 anyRemoved = true; 177
142 from[i] = null; 178 return;
143 to.push(observer);
144 }
145 } 179 }
146 180
147 if (anyRemoved) 181 delete objectInfo.changeObservers[priority];
148 RemoveNullElements(from);
149 } 182 }
150 183
151 function BeginPerformChange(objectInfo, type) { 184 function ObjectHasActiveObservers(objectInfo) {
152 objectInfo.performing[type] = (objectInfo.performing[type] || 0) + 1; 185 if (IS_UNDEFINED(objectInfo))
153 objectInfo.performingCount++; 186 return false;
154 RepartitionObservers(ObserverIsInactive, 187
155 objectInfo.changeObservers, 188 if (objectInfo.changeObserver)
156 objectInfo.inactiveObservers, 189 return ObserverIsActive(objectInfo.changeObserver, objectInfo);
157 objectInfo); 190
191 for (var priority in objectInfo.changeObservers) {
192 if (ObserverIsActive(objectInfo.changeObservers[priority], objectInfo))
193 return true;
194 }
195
196 return false;
158 } 197 }
159 198
160 function EndPerformChange(objectInfo, type) { 199 function ObjectAddPerfomingType(objectInfo, type) {
adamk 2013/07/23 21:48:08 Typo: s/Perfoming/Performing/
rafaelw 2013/08/02 20:10:32 Done.
161 objectInfo.performing[type]--; 200 objectInfo.performing = objectInfo.performing || CreateTypeMap();
162 objectInfo.performingCount--; 201 AddToTypeMap(objectInfo.performing, type);
163 RepartitionObservers(ObserverIsActive, 202 objectInfo.performingCount++;
164 objectInfo.inactiveObservers,
165 objectInfo.changeObservers,
166 objectInfo);
167 } 203 }
168 204
169 function EnsureObserverRemoved(objectInfo, callback) { 205 function ObjectRemovePerformingType(objectInfo, type) {
170 function remove(observerList) { 206 objectInfo.performingCount--;
171 for (var i = 0; i < observerList.length; i++) { 207 if (objectInfo.performingCount)
adamk 2013/07/23 21:48:08 a "> 0" would make this clearer to me; and this an
rafaelw 2013/08/02 20:10:32 The comparison is now gone. On 2013/07/23 21:48:0
172 if (observerList[i].callback === callback) { 208 RemoveFromTypeMap(objectInfo.performing, type);
173 observerList.splice(i, 1); 209 else
174 return true; 210 objectInfo.performing = null;
adamk 2013/07/23 21:48:08 Is it worth nulling this out? It seems like that'l
rafaelw 2013/08/02 20:10:32 Fair enough. Removing. On 2013/07/23 21:48:08, ad
175 } 211 }
176 }
177 return false;
178 }
179 212
180 if (!remove(objectInfo.changeObservers)) 213 function ObjectGetPerformingTypes(objectInfo) {
181 remove(objectInfo.inactiveObservers); 214 return objectInfo.performingCount ? objectInfo.performing : null;
adamk 2013/07/23 21:48:08 Maybe a > 0 here too?
rafaelw 2013/08/02 20:10:32 Done.
182 } 215 }
183 216
184 function AcceptArgIsValid(arg) { 217 function AcceptArgIsValid(arg) {
185 if (IS_UNDEFINED(arg)) 218 if (IS_UNDEFINED(arg))
186 return true; 219 return true;
187 220
188 if (!IS_SPEC_OBJECT(arg) || 221 if (!IS_SPEC_OBJECT(arg) ||
189 !IS_NUMBER(arg.length) || 222 !IS_NUMBER(arg.length) ||
190 arg.length < 0) 223 arg.length < 0)
191 return false; 224 return false;
192 225
193 var length = arg.length; 226 var length = arg.length;
194 for (var i = 0; i < length; i++) { 227 for (var i = 0; i < length; i++) {
195 if (!IS_STRING(arg[i])) 228 if (!IS_STRING(arg[i]))
196 return false; 229 return false;
197 } 230 }
198 return true; 231 return true;
199 } 232 }
200 233
234 function GetCallbackPriority(callback) {
235 var callbackInfo = callbackInfoMap.get(callback);
236 if (IS_UNDEFINED(callbackInfo))
237 return;
238 else if (IS_NUMBER(callbackInfo))
239 return callbackInfo;
240 else
241 return callbackInfo.priority;
242 }
243
201 function EnsureCallbackPriority(callback) { 244 function EnsureCallbackPriority(callback) {
202 if (!callbackInfoMap.has(callback)) 245 var priority = GetCallbackPriority(callback);
adamk 2013/07/23 21:48:08 I don't think calling GetCallbackPriority() makes
rafaelw 2013/08/02 20:10:32 The issue here is that CallbackInfo is polymorphic
rafaelw 2013/08/02 20:10:32 So this is really CallbackInfoGetPriority(callback
203 callbackInfoMap.set(callback, observationState.nextCallbackPriority++); 246 if (IS_UNDEFINED(priority)) {
247 priority = observationState.nextCallbackPriority++
248 callbackInfoMap.set(callback, priority);
249 }
250
251 return priority;
204 } 252 }
205 253
206 function NormalizeCallbackInfo(callback) { 254 function NormalizeCallbackInfo(callback) {
207 var callbackInfo = callbackInfoMap.get(callback); 255 var callbackInfo = callbackInfoMap.get(callback);
208 if (IS_NUMBER(callbackInfo)) { 256 if (IS_NUMBER(callbackInfo)) {
209 var priority = callbackInfo; 257 var priority = callbackInfo;
210 callbackInfo = new InternalArray; 258 callbackInfo = new InternalArray;
211 callbackInfo.priority = priority; 259 callbackInfo.priority = priority;
212 callbackInfoMap.set(callback, callbackInfo); 260 callbackInfoMap.set(callback, callbackInfo);
213 } 261 }
214 return callbackInfo; 262 return callbackInfo;
215 } 263 }
216 264
217 function ObjectObserve(object, callback, accept) { 265 function ObjectObserve(object, callback, acceptList) {
218 if (!IS_SPEC_OBJECT(object)) 266 if (!IS_SPEC_OBJECT(object))
219 throw MakeTypeError("observe_non_object", ["observe"]); 267 throw MakeTypeError("observe_non_object", ["observe"]);
220 if (!IS_SPEC_FUNCTION(callback)) 268 if (!IS_SPEC_FUNCTION(callback))
221 throw MakeTypeError("observe_non_function", ["observe"]); 269 throw MakeTypeError("observe_non_function", ["observe"]);
222 if (ObjectIsFrozen(callback)) 270 if (ObjectIsFrozen(callback))
223 throw MakeTypeError("observe_callback_frozen"); 271 throw MakeTypeError("observe_callback_frozen");
224 if (!AcceptArgIsValid(accept)) 272 if (!AcceptArgIsValid(acceptList))
225 throw MakeTypeError("observe_accept_invalid"); 273 throw MakeTypeError("observe_accept_invalid");
226 274
227 EnsureCallbackPriority(callback); 275 var priority = EnsureCallbackPriority(callback);
228 276
229 var objectInfo = objectInfoMap.get(object); 277 var objectInfo = objectInfoMap.get(object);
230 if (IS_UNDEFINED(objectInfo)) { 278 if (IS_UNDEFINED(objectInfo)) {
231 objectInfo = CreateObjectInfo(object); 279 objectInfo = CreateObjectInfo(object);
232 %SetIsObserved(object); 280 %SetIsObserved(object);
233 } 281 }
234 282
235 EnsureObserverRemoved(objectInfo, callback); 283 var observer = CreateObserver(callback, acceptList);
236 284 ObjectAddObserver(objectInfo, observer, priority);
237 var observer = CreateObserver(callback, accept);
238 if (ObserverIsActive(observer, objectInfo))
239 objectInfo.changeObservers.push(observer);
240 else
241 objectInfo.inactiveObservers.push(observer);
242
243 return object; 285 return object;
244 } 286 }
245 287
246 function ObjectUnobserve(object, callback) { 288 function ObjectUnobserve(object, callback) {
247 if (!IS_SPEC_OBJECT(object)) 289 if (!IS_SPEC_OBJECT(object))
248 throw MakeTypeError("observe_non_object", ["unobserve"]); 290 throw MakeTypeError("observe_non_object", ["unobserve"]);
249 if (!IS_SPEC_FUNCTION(callback)) 291 if (!IS_SPEC_FUNCTION(callback))
250 throw MakeTypeError("observe_non_function", ["unobserve"]); 292 throw MakeTypeError("observe_non_function", ["unobserve"]);
251 293
252 var objectInfo = objectInfoMap.get(object); 294 var objectInfo = objectInfoMap.get(object);
253 if (IS_UNDEFINED(objectInfo)) 295 if (IS_UNDEFINED(objectInfo))
254 return object; 296 return object;
255 297
256 EnsureObserverRemoved(objectInfo, callback); 298 var priority = GetCallbackPriority(callback);
299 ObjectRemoveObserver(objectInfo, priority);
257 return object; 300 return object;
258 } 301 }
259 302
260 function ArrayObserve(object, callback) { 303 function ArrayObserve(object, callback) {
261 return ObjectObserve(object, callback, ['new', 304 return ObjectObserve(object, callback, ['new',
262 'updated', 305 'updated',
263 'deleted', 306 'deleted',
264 'splice']); 307 'splice']);
265 } 308 }
266 309
267 function ArrayUnobserve(object, callback) { 310 function ArrayUnobserve(object, callback) {
268 return ObjectUnobserve(object, callback); 311 return ObjectUnobserve(object, callback);
269 } 312 }
270 313
271 function EnqueueToCallback(callback, changeRecord) { 314 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
315 if (!ObserverIsActive(observer, objectInfo) ||
316 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) {
317 return;
318 }
319
320 var callback = ObserverGetCallback(observer);
272 var callbackInfo = NormalizeCallbackInfo(callback); 321 var callbackInfo = NormalizeCallbackInfo(callback);
273 observationState.pendingObservers[callbackInfo.priority] = callback; 322 observationState.pendingObservers[callbackInfo.priority] = callback;
323 observationState.anyPendingObservers = true;
274 callbackInfo.push(changeRecord); 324 callbackInfo.push(changeRecord);
275 %SetObserverDeliveryPending(); 325 %SetObserverDeliveryPending();
276 } 326 }
277 327
278 function EnqueueChangeRecord(changeRecord, observers) { 328 function EnqueueChangeRecordToObservers(changeRecord, objectInfo) {
279 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 329 // TODO(rossberg): adjust once there is a story for symbols vs proxies.
280 if (IS_SYMBOL(changeRecord.name)) return; 330 if (IS_SYMBOL(changeRecord.name)) return;
281 331
282 for (var i = 0; i < observers.length; i++) { 332 if (objectInfo.changeObserver) {
283 var observer = observers[i]; 333 var observer = objectInfo.changeObserver;
284 if (IS_UNDEFINED(observer.accept[changeRecord.type])) 334 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
285 continue; 335 return;
336 }
286 337
287 EnqueueToCallback(observer.callback, changeRecord); 338 for (var priority in objectInfo.changeObservers) {
339 var observer = objectInfo.changeObservers[priority];
340 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
288 } 341 }
289 } 342 }
290 343
291 function BeginPerformSplice(array) { 344 function BeginPerformSplice(array) {
292 var objectInfo = objectInfoMap.get(array); 345 var objectInfo = objectInfoMap.get(array);
293 if (!IS_UNDEFINED(objectInfo)) 346 if (!IS_UNDEFINED(objectInfo))
294 BeginPerformChange(objectInfo, 'splice'); 347 ObjectAddPerfomingType(objectInfo, 'splice');
295 } 348 }
296 349
297 function EndPerformSplice(array) { 350 function EndPerformSplice(array) {
298 var objectInfo = objectInfoMap.get(array); 351 var objectInfo = objectInfoMap.get(array);
299 if (!IS_UNDEFINED(objectInfo)) 352 if (!IS_UNDEFINED(objectInfo))
300 EndPerformChange(objectInfo, 'splice'); 353 ObjectRemovePerformingType(objectInfo, 'splice');
301 } 354 }
302 355
303 function EnqueueSpliceRecord(array, index, removed, addedCount) { 356 function EnqueueSpliceRecord(array, index, removed, addedCount) {
304 var objectInfo = objectInfoMap.get(array); 357 var objectInfo = objectInfoMap.get(array);
305 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0) 358 if (!ObjectHasActiveObservers(objectInfo))
306 return; 359 return;
307 360
308 var changeRecord = { 361 var changeRecord = {
309 type: 'splice', 362 type: 'splice',
310 object: array, 363 object: array,
311 index: index, 364 index: index,
312 removed: removed, 365 removed: removed,
313 addedCount: addedCount 366 addedCount: addedCount
314 }; 367 };
315 368
316 ObjectFreeze(changeRecord); 369 ObjectFreeze(changeRecord);
317 ObjectFreeze(changeRecord.removed); 370 ObjectFreeze(changeRecord.removed);
318 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers); 371 EnqueueChangeRecordToObservers(changeRecord, objectInfo);
319 } 372 }
320 373
321 function NotifyChange(type, object, name, oldValue) { 374 function NotifyChange(type, object, name, oldValue) {
322 var objectInfo = objectInfoMap.get(object); 375 var objectInfo = objectInfoMap.get(object);
323 if (objectInfo.changeObservers.length === 0) 376 if (!ObjectHasActiveObservers(objectInfo))
324 return; 377 return;
325 378
326 var changeRecord = (arguments.length < 4) ? 379 var changeRecord = (arguments.length < 4) ?
327 { type: type, object: object, name: name } : 380 { type: type, object: object, name: name } :
328 { type: type, object: object, name: name, oldValue: oldValue }; 381 { type: type, object: object, name: name, oldValue: oldValue };
329 ObjectFreeze(changeRecord); 382 ObjectFreeze(changeRecord);
330 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers); 383 EnqueueChangeRecordToObservers(changeRecord, objectInfo);
331 } 384 }
332 385
333 var notifierPrototype = {}; 386 var notifierPrototype = {};
334 387
335 function ObjectNotifierNotify(changeRecord) { 388 function ObjectNotifierNotify(changeRecord) {
336 if (!IS_SPEC_OBJECT(this)) 389 if (!IS_SPEC_OBJECT(this))
337 throw MakeTypeError("called_on_non_object", ["notify"]); 390 throw MakeTypeError("called_on_non_object", ["notify"]);
338 391
339 var target = notifierTargetMap.get(this); 392 var target = notifierTargetMap.get(this);
340 if (IS_UNDEFINED(target)) 393 if (IS_UNDEFINED(target))
341 throw MakeTypeError("observe_notify_non_notifier"); 394 throw MakeTypeError("observe_notify_non_notifier");
342 if (!IS_STRING(changeRecord.type)) 395 if (!IS_STRING(changeRecord.type))
343 throw MakeTypeError("observe_type_non_string"); 396 throw MakeTypeError("observe_type_non_string");
344 397
345 var objectInfo = objectInfoMap.get(target); 398 var objectInfo = objectInfoMap.get(target);
346 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0) 399 if (!ObjectHasActiveObservers(objectInfo))
347 return; 400 return;
348 401
349 var newRecord = { object: target }; 402 var newRecord = { object: target };
350 for (var prop in changeRecord) { 403 for (var prop in changeRecord) {
351 if (prop === 'object') continue; 404 if (prop === 'object') continue;
352 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], 405 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop],
353 READ_ONLY + DONT_DELETE); 406 READ_ONLY + DONT_DELETE);
354 } 407 }
355 ObjectFreeze(newRecord); 408 ObjectFreeze(newRecord);
356 409
357 EnqueueChangeRecord(newRecord, objectInfo.changeObservers); 410 EnqueueChangeRecordToObservers(newRecord, objectInfo);
358 } 411 }
359 412
360 function ObjectNotifierPerformChange(changeType, changeFn, receiver) { 413 function ObjectNotifierPerformChange(changeType, changeFn, receiver) {
361 if (!IS_SPEC_OBJECT(this)) 414 if (!IS_SPEC_OBJECT(this))
362 throw MakeTypeError("called_on_non_object", ["performChange"]); 415 throw MakeTypeError("called_on_non_object", ["performChange"]);
363 416
364 var target = notifierTargetMap.get(this); 417 var target = notifierTargetMap.get(this);
365 if (IS_UNDEFINED(target)) 418 if (IS_UNDEFINED(target))
366 throw MakeTypeError("observe_notify_non_notifier"); 419 throw MakeTypeError("observe_notify_non_notifier");
367 if (!IS_STRING(changeType)) 420 if (!IS_STRING(changeType))
368 throw MakeTypeError("observe_perform_non_string"); 421 throw MakeTypeError("observe_perform_non_string");
369 if (!IS_SPEC_FUNCTION(changeFn)) 422 if (!IS_SPEC_FUNCTION(changeFn))
370 throw MakeTypeError("observe_perform_non_function"); 423 throw MakeTypeError("observe_perform_non_function");
371 424
372 if (IS_NULL_OR_UNDEFINED(receiver)) { 425 if (IS_NULL_OR_UNDEFINED(receiver)) {
373 receiver = %GetDefaultReceiver(changeFn) || receiver; 426 receiver = %GetDefaultReceiver(changeFn) || receiver;
374 } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) { 427 } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) {
375 receiver = ToObject(receiver); 428 receiver = ToObject(receiver);
376 } 429 }
377 430
378 var objectInfo = objectInfoMap.get(target); 431 var objectInfo = objectInfoMap.get(target);
379 if (IS_UNDEFINED(objectInfo)) 432 if (IS_UNDEFINED(objectInfo))
380 return; 433 return;
381 434
382 BeginPerformChange(objectInfo, changeType); 435 ObjectAddPerfomingType(objectInfo, changeType);
383 try { 436 try {
384 %_CallFunction(receiver, changeFn); 437 %_CallFunction(receiver, changeFn);
385 } finally { 438 } finally {
386 EndPerformChange(objectInfo, changeType); 439 ObjectRemovePerformingType(objectInfo, changeType);
387 } 440 }
388 } 441 }
389 442
390 function ObjectGetNotifier(object) { 443 function ObjectGetNotifier(object) {
391 if (!IS_SPEC_OBJECT(object)) 444 if (!IS_SPEC_OBJECT(object))
392 throw MakeTypeError("observe_non_object", ["getNotifier"]); 445 throw MakeTypeError("observe_non_object", ["getNotifier"]);
393 446
394 if (ObjectIsFrozen(object)) return null; 447 if (ObjectIsFrozen(object)) return null;
395 448
396 var objectInfo = objectInfoMap.get(object); 449 var objectInfo = objectInfoMap.get(object);
(...skipping 28 matching lines...) Expand all
425 } 478 }
426 479
427 function ObjectDeliverChangeRecords(callback) { 480 function ObjectDeliverChangeRecords(callback) {
428 if (!IS_SPEC_FUNCTION(callback)) 481 if (!IS_SPEC_FUNCTION(callback))
429 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); 482 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
430 483
431 while (CallbackDeliverPending(callback)) {} 484 while (CallbackDeliverPending(callback)) {}
432 } 485 }
433 486
434 function DeliverChangeRecords() { 487 function DeliverChangeRecords() {
435 while (observationState.pendingObservers.length) { 488 while (observationState.anyPendingObservers) {
436 var pendingObservers = observationState.pendingObservers; 489 var pendingObservers = observationState.pendingObservers;
437 observationState.pendingObservers = new InternalArray; 490 observationState.pendingObservers = { __proto__: null };
491 observationState.anyPendingObservers = false
438 for (var i in pendingObservers) { 492 for (var i in pendingObservers) {
adamk 2013/07/23 21:48:08 Can you change this 'i' to 'priority' to match els
rafaelw 2013/08/02 20:10:32 Done.
439 CallbackDeliverPending(pendingObservers[i]); 493 CallbackDeliverPending(pendingObservers[i]);
440 } 494 }
441 } 495 }
442 } 496 }
443 497
444 function SetupObjectObserve() { 498 function SetupObjectObserve() {
445 %CheckIsBootstrapping(); 499 %CheckIsBootstrapping();
446 InstallFunctions($Object, DONT_ENUM, $Array( 500 InstallFunctions($Object, DONT_ENUM, $Array(
447 "deliverChangeRecords", ObjectDeliverChangeRecords, 501 "deliverChangeRecords", ObjectDeliverChangeRecords,
448 "getNotifier", ObjectGetNotifier, 502 "getNotifier", ObjectGetNotifier,
449 "observe", ObjectObserve, 503 "observe", ObjectObserve,
450 "unobserve", ObjectUnobserve 504 "unobserve", ObjectUnobserve
451 )); 505 ));
452 InstallFunctions($Array, DONT_ENUM, $Array( 506 InstallFunctions($Array, DONT_ENUM, $Array(
453 "observe", ArrayObserve, 507 "observe", ArrayObserve,
454 "unobserve", ArrayUnobserve 508 "unobserve", ArrayUnobserve
455 )); 509 ));
456 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( 510 InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
457 "notify", ObjectNotifierNotify, 511 "notify", ObjectNotifierNotify,
458 "performChange", ObjectNotifierPerformChange 512 "performChange", ObjectNotifierPerformChange
459 )); 513 ));
460 } 514 }
461 515
462 SetupObjectObserve(); 516 SetupObjectObserve();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698