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 14 matching lines...) Expand all Loading... | |
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 Loading... | |
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 Loading... | |
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(); |
OLD | NEW |