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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 new ObservationWeakMap(observationState.callbackInfoMap); | 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: 0, |
71 performingCount: 0, | 71 performingCount: 0, |
72 }; | 72 }; |
73 objectInfoMap.set(object, info); | 73 objectInfoMap.set(object, info); |
74 return info; | 74 return info; |
75 } | 75 } |
76 | 76 |
77 var defaultAcceptTypes = { | 77 var typeConstants = { __proto__: null }; |
78 __proto__: null, | 78 var nextTypeConstant = 1; |
79 'new': true, | 79 var MAX_BITFIELD_CONSTANT = 1 << 30; |
80 'updated': true, | |
81 'deleted': true, | |
82 'prototype': true, | |
83 'reconfigured': true | |
84 }; | |
85 | 80 |
86 function CreateObserver(callback, accept) { | 81 function NormalizeTypeMap(typeMap) { |
87 var observer = { | 82 if (IS_NUMBER(typeMap)) { |
| 83 var bitfield = typeMap; |
| 84 var typeMap = []; |
| 85 var constant = 1; |
| 86 while (true) { |
| 87 if (bitfield & constant) |
| 88 typeMap[constant] = 1; |
| 89 if (constant == MAX_BITFIELD_CONSTANT) |
| 90 break; |
| 91 constant = constant << 1; |
| 92 } |
| 93 } |
| 94 |
| 95 return typeMap; |
| 96 } |
| 97 |
| 98 function GetTypeConstant(type) { |
| 99 var constant = typeConstants[type]; |
| 100 if (!constant) { |
| 101 constant = nextTypeConstant; |
| 102 typeConstants[type] = constant; |
| 103 nextTypeConstant = nextTypeConstant < MAX_BITFIELD_CONSTANT ? |
| 104 nextTypeConstant << 1 : nextTypeConstant + 1; |
| 105 } |
| 106 |
| 107 return constant; |
| 108 } |
| 109 |
| 110 function AddToTypeMap(typeMap, type, ignoreDuplicate) { |
| 111 var typeConstant = GetTypeConstant(type); |
| 112 if (IS_NUMBER(typeMap) && |
| 113 typeConstant <= MAX_BITFIELD_CONSTANT && |
| 114 (ignoreDuplicate || !(typeMap & typeConstant))) { |
| 115 typeMap |= typeConstant; |
| 116 return typeMap; |
| 117 } |
| 118 |
| 119 typeMap = NormalizeTypeMap(typeMap); |
| 120 var value = ignoreDuplicate ? 1 : (typeMap[typeConstant] || 0) + 1; |
| 121 typeMap[typeConstant] = value; |
| 122 return typeMap; |
| 123 } |
| 124 |
| 125 function RemoveFromTypeMap(typeMap, type) { |
| 126 var typeConstant = GetTypeConstant(type); |
| 127 if (IS_NUMBER(typeMap)) { |
| 128 if (typeConstant <= MAX_BITFIELD_CONSTANT && (typeMap & typeConstant)) |
| 129 typeMap -= typeConstant; |
| 130 } else if (typeMap[typeConstant] > 0) { |
| 131 typeMap[typeConstant]--; |
| 132 } |
| 133 |
| 134 return typeMap; |
| 135 } |
| 136 |
| 137 function CreateTypeMap(typeList) { |
| 138 var typeMap = 0; |
| 139 for (var i = 0; i < typeList.length; i++) { |
| 140 typeMap = AddToTypeMap(typeMap, typeList[i], true); |
| 141 } |
| 142 |
| 143 return typeMap; |
| 144 } |
| 145 |
| 146 function TypeMapHasType(typeMap, type) { |
| 147 return TypeMapHasConstant(typeMap, GetTypeConstant(type)); |
| 148 } |
| 149 |
| 150 function TypeMapHasConstant(typeMap, constant) { |
| 151 if (IS_NUMBER(typeMap)) |
| 152 return constant > MAX_BITFIELD_CONSTANT ? false : typeMap & constant; |
| 153 else |
| 154 return typeMap[constant] > 0; |
| 155 } |
| 156 |
| 157 function TypeMapsIntersect(typeMap1, typeMap2) { |
| 158 if (IS_NUMBER(typeMap1) && IS_NUMBER(typeMap2)) |
| 159 return typeMap1 & typeMap2; |
| 160 |
| 161 var checkMap = IS_NUMBER(typeMap1) ? typeMap1 : typeMap2; |
| 162 var iterateMap = IS_NUMBER(typeMap1) ? typeMap2 : typeMap1; |
| 163 |
| 164 for (var constant in iterateMap) { |
| 165 if (TypeMapHasConstant(checkMap, constant)) |
| 166 return true; |
| 167 } |
| 168 |
| 169 return false; |
| 170 } |
| 171 |
| 172 var defaultAcceptTypes = CreateTypeMap([ |
| 173 'new', |
| 174 'updated', |
| 175 'deleted', |
| 176 'prototype', |
| 177 'reconfigured' |
| 178 ]); |
| 179 |
| 180 GetTypeConstant('splice'); |
| 181 |
| 182 |
| 183 function CreateObserver(callback, acceptList) { |
| 184 return { |
88 __proto__: null, | 185 __proto__: null, |
89 callback: callback, | 186 callback: callback, |
90 accept: defaultAcceptTypes | 187 acceptMap: IS_UNDEFINED(acceptList) ? |
| 188 defaultAcceptTypes : CreateTypeMap(acceptList) |
91 }; | 189 }; |
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 } | 190 } |
103 | 191 |
104 function ObserverIsActive(observer, objectInfo) { | 192 function ObserverIsActive(observer, objectInfo) { |
105 if (objectInfo.performingCount === 0) | 193 return objectInfo.performingCount === 0 ? |
106 return true; | 194 true : !TypeMapsIntersect(objectInfo.performing, observer.acceptMap); |
107 | |
108 var performing = objectInfo.performing; | |
109 for (var type in performing) { | |
110 if (performing[type] > 0 && observer.accept[type]) | |
111 return false; | |
112 } | |
113 | |
114 return true; | |
115 } | 195 } |
116 | 196 |
117 function ObserverIsInactive(observer, objectInfo) { | 197 function ObserverIsInactive(observer, objectInfo) { |
118 return !ObserverIsActive(observer, objectInfo); | 198 return !ObserverIsActive(observer, objectInfo); |
119 } | 199 } |
120 | 200 |
121 function RemoveNullElements(from) { | 201 function RemoveNullElements(from) { |
122 var i = 0; | 202 var i = 0; |
123 var j = 0; | 203 var j = 0; |
124 for (; i < from.length; i++) { | 204 for (; i < from.length; i++) { |
(...skipping 16 matching lines...) Expand all Loading... |
141 anyRemoved = true; | 221 anyRemoved = true; |
142 from[i] = null; | 222 from[i] = null; |
143 to.push(observer); | 223 to.push(observer); |
144 } | 224 } |
145 } | 225 } |
146 | 226 |
147 if (anyRemoved) | 227 if (anyRemoved) |
148 RemoveNullElements(from); | 228 RemoveNullElements(from); |
149 } | 229 } |
150 | 230 |
| 231 function ObjectAddPerformingType(objectInfo, type) { |
| 232 if (!objectInfo.performing) { |
| 233 objectInfo.performing = AddToTypeMap(0, type); |
| 234 objectInfo.performingCount = 1; |
| 235 return true; |
| 236 } |
| 237 |
| 238 var hadType = TypeMapHasType(objectInfo.performing, type); |
| 239 objectInfo.performing = AddToTypeMap(objectInfo.performing, type); |
| 240 objectInfo.performingCount++; |
| 241 return !hadType; |
| 242 } |
| 243 |
| 244 function ObjectRemovePerformingType(objectInfo, type) { |
| 245 if (objectInfo.performingCount == 1) { |
| 246 objectInfo.performing = 0; |
| 247 objectInfo.performingCount = 0; |
| 248 return true; |
| 249 } |
| 250 |
| 251 objectInfo.performing = RemoveFromTypeMap(objectInfo.performing, type); |
| 252 objectInfo.performingCount--; |
| 253 return !TypeMapHasType(objectInfo.performing, type); |
| 254 } |
| 255 |
151 function BeginPerformChange(objectInfo, type) { | 256 function BeginPerformChange(objectInfo, type) { |
152 objectInfo.performing[type] = (objectInfo.performing[type] || 0) + 1; | 257 if (ObjectAddPerformingType(objectInfo, type)) { |
153 objectInfo.performingCount++; | 258 RepartitionObservers(ObserverIsInactive, |
154 RepartitionObservers(ObserverIsInactive, | 259 objectInfo.changeObservers, |
155 objectInfo.changeObservers, | 260 objectInfo.inactiveObservers, |
156 objectInfo.inactiveObservers, | 261 objectInfo); |
157 objectInfo); | 262 } |
158 } | 263 } |
159 | 264 |
160 function EndPerformChange(objectInfo, type) { | 265 function EndPerformChange(objectInfo, type) { |
161 objectInfo.performing[type]--; | 266 if (ObjectRemovePerformingType(objectInfo, type)) { |
162 objectInfo.performingCount--; | 267 RepartitionObservers(ObserverIsActive, |
163 RepartitionObservers(ObserverIsActive, | 268 objectInfo.inactiveObservers, |
164 objectInfo.inactiveObservers, | 269 objectInfo.changeObservers, |
165 objectInfo.changeObservers, | 270 objectInfo); |
166 objectInfo); | 271 } |
167 } | 272 } |
168 | 273 |
169 function EnsureObserverRemoved(objectInfo, callback) { | 274 function EnsureObserverRemoved(objectInfo, callback) { |
170 function remove(observerList) { | 275 function remove(observerList) { |
171 for (var i = 0; i < observerList.length; i++) { | 276 for (var i = 0; i < observerList.length; i++) { |
172 if (observerList[i].callback === callback) { | 277 if (observerList[i].callback === callback) { |
173 observerList.splice(i, 1); | 278 observerList.splice(i, 1); |
174 return true; | 279 return true; |
175 } | 280 } |
176 } | 281 } |
(...skipping 30 matching lines...) Expand all Loading... |
207 var callbackInfo = callbackInfoMap.get(callback); | 312 var callbackInfo = callbackInfoMap.get(callback); |
208 if (IS_NUMBER(callbackInfo)) { | 313 if (IS_NUMBER(callbackInfo)) { |
209 var priority = callbackInfo; | 314 var priority = callbackInfo; |
210 callbackInfo = new InternalArray; | 315 callbackInfo = new InternalArray; |
211 callbackInfo.priority = priority; | 316 callbackInfo.priority = priority; |
212 callbackInfoMap.set(callback, callbackInfo); | 317 callbackInfoMap.set(callback, callbackInfo); |
213 } | 318 } |
214 return callbackInfo; | 319 return callbackInfo; |
215 } | 320 } |
216 | 321 |
217 function ObjectObserve(object, callback, accept) { | 322 function ObjectObserve(object, callback, acceptList) { |
218 if (!IS_SPEC_OBJECT(object)) | 323 if (!IS_SPEC_OBJECT(object)) |
219 throw MakeTypeError("observe_non_object", ["observe"]); | 324 throw MakeTypeError("observe_non_object", ["observe"]); |
220 if (!IS_SPEC_FUNCTION(callback)) | 325 if (!IS_SPEC_FUNCTION(callback)) |
221 throw MakeTypeError("observe_non_function", ["observe"]); | 326 throw MakeTypeError("observe_non_function", ["observe"]); |
222 if (ObjectIsFrozen(callback)) | 327 if (ObjectIsFrozen(callback)) |
223 throw MakeTypeError("observe_callback_frozen"); | 328 throw MakeTypeError("observe_callback_frozen"); |
224 if (!AcceptArgIsValid(accept)) | 329 if (!AcceptArgIsValid(acceptList)) |
225 throw MakeTypeError("observe_accept_invalid"); | 330 throw MakeTypeError("observe_accept_invalid"); |
226 | 331 |
227 EnsureCallbackPriority(callback); | 332 EnsureCallbackPriority(callback); |
228 | 333 |
229 var objectInfo = objectInfoMap.get(object); | 334 var objectInfo = objectInfoMap.get(object); |
230 if (IS_UNDEFINED(objectInfo)) { | 335 if (IS_UNDEFINED(objectInfo)) { |
231 objectInfo = CreateObjectInfo(object); | 336 objectInfo = CreateObjectInfo(object); |
232 %SetIsObserved(object); | 337 %SetIsObserved(object); |
233 } | 338 } |
234 | 339 |
235 EnsureObserverRemoved(objectInfo, callback); | 340 EnsureObserverRemoved(objectInfo, callback); |
236 | 341 |
237 var observer = CreateObserver(callback, accept); | 342 var observer = CreateObserver(callback, acceptList); |
238 if (ObserverIsActive(observer, objectInfo)) | 343 if (ObserverIsActive(observer, objectInfo)) |
239 objectInfo.changeObservers.push(observer); | 344 objectInfo.changeObservers.push(observer); |
240 else | 345 else |
241 objectInfo.inactiveObservers.push(observer); | 346 objectInfo.inactiveObservers.push(observer); |
242 | 347 |
243 return object; | 348 return object; |
244 } | 349 } |
245 | 350 |
246 function ObjectUnobserve(object, callback) { | 351 function ObjectUnobserve(object, callback) { |
247 if (!IS_SPEC_OBJECT(object)) | 352 if (!IS_SPEC_OBJECT(object)) |
(...skipping 20 matching lines...) Expand all Loading... |
268 return ObjectUnobserve(object, callback); | 373 return ObjectUnobserve(object, callback); |
269 } | 374 } |
270 | 375 |
271 function EnqueueToCallback(callback, changeRecord) { | 376 function EnqueueToCallback(callback, changeRecord) { |
272 var callbackInfo = NormalizeCallbackInfo(callback); | 377 var callbackInfo = NormalizeCallbackInfo(callback); |
273 observationState.pendingObservers[callbackInfo.priority] = callback; | 378 observationState.pendingObservers[callbackInfo.priority] = callback; |
274 callbackInfo.push(changeRecord); | 379 callbackInfo.push(changeRecord); |
275 %SetObserverDeliveryPending(); | 380 %SetObserverDeliveryPending(); |
276 } | 381 } |
277 | 382 |
| 383 function ObserverAcceptsType(observer, type) { |
| 384 return TypeMapHasType(observer.acceptMap, type); |
| 385 } |
| 386 |
278 function EnqueueChangeRecord(changeRecord, observers) { | 387 function EnqueueChangeRecord(changeRecord, observers) { |
279 // TODO(rossberg): adjust once there is a story for symbols vs proxies. | 388 // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
280 if (IS_SYMBOL(changeRecord.name)) return; | 389 if (IS_SYMBOL(changeRecord.name)) return; |
281 | 390 |
282 for (var i = 0; i < observers.length; i++) { | 391 for (var i = 0; i < observers.length; i++) { |
283 var observer = observers[i]; | 392 var observer = observers[i]; |
284 if (IS_UNDEFINED(observer.accept[changeRecord.type])) | 393 if (ObserverAcceptsType(observer, changeRecord.type)) |
285 continue; | 394 EnqueueToCallback(observer.callback, changeRecord); |
286 | |
287 EnqueueToCallback(observer.callback, changeRecord); | |
288 } | 395 } |
289 } | 396 } |
290 | 397 |
291 function BeginPerformSplice(array) { | 398 function BeginPerformSplice(array) { |
292 var objectInfo = objectInfoMap.get(array); | 399 var objectInfo = objectInfoMap.get(array); |
293 if (!IS_UNDEFINED(objectInfo)) | 400 if (!IS_UNDEFINED(objectInfo)) |
294 BeginPerformChange(objectInfo, 'splice'); | 401 BeginPerformChange(objectInfo, 'splice'); |
295 } | 402 } |
296 | 403 |
297 function EndPerformSplice(array) { | 404 function EndPerformSplice(array) { |
(...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 "observe", ArrayObserve, | 560 "observe", ArrayObserve, |
454 "unobserve", ArrayUnobserve | 561 "unobserve", ArrayUnobserve |
455 )); | 562 )); |
456 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( | 563 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( |
457 "notify", ObjectNotifierNotify, | 564 "notify", ObjectNotifierNotify, |
458 "performChange", ObjectNotifierPerformChange | 565 "performChange", ObjectNotifierPerformChange |
459 )); | 566 )); |
460 } | 567 } |
461 | 568 |
462 SetupObjectObserve(); | 569 SetupObjectObserve(); |
OLD | NEW |