| 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 |