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 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 // 3) observationState.pendingObservers. This is the set of observers which | 49 // 3) observationState.pendingObservers. This is the set of observers which |
50 // have change records which must be delivered. During "normal" delivery | 50 // have change records which must be delivered. During "normal" delivery |
51 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which | 51 // (i.e. not Object.deliverChangeRecords), this is the mechanism by which |
52 // callbacks are invoked in the proper order until there are no more | 52 // callbacks are invoked in the proper order until there are no more |
53 // change records pending to a callback. | 53 // change records pending to a callback. |
54 // | 54 // |
55 // Note that in order to reduce allocation and processing costs, the | 55 // Note that in order to reduce allocation and processing costs, the |
56 // implementation of (1) and (2) have "optimized" states which represent | 56 // implementation of (1) and (2) have "optimized" states which represent |
57 // common cases which can be handled more efficiently. | 57 // common cases which can be handled more efficiently. |
58 | 58 |
59 var observationState; | 59 var observationState = %GetObservationState(); |
60 | 60 if (IS_UNDEFINED(observationState.callbackInfoMap)) { |
61 function GetObservationState() { | 61 observationState.callbackInfoMap = %ObservationWeakMapCreate(); |
62 if (IS_UNDEFINED(observationState)) | 62 observationState.objectInfoMap = %ObservationWeakMapCreate(); |
63 observationState = %GetObservationState(); | 63 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate(); |
64 | 64 observationState.pendingObservers = null; |
65 if (IS_UNDEFINED(observationState.callbackInfoMap)) { | 65 observationState.nextCallbackPriority = 0; |
66 observationState.callbackInfoMap = %ObservationWeakMapCreate(); | |
67 observationState.objectInfoMap = %ObservationWeakMapCreate(); | |
68 observationState.notifierObjectInfoMap = %ObservationWeakMapCreate(); | |
69 observationState.pendingObservers = null; | |
70 observationState.nextCallbackPriority = 0; | |
71 } | |
72 | |
73 return observationState; | |
74 } | 66 } |
75 | 67 |
76 function GetWeakMapWrapper() { | 68 function ObservationWeakMap(map) { |
77 function MapWrapper(map) { | 69 this.map_ = map; |
78 this.map_ = map; | |
79 }; | |
80 | |
81 MapWrapper.prototype = { | |
82 get: function(key) { | |
83 key = %UnwrapGlobalProxy(key); | |
84 if (!IS_SPEC_OBJECT(key)) return UNDEFINED; | |
85 return %WeakCollectionGet(this.map_, key); | |
86 }, | |
87 set: function(key, value) { | |
88 key = %UnwrapGlobalProxy(key); | |
89 if (!IS_SPEC_OBJECT(key)) return UNDEFINED; | |
90 %WeakCollectionSet(this.map_, key, value); | |
91 }, | |
92 has: function(key) { | |
93 return !IS_UNDEFINED(this.get(key)); | |
94 } | |
95 }; | |
96 | |
97 return MapWrapper; | |
98 } | 70 } |
99 | 71 |
100 var contextMaps; | 72 ObservationWeakMap.prototype = { |
| 73 get: function(key) { |
| 74 key = %UnwrapGlobalProxy(key); |
| 75 if (!IS_SPEC_OBJECT(key)) return UNDEFINED; |
| 76 return %WeakCollectionGet(this.map_, key); |
| 77 }, |
| 78 set: function(key, value) { |
| 79 key = %UnwrapGlobalProxy(key); |
| 80 if (!IS_SPEC_OBJECT(key)) return UNDEFINED; |
| 81 %WeakCollectionSet(this.map_, key, value); |
| 82 }, |
| 83 has: function(key) { |
| 84 return !IS_UNDEFINED(this.get(key)); |
| 85 } |
| 86 }; |
101 | 87 |
102 function GetContextMaps() { | 88 var callbackInfoMap = |
103 if (IS_UNDEFINED(contextMaps)) { | 89 new ObservationWeakMap(observationState.callbackInfoMap); |
104 var map = GetWeakMapWrapper(); | 90 var objectInfoMap = new ObservationWeakMap(observationState.objectInfoMap); |
105 var observationState = GetObservationState(); | 91 var notifierObjectInfoMap = |
106 contextMaps = { | 92 new ObservationWeakMap(observationState.notifierObjectInfoMap); |
107 callbackInfoMap: new map(observationState.callbackInfoMap), | |
108 objectInfoMap: new map(observationState.objectInfoMap), | |
109 notifierObjectInfoMap: new map(observationState.notifierObjectInfoMap) | |
110 }; | |
111 } | |
112 | |
113 return contextMaps; | |
114 } | |
115 | |
116 function GetCallbackInfoMap() { | |
117 return GetContextMaps().callbackInfoMap; | |
118 } | |
119 | |
120 function GetObjectInfoMap() { | |
121 return GetContextMaps().objectInfoMap; | |
122 } | |
123 | |
124 function GetNotifierObjectInfoMap() { | |
125 return GetContextMaps().notifierObjectInfoMap; | |
126 } | |
127 | |
128 function GetPendingObservers() { | |
129 return GetObservationState().pendingObservers; | |
130 } | |
131 | |
132 function SetPendingObservers(pendingObservers) { | |
133 GetObservationState().pendingObservers = pendingObservers; | |
134 } | |
135 | |
136 function GetNextCallbackPriority() { | |
137 return GetObservationState().nextCallbackPriority++; | |
138 } | |
139 | 93 |
140 function nullProtoObject() { | 94 function nullProtoObject() { |
141 return { __proto__: null }; | 95 return { __proto__: null }; |
142 } | 96 } |
143 | 97 |
144 function TypeMapCreate() { | 98 function TypeMapCreate() { |
145 return nullProtoObject(); | 99 return nullProtoObject(); |
146 } | 100 } |
147 | 101 |
148 function TypeMapAddType(typeMap, type, ignoreDuplicate) { | 102 function TypeMapAddType(typeMap, type, ignoreDuplicate) { |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
219 if (!%IsJSProxy(object)) | 173 if (!%IsJSProxy(object)) |
220 %SetIsObserved(object); | 174 %SetIsObserved(object); |
221 | 175 |
222 objectInfo = { | 176 objectInfo = { |
223 object: object, | 177 object: object, |
224 changeObservers: null, | 178 changeObservers: null, |
225 notifier: null, | 179 notifier: null, |
226 performing: null, | 180 performing: null, |
227 performingCount: 0, | 181 performingCount: 0, |
228 }; | 182 }; |
229 GetObjectInfoMap().set(object, objectInfo); | 183 objectInfoMap.set(object, objectInfo); |
230 } | 184 } |
231 return objectInfo; | 185 return objectInfo; |
232 } | 186 } |
233 | 187 |
234 function ObjectInfoGet(object) { | 188 function ObjectInfoGet(object) { |
235 return GetObjectInfoMap().get(object); | 189 return objectInfoMap.get(object); |
236 } | 190 } |
237 | 191 |
238 function ObjectInfoGetFromNotifier(notifier) { | 192 function ObjectInfoGetFromNotifier(notifier) { |
239 return GetNotifierObjectInfoMap().get(notifier); | 193 return notifierObjectInfoMap.get(notifier); |
240 } | 194 } |
241 | 195 |
242 function ObjectInfoGetNotifier(objectInfo) { | 196 function ObjectInfoGetNotifier(objectInfo) { |
243 if (IS_NULL(objectInfo.notifier)) { | 197 if (IS_NULL(objectInfo.notifier)) { |
244 objectInfo.notifier = { __proto__: notifierPrototype }; | 198 objectInfo.notifier = { __proto__: notifierPrototype }; |
245 GetNotifierObjectInfoMap().set(objectInfo.notifier, objectInfo); | 199 notifierObjectInfoMap.set(objectInfo.notifier, objectInfo); |
246 } | 200 } |
247 | 201 |
248 return objectInfo.notifier; | 202 return objectInfo.notifier; |
249 } | 203 } |
250 | 204 |
251 function ObjectInfoGetObject(objectInfo) { | 205 function ObjectInfoGetObject(objectInfo) { |
252 return objectInfo.object; | 206 return objectInfo.object; |
253 } | 207 } |
254 | 208 |
255 function ChangeObserversIsOptimized(changeObservers) { | 209 function ChangeObserversIsOptimized(changeObservers) { |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 arg.length < 0) | 295 arg.length < 0) |
342 return false; | 296 return false; |
343 | 297 |
344 return true; | 298 return true; |
345 } | 299 } |
346 | 300 |
347 // CallbackInfo's optimized state is just a number which represents its global | 301 // CallbackInfo's optimized state is just a number which represents its global |
348 // priority. When a change record must be enqueued for the callback, it | 302 // priority. When a change record must be enqueued for the callback, it |
349 // normalizes. When delivery clears any pending change records, it re-optimizes. | 303 // normalizes. When delivery clears any pending change records, it re-optimizes. |
350 function CallbackInfoGet(callback) { | 304 function CallbackInfoGet(callback) { |
351 return GetCallbackInfoMap().get(callback); | 305 return callbackInfoMap.get(callback); |
352 } | 306 } |
353 | 307 |
354 function CallbackInfoGetOrCreate(callback) { | 308 function CallbackInfoGetOrCreate(callback) { |
355 var callbackInfo = GetCallbackInfoMap().get(callback); | 309 var callbackInfo = callbackInfoMap.get(callback); |
356 if (!IS_UNDEFINED(callbackInfo)) | 310 if (!IS_UNDEFINED(callbackInfo)) |
357 return callbackInfo; | 311 return callbackInfo; |
358 | 312 |
359 var priority = GetNextCallbackPriority(); | 313 var priority = observationState.nextCallbackPriority++ |
360 GetCallbackInfoMap().set(callback, priority); | 314 callbackInfoMap.set(callback, priority); |
361 return priority; | 315 return priority; |
362 } | 316 } |
363 | 317 |
364 function CallbackInfoGetPriority(callbackInfo) { | 318 function CallbackInfoGetPriority(callbackInfo) { |
365 if (IS_NUMBER(callbackInfo)) | 319 if (IS_NUMBER(callbackInfo)) |
366 return callbackInfo; | 320 return callbackInfo; |
367 else | 321 else |
368 return callbackInfo.priority; | 322 return callbackInfo.priority; |
369 } | 323 } |
370 | 324 |
371 function CallbackInfoNormalize(callback) { | 325 function CallbackInfoNormalize(callback) { |
372 var callbackInfo = GetCallbackInfoMap().get(callback); | 326 var callbackInfo = callbackInfoMap.get(callback); |
373 if (IS_NUMBER(callbackInfo)) { | 327 if (IS_NUMBER(callbackInfo)) { |
374 var priority = callbackInfo; | 328 var priority = callbackInfo; |
375 callbackInfo = new InternalArray; | 329 callbackInfo = new InternalArray; |
376 callbackInfo.priority = priority; | 330 callbackInfo.priority = priority; |
377 GetCallbackInfoMap().set(callback, callbackInfo); | 331 callbackInfoMap.set(callback, callbackInfo); |
378 } | 332 } |
379 return callbackInfo; | 333 return callbackInfo; |
380 } | 334 } |
381 | 335 |
382 function ObjectObserve(object, callback, acceptList) { | 336 function ObjectObserve(object, callback, acceptList) { |
383 if (!IS_SPEC_OBJECT(object)) | 337 if (!IS_SPEC_OBJECT(object)) |
384 throw MakeTypeError("observe_non_object", ["observe"]); | 338 throw MakeTypeError("observe_non_object", ["observe"]); |
385 if (!IS_SPEC_FUNCTION(callback)) | 339 if (!IS_SPEC_FUNCTION(callback)) |
386 throw MakeTypeError("observe_non_function", ["observe"]); | 340 throw MakeTypeError("observe_non_function", ["observe"]); |
387 if (ObjectIsFrozen(callback)) | 341 if (ObjectIsFrozen(callback)) |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
429 var callback = ObserverGetCallback(observer); | 383 var callback = ObserverGetCallback(observer); |
430 if (needsAccessCheck && | 384 if (needsAccessCheck && |
431 // Drop all splice records on the floor for access-checked objects | 385 // Drop all splice records on the floor for access-checked objects |
432 (changeRecord.type == 'splice' || | 386 (changeRecord.type == 'splice' || |
433 !%IsAccessAllowedForObserver( | 387 !%IsAccessAllowedForObserver( |
434 callback, changeRecord.object, changeRecord.name))) { | 388 callback, changeRecord.object, changeRecord.name))) { |
435 return; | 389 return; |
436 } | 390 } |
437 | 391 |
438 var callbackInfo = CallbackInfoNormalize(callback); | 392 var callbackInfo = CallbackInfoNormalize(callback); |
439 if (IS_NULL(GetPendingObservers())) { | 393 if (IS_NULL(observationState.pendingObservers)) { |
440 SetPendingObservers(nullProtoObject()) | 394 observationState.pendingObservers = nullProtoObject(); |
441 GetMicrotaskQueue().push(ObserveMicrotaskRunner); | 395 GetMicrotaskQueue().push(ObserveMicrotaskRunner); |
442 %SetMicrotaskPending(true); | 396 %SetMicrotaskPending(true); |
443 } | 397 } |
444 GetPendingObservers()[callbackInfo.priority] = callback; | 398 observationState.pendingObservers[callbackInfo.priority] = callback; |
445 callbackInfo.push(changeRecord); | 399 callbackInfo.push(changeRecord); |
446 } | 400 } |
447 | 401 |
448 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { | 402 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { |
449 if (!ObjectInfoHasActiveObservers(objectInfo)) | 403 if (!ObjectInfoHasActiveObservers(objectInfo)) |
450 return; | 404 return; |
451 | 405 |
452 var hasType = !IS_UNDEFINED(type); | 406 var hasType = !IS_UNDEFINED(type); |
453 var newRecord = hasType ? | 407 var newRecord = hasType ? |
454 { object: ObjectInfoGetObject(objectInfo), type: type } : | 408 { object: ObjectInfoGetObject(objectInfo), type: type } : |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
587 if (!IS_SPEC_OBJECT(object)) | 541 if (!IS_SPEC_OBJECT(object)) |
588 throw MakeTypeError("observe_non_object", ["getNotifier"]); | 542 throw MakeTypeError("observe_non_object", ["getNotifier"]); |
589 | 543 |
590 if (ObjectIsFrozen(object)) return null; | 544 if (ObjectIsFrozen(object)) return null; |
591 | 545 |
592 var objectInfo = ObjectInfoGetOrCreate(object); | 546 var objectInfo = ObjectInfoGetOrCreate(object); |
593 return ObjectInfoGetNotifier(objectInfo); | 547 return ObjectInfoGetNotifier(objectInfo); |
594 } | 548 } |
595 | 549 |
596 function CallbackDeliverPending(callback) { | 550 function CallbackDeliverPending(callback) { |
597 var callbackInfo = GetCallbackInfoMap().get(callback); | 551 var callbackInfo = callbackInfoMap.get(callback); |
598 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) | 552 if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo)) |
599 return false; | 553 return false; |
600 | 554 |
601 // Clear the pending change records from callback and return it to its | 555 // Clear the pending change records from callback and return it to its |
602 // "optimized" state. | 556 // "optimized" state. |
603 var priority = callbackInfo.priority; | 557 var priority = callbackInfo.priority; |
604 GetCallbackInfoMap().set(callback, priority); | 558 callbackInfoMap.set(callback, priority); |
605 | 559 |
606 if (GetPendingObservers()) | 560 if (observationState.pendingObservers) |
607 delete GetPendingObservers()[priority]; | 561 delete observationState.pendingObservers[priority]; |
608 | 562 |
609 var delivered = []; | 563 var delivered = []; |
610 %MoveArrayContents(callbackInfo, delivered); | 564 %MoveArrayContents(callbackInfo, delivered); |
611 | 565 |
612 try { | 566 try { |
613 %_CallFunction(UNDEFINED, delivered, callback); | 567 %_CallFunction(UNDEFINED, delivered, callback); |
614 } catch (ex) {} // TODO(rossberg): perhaps log uncaught exceptions. | 568 } catch (ex) {} // TODO(rossberg): perhaps log uncaught exceptions. |
615 return true; | 569 return true; |
616 } | 570 } |
617 | 571 |
618 function ObjectDeliverChangeRecords(callback) { | 572 function ObjectDeliverChangeRecords(callback) { |
619 if (!IS_SPEC_FUNCTION(callback)) | 573 if (!IS_SPEC_FUNCTION(callback)) |
620 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); | 574 throw MakeTypeError("observe_non_function", ["deliverChangeRecords"]); |
621 | 575 |
622 while (CallbackDeliverPending(callback)) {} | 576 while (CallbackDeliverPending(callback)) {} |
623 } | 577 } |
624 | 578 |
625 function ObserveMicrotaskRunner() { | 579 function ObserveMicrotaskRunner() { |
626 var pendingObservers = GetPendingObservers(); | 580 var pendingObservers = observationState.pendingObservers; |
627 if (pendingObservers) { | 581 if (pendingObservers) { |
628 SetPendingObservers(null); | 582 observationState.pendingObservers = null; |
629 for (var i in pendingObservers) { | 583 for (var i in pendingObservers) { |
630 CallbackDeliverPending(pendingObservers[i]); | 584 CallbackDeliverPending(pendingObservers[i]); |
631 } | 585 } |
632 } | 586 } |
633 } | 587 } |
634 | 588 |
635 function SetupObjectObserve() { | 589 function SetupObjectObserve() { |
636 %CheckIsBootstrapping(); | 590 %CheckIsBootstrapping(); |
637 InstallFunctions($Object, DONT_ENUM, $Array( | 591 InstallFunctions($Object, DONT_ENUM, $Array( |
638 "deliverChangeRecords", ObjectDeliverChangeRecords, | 592 "deliverChangeRecords", ObjectDeliverChangeRecords, |
639 "getNotifier", ObjectGetNotifier, | 593 "getNotifier", ObjectGetNotifier, |
640 "observe", ObjectObserve, | 594 "observe", ObjectObserve, |
641 "unobserve", ObjectUnobserve | 595 "unobserve", ObjectUnobserve |
642 )); | 596 )); |
643 InstallFunctions($Array, DONT_ENUM, $Array( | 597 InstallFunctions($Array, DONT_ENUM, $Array( |
644 "observe", ArrayObserve, | 598 "observe", ArrayObserve, |
645 "unobserve", ArrayUnobserve | 599 "unobserve", ArrayUnobserve |
646 )); | 600 )); |
647 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( | 601 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( |
648 "notify", ObjectNotifierNotify, | 602 "notify", ObjectNotifierNotify, |
649 "performChange", ObjectNotifierPerformChange | 603 "performChange", ObjectNotifierPerformChange |
650 )); | 604 )); |
651 } | 605 } |
652 | 606 |
653 SetupObjectObserve(); | 607 SetupObjectObserve(); |
OLD | NEW |