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

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: rebase Created 7 years, 4 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 | test/mjsunit/harmony/object-observe.js » ('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 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 13 matching lines...) Expand all
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.notifierObjectInfoMap = %ObservationWeakMapCreate();
35 observationState.pendingObservers = new InternalArray; 35 observationState.pendingObservers = { __proto__: null };
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);
46 if (!IS_SPEC_OBJECT(key)) return void 0; 47 if (!IS_SPEC_OBJECT(key)) return void 0;
47 return %WeakCollectionGet(this.map_, key); 48 return %WeakCollectionGet(this.map_, key);
48 }, 49 },
49 set: function(key, value) { 50 set: function(key, value) {
50 key = %UnwrapGlobalProxy(key); 51 key = %UnwrapGlobalProxy(key);
51 if (!IS_SPEC_OBJECT(key)) return void 0; 52 if (!IS_SPEC_OBJECT(key)) return void 0;
52 %WeakCollectionSet(this.map_, key, value); 53 %WeakCollectionSet(this.map_, key, value);
53 }, 54 },
54 has: function(key) { 55 has: function(key) {
55 return !IS_UNDEFINED(this.get(key)); 56 return !IS_UNDEFINED(this.get(key));
56 } 57 }
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 notifierObjectInfoMap =
63 new ObservationWeakMap(observationState.notifierTargetMap); 64 new ObservationWeakMap(observationState.notifierObjectInfoMap);
64 65
65 function CreateObjectInfo(object) { 66 function TypeMapCreate() {
66 var info = { 67 return { __proto__: null };
67 changeObservers: new InternalArray,
68 notifier: null,
69 inactiveObservers: new InternalArray,
70 performing: { __proto__: null },
71 performingCount: 0,
72 };
73 objectInfoMap.set(object, info);
74 return info;
75 } 68 }
76 69
77 var defaultAcceptTypes = { 70 function TypeMapAddType(typeMap, type, ignoreDuplicate) {
78 __proto__: null, 71 typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1;
rossberg 2013/08/07 13:02:14 Hm, this hack could be avoided if AcceptArgIsValid
rafaelw 2013/08/07 20:36:28 That's not the issue. objectInfo.performing is a t
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 } 72 }
103 73
104 function ObserverIsActive(observer, objectInfo) { 74 function TypeMapRemoveType(typeMap, type) {
105 if (objectInfo.performingCount === 0) 75 typeMap[type]--;
76 }
77
78 function TypeMapCreateFromList(typeList) {
79 var typeMap = TypeMapCreate();
80 for (var i = 0; i < typeList.length; i++) {
81 TypeMapAddType(typeMap, typeList[i], true);
82 }
83 return typeMap;
84 }
85
86 function TypeMapHasType(typeMap, type) {
87 return typeMap[type];
rossberg 2013/08/07 13:02:14 Nit: !!typeMap[type]
rafaelw 2013/08/07 20:36:28 Done.
88 }
89
90 function TypeMapIsDisjointFrom(typeMap1, typeMap2) {
91 if (!typeMap1 || !typeMap2)
106 return true; 92 return true;
107 93
108 var performing = objectInfo.performing; 94 for (var type in typeMap1) {
109 for (var type in performing) { 95 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type))
110 if (performing[type] > 0 && observer.accept[type])
111 return false; 96 return false;
112 } 97 }
113 98
114 return true; 99 return true;
115 } 100 }
116 101
117 function ObserverIsInactive(observer, objectInfo) { 102 var defaultAcceptTypes = TypeMapCreateFromList([
118 return !ObserverIsActive(observer, objectInfo); 103 'new',
104 'updated',
105 'deleted',
106 'prototype',
107 'reconfigured'
108 ]);
109
110 // An Observer is a registration to observe an object by a callback with a
111 // a given set of accept types. If the set of accept types is the default
rossberg 2013/08/07 13:02:14 Nit: duplicate "a"
rafaelw 2013/08/07 20:36:28 Done.
112 // set for Object.observe, the observer is represented as a direct reference
113 // to the callback. An observer never changes its accept types and thus never
114 // needs to "normalize".
115 function ObserverCreate(callback, acceptList) {
116 return IS_UNDEFINED(acceptList) ? callback : {
117 __proto__: null,
118 callback: callback,
119 accept: TypeMapCreateFromList(acceptList)
120 };
119 } 121 }
120 122
121 function RemoveNullElements(from) { 123 function ObserverGetCallback(observer) {
122 var i = 0; 124 return IS_SPEC_FUNCTION(observer) ? observer : observer.callback;
123 var j = 0; 125 }
124 for (; i < from.length; i++) { 126
125 if (from[i] === null) 127 function ObserverGetAcceptTypes(observer) {
126 continue; 128 return IS_SPEC_FUNCTION(observer) ? defaultAcceptTypes : observer.accept;
127 if (j < i) 129 }
128 from[j] = from[i]; 130
129 j++; 131 function ObserverIsActive(observer, objectInfo) {
132 return TypeMapIsDisjointFrom(ObjectInfoGetPerformingTypes(objectInfo),
133 ObserverGetAcceptTypes(observer));
134 }
135
136 function ObjectInfoGetOrCreate(object) {
137 var objectInfo = objectInfoMap.get(object);
138 if (IS_UNDEFINED(objectInfo)) {
139 if (!%IsJSProxy(object))
140 %SetIsObserved(object);
141
142 objectInfo = {
143 object: object,
144 changeObserver: null,
145 changeObservers: null,
146 notifier: null,
147 performing: null,
148 performingCount: 0,
149 };
150 objectInfoMap.set(object, objectInfo);
151 }
152 return objectInfo;
153 }
154
155 function ObjectInfoGet(object) {
rossberg 2013/08/07 13:02:14 Is this still used at all? If not, you could as we
rafaelw 2013/08/07 20:36:28 Done.
156 return objectInfoMap.get(object);
157 }
158
159 function ObjectInfoGetFromNotifier(notifier) {
160 return notifierObjectInfoMap.get(notifier);
161 }
162
163 function ObjectInfoGetNotifier(objectInfo) {
164 if (IS_NULL(objectInfo.notifier)) {
165 objectInfo.notifier = { __proto__: notifierPrototype };
166 notifierObjectInfoMap.set(objectInfo.notifier, objectInfo);
130 } 167 }
131 168
132 if (i !== j) 169 return objectInfo.notifier;
133 from.length = from.length - (i - j);
134 } 170 }
135 171
136 function RepartitionObservers(conditionFn, from, to, objectInfo) { 172 function ObjectInfoGetObject(objectInfo) {
137 var anyRemoved = false; 173 return objectInfo.object;
138 for (var i = 0; i < from.length; i++) { 174 }
139 var observer = from[i]; 175
140 if (conditionFn(observer, objectInfo)) { 176 // The set of observers on an object is called 'changeObservers'. The first
141 anyRemoved = true; 177 // observer is referenced directly via objectInfo.changeObserver. When a second
rossberg 2013/08/07 13:02:14 Wouldn't it be slightly simpler to use only 'chang
rafaelw 2013/08/07 20:36:28 The problem is that an "observer" can be normal (a
rossberg 2013/08/09 11:26:51 I don't feel too strongly about it. But I would ar
rafaelw 2013/08/09 14:14:37 Fair enough. Done. On 2013/08/09 11:26:51, rossbe
142 from[i] = null; 178 // is added, changeObservers "normalizes" to become a mapping of callback
143 to.push(observer); 179 // priority -> observer and is then stored on objectInfo.changeObservers.
144 } 180 function ObjectInfoNormalizeChangeObservers(objectInfo) {
181 var observer = objectInfo.changeObserver;
182 objectInfo.changeObserver = null;
183 objectInfo.changeObservers = { __proto__: null };
184 var callback = ObserverGetCallback(observer);
185 var callbackInfo = CallbackInfoGet(callback);
186 var priority = CallbackInfoGetPriority(callbackInfo);
187 objectInfo.changeObservers[priority] = observer;
188 }
189
190 function ObjectInfoAddObserver(objectInfo, callback, acceptList) {
191 var callbackInfo = CallbackInfoGetOrCreate(callback);
192 var observer = ObserverCreate(callback, acceptList);
193
194 if (!objectInfo.changeObserver && !objectInfo.changeObservers) {
195 objectInfo.changeObserver = observer;
196 return;
145 } 197 }
146 198
147 if (anyRemoved) 199 if (objectInfo.changeObserver)
148 RemoveNullElements(from); 200 ObjectInfoNormalizeChangeObservers(objectInfo);
201
202 var priority = CallbackInfoGetPriority(callbackInfo);
203 objectInfo.changeObservers[priority] = observer;
149 } 204 }
150 205
151 function BeginPerformChange(objectInfo, type) { 206 function ObjectInfoRemoveObserver(objectInfo, callback) {
152 objectInfo.performing[type] = (objectInfo.performing[type] || 0) + 1; 207 if (objectInfo.changeObserver) {
153 objectInfo.performingCount++; 208 if (callback === ObserverGetCallback(objectInfo.changeObserver))
154 RepartitionObservers(ObserverIsInactive, 209 objectInfo.changeObserver = null;
155 objectInfo.changeObservers, 210
156 objectInfo.inactiveObservers, 211 return;
157 objectInfo); 212 }
213
214 var callbackInfo = CallbackInfoGet(callback);
215 var priority = CallbackInfoGetPriority(callbackInfo);
216 delete objectInfo.changeObservers[priority];
158 } 217 }
159 218
160 function EndPerformChange(objectInfo, type) { 219 function ObjectInfoHasActiveObservers(objectInfo) {
161 objectInfo.performing[type]--; 220 if (IS_UNDEFINED(objectInfo))
162 objectInfo.performingCount--; 221 return false;
163 RepartitionObservers(ObserverIsActive, 222
164 objectInfo.inactiveObservers, 223 if (objectInfo.changeObserver)
165 objectInfo.changeObservers, 224 return ObserverIsActive(objectInfo.changeObserver, objectInfo);
166 objectInfo); 225
226 for (var priority in objectInfo.changeObservers) {
227 if (ObserverIsActive(objectInfo.changeObservers[priority], objectInfo))
228 return true;
229 }
230
231 return false;
167 } 232 }
168 233
169 function EnsureObserverRemoved(objectInfo, callback) { 234 function ObjectInfoAddPerformingType(objectInfo, type) {
170 function remove(observerList) { 235 objectInfo.performing = objectInfo.performing || TypeMapCreate();
171 for (var i = 0; i < observerList.length; i++) { 236 TypeMapAddType(objectInfo.performing, type);
172 if (observerList[i].callback === callback) { 237 objectInfo.performingCount++;
173 observerList.splice(i, 1); 238 }
174 return true;
175 }
176 }
177 return false;
178 }
179 239
180 if (!remove(objectInfo.changeObservers)) 240 function ObjectInfoRemovePerformingType(objectInfo, type) {
181 remove(objectInfo.inactiveObservers); 241 objectInfo.performingCount--;
242 TypeMapRemoveType(objectInfo.performing, type);
243 }
244
245 function ObjectInfoGetPerformingTypes(objectInfo) {
246 return objectInfo.performingCount > 0 ? objectInfo.performing : null;
182 } 247 }
183 248
184 function AcceptArgIsValid(arg) { 249 function AcceptArgIsValid(arg) {
185 if (IS_UNDEFINED(arg)) 250 if (IS_UNDEFINED(arg))
186 return true; 251 return true;
187 252
188 if (!IS_SPEC_OBJECT(arg) || 253 if (!IS_SPEC_OBJECT(arg) ||
189 !IS_NUMBER(arg.length) || 254 !IS_NUMBER(arg.length) ||
190 arg.length < 0) 255 arg.length < 0)
191 return false; 256 return false;
192 257
193 var length = arg.length; 258 var length = arg.length;
194 for (var i = 0; i < length; i++) { 259 for (var i = 0; i < length; i++) {
195 if (!IS_STRING(arg[i])) 260 if (!IS_STRING(arg[i]))
196 return false; 261 return false;
197 } 262 }
198 return true; 263 return true;
199 } 264 }
200 265
201 function EnsureCallbackPriority(callback) { 266 function CallbackInfoGet(callback) {
202 if (!callbackInfoMap.has(callback)) 267 return callbackInfoMap.get(callback);
203 callbackInfoMap.set(callback, observationState.nextCallbackPriority++);
204 } 268 }
205 269
206 function NormalizeCallbackInfo(callback) { 270 function CallbackInfoGetOrCreate(callback) {
271 var callbackInfo = callbackInfoMap.get(callback);
272 if (!IS_UNDEFINED(callbackInfo))
273 return callbackInfo;
274
275 var priority = observationState.nextCallbackPriority++
276 callbackInfoMap.set(callback, priority);
277 return priority;
278 }
279
280 function CallbackInfoGetPriority(callbackInfo) {
281 if (IS_NUMBER(callbackInfo))
282 return callbackInfo;
283 else
284 return callbackInfo.priority;
285 }
286
287 function CallbackInfoNormalize(callback) {
207 var callbackInfo = callbackInfoMap.get(callback); 288 var callbackInfo = callbackInfoMap.get(callback);
208 if (IS_NUMBER(callbackInfo)) { 289 if (IS_NUMBER(callbackInfo)) {
209 var priority = callbackInfo; 290 var priority = callbackInfo;
210 callbackInfo = new InternalArray; 291 callbackInfo = new InternalArray;
211 callbackInfo.priority = priority; 292 callbackInfo.priority = priority;
212 callbackInfoMap.set(callback, callbackInfo); 293 callbackInfoMap.set(callback, callbackInfo);
213 } 294 }
214 return callbackInfo; 295 return callbackInfo;
215 } 296 }
216 297
217 function ObjectObserve(object, callback, accept) { 298 function ObjectObserve(object, callback, acceptList) {
218 if (!IS_SPEC_OBJECT(object)) 299 if (!IS_SPEC_OBJECT(object))
219 throw MakeTypeError("observe_non_object", ["observe"]); 300 throw MakeTypeError("observe_non_object", ["observe"]);
220 if (!IS_SPEC_FUNCTION(callback)) 301 if (!IS_SPEC_FUNCTION(callback))
221 throw MakeTypeError("observe_non_function", ["observe"]); 302 throw MakeTypeError("observe_non_function", ["observe"]);
222 if (ObjectIsFrozen(callback)) 303 if (ObjectIsFrozen(callback))
223 throw MakeTypeError("observe_callback_frozen"); 304 throw MakeTypeError("observe_callback_frozen");
224 if (!AcceptArgIsValid(accept)) 305 if (!AcceptArgIsValid(acceptList))
225 throw MakeTypeError("observe_accept_invalid"); 306 throw MakeTypeError("observe_accept_invalid");
226 307
227 EnsureCallbackPriority(callback); 308 var objectInfo = ObjectInfoGetOrCreate(object);
228 309 ObjectInfoAddObserver(objectInfo, callback, acceptList);
229 var objectInfo = objectInfoMap.get(object);
230 if (IS_UNDEFINED(objectInfo)) {
231 objectInfo = CreateObjectInfo(object);
232 %SetIsObserved(object);
233 }
234
235 EnsureObserverRemoved(objectInfo, callback);
236
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; 310 return object;
244 } 311 }
245 312
246 function ObjectUnobserve(object, callback) { 313 function ObjectUnobserve(object, callback) {
247 if (!IS_SPEC_OBJECT(object)) 314 if (!IS_SPEC_OBJECT(object))
248 throw MakeTypeError("observe_non_object", ["unobserve"]); 315 throw MakeTypeError("observe_non_object", ["unobserve"]);
249 if (!IS_SPEC_FUNCTION(callback)) 316 if (!IS_SPEC_FUNCTION(callback))
250 throw MakeTypeError("observe_non_function", ["unobserve"]); 317 throw MakeTypeError("observe_non_function", ["unobserve"]);
251 318
252 var objectInfo = objectInfoMap.get(object); 319 var objectInfo = objectInfoMap.get(object);
253 if (IS_UNDEFINED(objectInfo)) 320 if (IS_UNDEFINED(objectInfo))
254 return object; 321 return object;
255 322
256 EnsureObserverRemoved(objectInfo, callback); 323 ObjectInfoRemoveObserver(objectInfo, callback);
257 return object; 324 return object;
258 } 325 }
259 326
260 function ArrayObserve(object, callback) { 327 function ArrayObserve(object, callback) {
261 return ObjectObserve(object, callback, ['new', 328 return ObjectObserve(object, callback, ['new',
262 'updated', 329 'updated',
263 'deleted', 330 'deleted',
264 'splice']); 331 'splice']);
265 } 332 }
266 333
267 function ArrayUnobserve(object, callback) { 334 function ArrayUnobserve(object, callback) {
268 return ObjectUnobserve(object, callback); 335 return ObjectUnobserve(object, callback);
269 } 336 }
270 337
271 function EnqueueToCallback(callback, changeRecord) { 338 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
272 var callbackInfo = NormalizeCallbackInfo(callback); 339 if (!ObserverIsActive(observer, objectInfo) ||
340 !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) {
341 return;
342 }
343
344 var callback = ObserverGetCallback(observer);
345 var callbackInfo = CallbackInfoNormalize(callback);
273 observationState.pendingObservers[callbackInfo.priority] = callback; 346 observationState.pendingObservers[callbackInfo.priority] = callback;
347 observationState.anyPendingObservers = true;
274 callbackInfo.push(changeRecord); 348 callbackInfo.push(changeRecord);
275 %SetObserverDeliveryPending(); 349 %SetObserverDeliveryPending();
276 } 350 }
277 351
278 function EnqueueChangeRecord(changeRecord, observers) { 352 function ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord) {
279 // TODO(rossberg): adjust once there is a story for symbols vs proxies. 353 // TODO(rossberg): adjust once there is a story for symbols vs proxies.
280 if (IS_SYMBOL(changeRecord.name)) return; 354 if (IS_SYMBOL(changeRecord.name)) return;
281 355
282 for (var i = 0; i < observers.length; i++) { 356 if (objectInfo.changeObserver) {
283 var observer = observers[i]; 357 var observer = objectInfo.changeObserver;
284 if (IS_UNDEFINED(observer.accept[changeRecord.type])) 358 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
285 continue; 359 return;
360 }
286 361
287 EnqueueToCallback(observer.callback, changeRecord); 362 for (var priority in objectInfo.changeObservers) {
363 var observer = objectInfo.changeObservers[priority];
364 ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
288 } 365 }
289 } 366 }
290 367
291 function BeginPerformSplice(array) { 368 function BeginPerformSplice(array) {
292 var objectInfo = objectInfoMap.get(array); 369 var objectInfo = objectInfoMap.get(array);
293 if (!IS_UNDEFINED(objectInfo)) 370 if (!IS_UNDEFINED(objectInfo))
294 BeginPerformChange(objectInfo, 'splice'); 371 ObjectInfoAddPerformingType(objectInfo, 'splice');
295 } 372 }
296 373
297 function EndPerformSplice(array) { 374 function EndPerformSplice(array) {
298 var objectInfo = objectInfoMap.get(array); 375 var objectInfo = objectInfoMap.get(array);
299 if (!IS_UNDEFINED(objectInfo)) 376 if (!IS_UNDEFINED(objectInfo))
300 EndPerformChange(objectInfo, 'splice'); 377 ObjectInfoRemovePerformingType(objectInfo, 'splice');
301 } 378 }
302 379
303 function EnqueueSpliceRecord(array, index, removed, addedCount) { 380 function EnqueueSpliceRecord(array, index, removed, addedCount) {
304 var objectInfo = objectInfoMap.get(array); 381 var objectInfo = objectInfoMap.get(array);
305 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0) 382 if (!ObjectInfoHasActiveObservers(objectInfo))
306 return; 383 return;
307 384
308 var changeRecord = { 385 var changeRecord = {
309 type: 'splice', 386 type: 'splice',
310 object: array, 387 object: array,
311 index: index, 388 index: index,
312 removed: removed, 389 removed: removed,
313 addedCount: addedCount 390 addedCount: addedCount
314 }; 391 };
315 392
316 ObjectFreeze(changeRecord); 393 ObjectFreeze(changeRecord);
317 ObjectFreeze(changeRecord.removed); 394 ObjectFreeze(changeRecord.removed);
318 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers); 395 ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord);
319 } 396 }
320 397
321 function NotifyChange(type, object, name, oldValue) { 398 function NotifyChange(type, object, name, oldValue) {
322 var objectInfo = objectInfoMap.get(object); 399 var objectInfo = objectInfoMap.get(object);
323 if (objectInfo.changeObservers.length === 0) 400 if (!ObjectInfoHasActiveObservers(objectInfo))
324 return; 401 return;
325 402
326 var changeRecord = (arguments.length < 4) ? 403 var changeRecord = (arguments.length < 4) ?
327 { type: type, object: object, name: name } : 404 { type: type, object: object, name: name } :
328 { type: type, object: object, name: name, oldValue: oldValue }; 405 { type: type, object: object, name: name, oldValue: oldValue };
329 ObjectFreeze(changeRecord); 406 ObjectFreeze(changeRecord);
330 EnqueueChangeRecord(changeRecord, objectInfo.changeObservers); 407 ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord);
331 } 408 }
332 409
333 var notifierPrototype = {}; 410 var notifierPrototype = {};
334 411
335 function ObjectNotifierNotify(changeRecord) { 412 function ObjectNotifierNotify(changeRecord) {
336 if (!IS_SPEC_OBJECT(this)) 413 if (!IS_SPEC_OBJECT(this))
337 throw MakeTypeError("called_on_non_object", ["notify"]); 414 throw MakeTypeError("called_on_non_object", ["notify"]);
338 415
339 var target = notifierTargetMap.get(this); 416 var objectInfo = ObjectInfoGetFromNotifier(this);
340 if (IS_UNDEFINED(target)) 417 if (IS_UNDEFINED(objectInfo))
341 throw MakeTypeError("observe_notify_non_notifier"); 418 throw MakeTypeError("observe_notify_non_notifier");
342 if (!IS_STRING(changeRecord.type)) 419 if (!IS_STRING(changeRecord.type))
343 throw MakeTypeError("observe_type_non_string"); 420 throw MakeTypeError("observe_type_non_string");
344 421
345 var objectInfo = objectInfoMap.get(target); 422 if (!ObjectInfoHasActiveObservers(objectInfo))
346 if (IS_UNDEFINED(objectInfo) || objectInfo.changeObservers.length === 0)
347 return; 423 return;
348 424
349 var newRecord = { object: target }; 425 var newRecord = { object: ObjectInfoGetObject(objectInfo) };
350 for (var prop in changeRecord) { 426 for (var prop in changeRecord) {
351 if (prop === 'object') continue; 427 if (prop === 'object') continue;
352 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], 428 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop],
353 READ_ONLY + DONT_DELETE); 429 READ_ONLY + DONT_DELETE);
354 } 430 }
355 ObjectFreeze(newRecord); 431 ObjectFreeze(newRecord);
356 432
357 EnqueueChangeRecord(newRecord, objectInfo.changeObservers); 433 ObjectInfoEnqueueChangeRecord(objectInfo, newRecord);
358 } 434 }
359 435
360 function ObjectNotifierPerformChange(changeType, changeFn, receiver) { 436 function ObjectNotifierPerformChange(changeType, changeFn, receiver) {
361 if (!IS_SPEC_OBJECT(this)) 437 if (!IS_SPEC_OBJECT(this))
362 throw MakeTypeError("called_on_non_object", ["performChange"]); 438 throw MakeTypeError("called_on_non_object", ["performChange"]);
363 439
364 var target = notifierTargetMap.get(this); 440 var objectInfo = ObjectInfoGetFromNotifier(this);
365 if (IS_UNDEFINED(target)) 441
442 if (IS_UNDEFINED(objectInfo))
366 throw MakeTypeError("observe_notify_non_notifier"); 443 throw MakeTypeError("observe_notify_non_notifier");
367 if (!IS_STRING(changeType)) 444 if (!IS_STRING(changeType))
368 throw MakeTypeError("observe_perform_non_string"); 445 throw MakeTypeError("observe_perform_non_string");
369 if (!IS_SPEC_FUNCTION(changeFn)) 446 if (!IS_SPEC_FUNCTION(changeFn))
370 throw MakeTypeError("observe_perform_non_function"); 447 throw MakeTypeError("observe_perform_non_function");
371 448
372 if (IS_NULL_OR_UNDEFINED(receiver)) { 449 if (IS_NULL_OR_UNDEFINED(receiver)) {
373 receiver = %GetDefaultReceiver(changeFn) || receiver; 450 receiver = %GetDefaultReceiver(changeFn) || receiver;
374 } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) { 451 } else if (!IS_SPEC_OBJECT(receiver) && %IsClassicModeFunction(changeFn)) {
375 receiver = ToObject(receiver); 452 receiver = ToObject(receiver);
376 } 453 }
377 454
378 var objectInfo = objectInfoMap.get(target); 455 ObjectInfoAddPerformingType(objectInfo, changeType);
379 if (IS_UNDEFINED(objectInfo))
380 return;
381
382 BeginPerformChange(objectInfo, changeType);
383 try { 456 try {
384 %_CallFunction(receiver, changeFn); 457 %_CallFunction(receiver, changeFn);
385 } finally { 458 } finally {
386 EndPerformChange(objectInfo, changeType); 459 ObjectInfoRemovePerformingType(objectInfo, changeType);
387 } 460 }
388 } 461 }
389 462
390 function ObjectGetNotifier(object) { 463 function ObjectGetNotifier(object) {
391 if (!IS_SPEC_OBJECT(object)) 464 if (!IS_SPEC_OBJECT(object))
392 throw MakeTypeError("observe_non_object", ["getNotifier"]); 465 throw MakeTypeError("observe_non_object", ["getNotifier"]);
393 466
394 if (ObjectIsFrozen(object)) return null; 467 if (ObjectIsFrozen(object)) return null;
395 468
396 var objectInfo = objectInfoMap.get(object); 469 var objectInfo = ObjectInfoGetOrCreate(object);
397 if (IS_UNDEFINED(objectInfo)) { 470 return ObjectInfoGetNotifier(objectInfo);
398 objectInfo = CreateObjectInfo(object);
399 %SetIsObserved(object);
400 }
401
402 if (IS_NULL(objectInfo.notifier)) {
403 objectInfo.notifier = { __proto__: notifierPrototype };
404 notifierTargetMap.set(objectInfo.notifier, object);
405 }
406
407 return objectInfo.notifier;
408 } 471 }
409 472
410 function CallbackDeliverPending(callback) { 473 function CallbackDeliverPending(callback) {
411 var callbackInfo = callbackInfoMap.get(callback); 474 var callbackInfo = callbackInfoMap.get(callback);
412 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) 475 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
413 return false; 476 return false;
414 477
415 // Clear the pending change records from callback and return it to its 478 // Clear the pending change records from callback and return it to its
416 // "optimized" state. 479 // "optimized" state.
417 var priority = callbackInfo.priority; 480 var priority = callbackInfo.priority;
(...skipping 10 matching lines...) Expand all
428 } 491 }
429 492
430 function ObjectDeliverChangeRecords(callback) { 493 function ObjectDeliverChangeRecords(callback) {
431 if (!IS_SPEC_FUNCTION(callback)) 494 if (!IS_SPEC_FUNCTION(callback))
432 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); 495 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]);
433 496
434 while (CallbackDeliverPending(callback)) {} 497 while (CallbackDeliverPending(callback)) {}
435 } 498 }
436 499
437 function DeliverChangeRecords() { 500 function DeliverChangeRecords() {
438 while (observationState.pendingObservers.length) { 501 while (observationState.anyPendingObservers) {
rossberg 2013/08/07 13:02:14 I don't think you need to make this flag part of t
rafaelw 2013/08/07 20:36:28 I don't understand. observationState.anyPendingObs
rossberg 2013/08/09 11:26:51 What I mean is that all you are really implementin
rafaelw 2013/08/09 14:14:37 I tried & profiled this version, but it made deliv
rossberg 2013/08/13 09:19:25 Hm, that sounds like a lot. Did you try a version
rafaelw 2013/08/19 18:51:24 That one is 3x more expensive. On 2013/08/13 09:1
439 var pendingObservers = observationState.pendingObservers; 502 var pendingObservers = observationState.pendingObservers;
440 observationState.pendingObservers = new InternalArray; 503 observationState.pendingObservers = { __proto__: null };
504 observationState.anyPendingObservers = false
441 for (var i in pendingObservers) { 505 for (var i in pendingObservers) {
442 CallbackDeliverPending(pendingObservers[i]); 506 CallbackDeliverPending(pendingObservers[i]);
443 } 507 }
444 } 508 }
445 } 509 }
446 510
447 function SetupObjectObserve() { 511 function SetupObjectObserve() {
448 %CheckIsBootstrapping(); 512 %CheckIsBootstrapping();
449 InstallFunctions($Object, DONT_ENUM, $Array( 513 InstallFunctions($Object, DONT_ENUM, $Array(
450 "deliverChangeRecords", ObjectDeliverChangeRecords, 514 "deliverChangeRecords", ObjectDeliverChangeRecords,
451 "getNotifier", ObjectGetNotifier, 515 "getNotifier", ObjectGetNotifier,
452 "observe", ObjectObserve, 516 "observe", ObjectObserve,
453 "unobserve", ObjectUnobserve 517 "unobserve", ObjectUnobserve
454 )); 518 ));
455 InstallFunctions($Array, DONT_ENUM, $Array( 519 InstallFunctions($Array, DONT_ENUM, $Array(
456 "observe", ArrayObserve, 520 "observe", ArrayObserve,
457 "unobserve", ArrayUnobserve 521 "unobserve", ArrayUnobserve
458 )); 522 ));
459 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( 523 InstallFunctions(notifierPrototype, DONT_ENUM, $Array(
460 "notify", ObjectNotifierNotify, 524 "notify", ObjectNotifierNotify,
461 "performChange", ObjectNotifierPerformChange 525 "performChange", ObjectNotifierPerformChange
462 )); 526 ));
463 } 527 }
464 528
465 SetupObjectObserve(); 529 SetupObjectObserve();
OLDNEW
« no previous file with comments | « no previous file | test/mjsunit/harmony/object-observe.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698