OLD | NEW |
---|---|
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 10 matching lines...) Expand all Loading... | |
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
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.observerInfoMap)) { | 31 if (IS_UNDEFINED(observationState.callbackInfoMap)) { |
32 observationState.observerInfoMap = %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 = new InternalArray; |
36 observationState.observerPriority = 0; | 36 observationState.nextCallbackPriority = 0; |
37 } | 37 } |
38 | 38 |
39 function ObservationWeakMap(map) { | 39 function ObservationWeakMap(map) { |
40 this.map_ = map; | 40 this.map_ = map; |
41 } | 41 } |
42 | 42 |
43 ObservationWeakMap.prototype = { | 43 ObservationWeakMap.prototype = { |
44 get: function(key) { | 44 get: function(key) { |
45 key = %UnwrapGlobalProxy(key); | 45 key = %UnwrapGlobalProxy(key); |
46 if (!IS_SPEC_OBJECT(key)) return void 0; | 46 if (!IS_SPEC_OBJECT(key)) return void 0; |
47 return %WeakMapGet(this.map_, key); | 47 return %WeakMapGet(this.map_, key); |
48 }, | 48 }, |
49 set: function(key, value) { | 49 set: function(key, value) { |
50 key = %UnwrapGlobalProxy(key); | 50 key = %UnwrapGlobalProxy(key); |
51 if (!IS_SPEC_OBJECT(key)) return void 0; | 51 if (!IS_SPEC_OBJECT(key)) return void 0; |
52 %WeakMapSet(this.map_, key, value); | 52 %WeakMapSet(this.map_, key, value); |
53 }, | 53 }, |
54 has: function(key) { | 54 has: function(key) { |
55 return !IS_UNDEFINED(this.get(key)); | 55 return !IS_UNDEFINED(this.get(key)); |
56 } | 56 } |
57 }; | 57 }; |
58 | 58 |
59 var observerInfoMap = | 59 var callbackInfoMap = |
60 new ObservationWeakMap(observationState.observerInfoMap); | 60 new ObservationWeakMap(observationState.callbackInfoMap); |
61 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap); | 61 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap); |
62 var notifierTargetMap = | 62 var notifierTargetMap = |
63 new ObservationWeakMap(observationState.notifierTargetMap); | 63 new ObservationWeakMap(observationState.notifierTargetMap); |
64 | 64 |
65 function CreateObjectInfo(object) { | 65 function CreateObjectInfo(object) { |
66 var info = { | 66 var info = { |
67 changeObservers: new InternalArray, | 67 changeObservers: new InternalArray, |
68 notifier: null, | 68 notifier: null, |
69 inactiveObservers: new InternalArray, | 69 inactiveObservers: new InternalArray, |
70 performing: { __proto__: null }, | 70 performing: { __proto__: null }, |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
191 return false; | 191 return false; |
192 | 192 |
193 var length = arg.length; | 193 var length = arg.length; |
194 for (var i = 0; i < length; i++) { | 194 for (var i = 0; i < length; i++) { |
195 if (!IS_STRING(arg[i])) | 195 if (!IS_STRING(arg[i])) |
196 return false; | 196 return false; |
197 } | 197 } |
198 return true; | 198 return true; |
199 } | 199 } |
200 | 200 |
201 function EnsureCallbackPriority(callback) { | |
202 if (!callbackInfoMap.has(callback)) | |
203 callbackInfoMap.set(callback, observationState.nextCallbackPriority++); | |
204 } | |
205 | |
206 function NormalizeCallback(callback) { | |
rossberg
2013/07/15 17:02:43
Nit: Not sure I like the name too much, since it's
rafaelw
2013/07/15 19:13:11
Agreed. I'd like to be explicit that this call is
| |
207 var callbackInfo = callbackInfoMap.get(callback); | |
208 if (IS_NUMBER(callbackInfo)) { | |
209 var priority = callbackInfo; | |
210 callbackInfo = new InternalArray; | |
211 callbackInfo.priority = priority; | |
212 callbackInfoMap.set(callback, callbackInfo); | |
213 } | |
214 return callbackInfo; | |
215 } | |
216 | |
201 function ObjectObserve(object, callback, accept) { | 217 function ObjectObserve(object, callback, accept) { |
202 if (!IS_SPEC_OBJECT(object)) | 218 if (!IS_SPEC_OBJECT(object)) |
203 throw MakeTypeError("observe_non_object", ["observe"]); | 219 throw MakeTypeError("observe_non_object", ["observe"]); |
204 if (!IS_SPEC_FUNCTION(callback)) | 220 if (!IS_SPEC_FUNCTION(callback)) |
205 throw MakeTypeError("observe_non_function", ["observe"]); | 221 throw MakeTypeError("observe_non_function", ["observe"]); |
206 if (ObjectIsFrozen(callback)) | 222 if (ObjectIsFrozen(callback)) |
207 throw MakeTypeError("observe_callback_frozen"); | 223 throw MakeTypeError("observe_callback_frozen"); |
208 if (!AcceptArgIsValid(accept)) | 224 if (!AcceptArgIsValid(accept)) |
209 throw MakeTypeError("observe_accept_invalid"); | 225 throw MakeTypeError("observe_accept_invalid"); |
210 | 226 |
211 if (!observerInfoMap.has(callback)) { | 227 EnsureCallbackPriority(callback); |
212 observerInfoMap.set(callback, { | |
213 pendingChangeRecords: null, | |
214 priority: observationState.observerPriority++, | |
215 }); | |
216 } | |
217 | 228 |
218 var objectInfo = objectInfoMap.get(object); | 229 var objectInfo = objectInfoMap.get(object); |
219 if (IS_UNDEFINED(objectInfo)) { | 230 if (IS_UNDEFINED(objectInfo)) { |
220 objectInfo = CreateObjectInfo(object); | 231 objectInfo = CreateObjectInfo(object); |
221 %SetIsObserved(object); | 232 %SetIsObserved(object); |
222 } | 233 } |
223 | 234 |
224 EnsureObserverRemoved(objectInfo, callback); | 235 EnsureObserverRemoved(objectInfo, callback); |
225 | 236 |
226 var observer = CreateObserver(callback, accept); | 237 var observer = CreateObserver(callback, accept); |
(...skipping 23 matching lines...) Expand all Loading... | |
250 return ObjectObserve(object, callback, ['new', | 261 return ObjectObserve(object, callback, ['new', |
251 'updated', | 262 'updated', |
252 'deleted', | 263 'deleted', |
253 'splice']); | 264 'splice']); |
254 } | 265 } |
255 | 266 |
256 function ArrayUnobserve(object, callback) { | 267 function ArrayUnobserve(object, callback) { |
257 return ObjectUnobserve(object, callback); | 268 return ObjectUnobserve(object, callback); |
258 } | 269 } |
259 | 270 |
271 function EnqueueToCallback(callback, changeRecord) { | |
272 var callbackInfo = NormalizeCallback(callback); | |
273 observationState.pendingObservers[callbackInfo.priority] = callback; | |
274 callbackInfo.push(changeRecord); | |
275 %SetObserverDeliveryPending(); | |
276 } | |
277 | |
260 function EnqueueChangeRecord(changeRecord, observers) { | 278 function EnqueueChangeRecord(changeRecord, observers) { |
261 // TODO(rossberg): adjust once there is a story for symbols vs proxies. | 279 // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
262 if (IS_SYMBOL(changeRecord.name)) return; | 280 if (IS_SYMBOL(changeRecord.name)) return; |
263 | 281 |
264 for (var i = 0; i < observers.length; i++) { | 282 for (var i = 0; i < observers.length; i++) { |
265 var observer = observers[i]; | 283 var observer = observers[i]; |
266 if (IS_UNDEFINED(observer.accept[changeRecord.type])) | 284 if (IS_UNDEFINED(observer.accept[changeRecord.type])) |
267 continue; | 285 continue; |
268 | 286 |
269 var callback = observer.callback; | 287 EnqueueToCallback(observer.callback, changeRecord); |
270 var observerInfo = observerInfoMap.get(callback); | |
271 observationState.pendingObservers[observerInfo.priority] = callback; | |
272 %SetObserverDeliveryPending(); | |
273 if (IS_NULL(observerInfo.pendingChangeRecords)) { | |
274 observerInfo.pendingChangeRecords = new InternalArray(changeRecord); | |
275 } else { | |
276 observerInfo.pendingChangeRecords.push(changeRecord); | |
277 } | |
278 } | 288 } |
279 } | 289 } |
280 | 290 |
281 function BeginPerformSplice(array) { | 291 function BeginPerformSplice(array) { |
282 var objectInfo = objectInfoMap.get(array); | 292 var objectInfo = objectInfoMap.get(array); |
283 if (!IS_UNDEFINED(objectInfo)) | 293 if (!IS_UNDEFINED(objectInfo)) |
284 BeginPerformChange(objectInfo, 'splice'); | 294 BeginPerformChange(objectInfo, 'splice'); |
285 } | 295 } |
286 | 296 |
287 function EndPerformSplice(array) { | 297 function EndPerformSplice(array) { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
387 if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object); | 397 if (IS_UNDEFINED(objectInfo)) objectInfo = CreateObjectInfo(object); |
388 | 398 |
389 if (IS_NULL(objectInfo.notifier)) { | 399 if (IS_NULL(objectInfo.notifier)) { |
390 objectInfo.notifier = { __proto__: notifierPrototype }; | 400 objectInfo.notifier = { __proto__: notifierPrototype }; |
391 notifierTargetMap.set(objectInfo.notifier, object); | 401 notifierTargetMap.set(objectInfo.notifier, object); |
392 } | 402 } |
393 | 403 |
394 return objectInfo.notifier; | 404 return objectInfo.notifier; |
395 } | 405 } |
396 | 406 |
397 function DeliverChangeRecordsForObserver(observer) { | 407 function CallbackDeliverPending(callback) { |
398 var observerInfo = observerInfoMap.get(observer); | 408 var callbackInfo = callbackInfoMap.get(callback); |
399 if (IS_UNDEFINED(observerInfo)) | 409 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) |
400 return false; | 410 return false; |
401 | 411 |
402 var pendingChangeRecords = observerInfo.pendingChangeRecords; | 412 // Clear the pending change records from callback and return it to its |
403 if (IS_NULL(pendingChangeRecords)) | 413 // "optimized" state. |
404 return false; | 414 var priority = callbackInfo.priority; |
415 callbackInfoMap.set(callback, priority); | |
405 | 416 |
406 observerInfo.pendingChangeRecords = null; | 417 delete observationState.pendingObservers[priority]; |
407 delete observationState.pendingObservers[observerInfo.priority]; | |
408 var delivered = []; | 418 var delivered = []; |
409 %MoveArrayContents(pendingChangeRecords, delivered); | 419 %MoveArrayContents(callbackInfo, delivered); |
420 | |
410 try { | 421 try { |
411 %Call(void 0, delivered, observer); | 422 %Call(void 0, delivered, callback); |
412 } catch (ex) {} | 423 } catch (ex) {} |
413 return true; | 424 return true; |
414 } | 425 } |
415 | 426 |
416 function ObjectDeliverChangeRecords(callback) { | 427 function ObjectDeliverChangeRecords(callback) { |
417 if (!IS_SPEC_FUNCTION(callback)) | 428 if (!IS_SPEC_FUNCTION(callback)) |
418 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); | 429 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); |
419 | 430 |
420 while (DeliverChangeRecordsForObserver(callback)) {} | 431 while (CallbackDeliverPending(callback)) {} |
421 } | 432 } |
422 | 433 |
423 function DeliverChangeRecords() { | 434 function DeliverChangeRecords() { |
424 while (observationState.pendingObservers.length) { | 435 while (observationState.pendingObservers.length) { |
425 var pendingObservers = observationState.pendingObservers; | 436 var pendingObservers = observationState.pendingObservers; |
426 observationState.pendingObservers = new InternalArray; | 437 observationState.pendingObservers = new InternalArray; |
427 for (var i in pendingObservers) { | 438 for (var i in pendingObservers) { |
428 DeliverChangeRecordsForObserver(pendingObservers[i]); | 439 CallbackDeliverPending(pendingObservers[i]); |
429 } | 440 } |
430 } | 441 } |
431 } | 442 } |
432 | 443 |
433 function SetupObjectObserve() { | 444 function SetupObjectObserve() { |
434 %CheckIsBootstrapping(); | 445 %CheckIsBootstrapping(); |
435 InstallFunctions($Object, DONT_ENUM, $Array( | 446 InstallFunctions($Object, DONT_ENUM, $Array( |
436 "deliverChangeRecords", ObjectDeliverChangeRecords, | 447 "deliverChangeRecords", ObjectDeliverChangeRecords, |
437 "getNotifier", ObjectGetNotifier, | 448 "getNotifier", ObjectGetNotifier, |
438 "observe", ObjectObserve, | 449 "observe", ObjectObserve, |
439 "unobserve", ObjectUnobserve | 450 "unobserve", ObjectUnobserve |
440 )); | 451 )); |
441 InstallFunctions($Array, DONT_ENUM, $Array( | 452 InstallFunctions($Array, DONT_ENUM, $Array( |
442 "observe", ArrayObserve, | 453 "observe", ArrayObserve, |
443 "unobserve", ArrayUnobserve | 454 "unobserve", ArrayUnobserve |
444 )); | 455 )); |
445 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( | 456 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( |
446 "notify", ObjectNotifierNotify, | 457 "notify", ObjectNotifierNotify, |
447 "performChange", ObjectNotifierPerformChange | 458 "performChange", ObjectNotifierPerformChange |
448 )); | 459 )); |
449 } | 460 } |
450 | 461 |
451 SetupObjectObserve(); | 462 SetupObjectObserve(); |
OLD | NEW |