| 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 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 121 | 121 |
| 122 for (var type in typeMap1) { | 122 for (var type in typeMap1) { |
| 123 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type)) | 123 if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type)) |
| 124 return false; | 124 return false; |
| 125 } | 125 } |
| 126 | 126 |
| 127 return true; | 127 return true; |
| 128 } | 128 } |
| 129 | 129 |
| 130 var defaultAcceptTypes = TypeMapCreateFromList([ | 130 var defaultAcceptTypes = TypeMapCreateFromList([ |
| 131 'new', | 131 'add', |
| 132 'updated', | 132 'update', |
| 133 'deleted', | 133 'delete', |
| 134 'prototype', | 134 'setPrototype', |
| 135 'reconfigured' | 135 'reconfigure', |
| 136 'preventExtensions' |
| 136 ]); | 137 ]); |
| 137 | 138 |
| 138 // An Observer is a registration to observe an object by a callback with | 139 // An Observer is a registration to observe an object by a callback with |
| 139 // a given set of accept types. If the set of accept types is the default | 140 // a given set of accept types. If the set of accept types is the default |
| 140 // set for Object.observe, the observer is represented as a direct reference | 141 // set for Object.observe, the observer is represented as a direct reference |
| 141 // to the callback. An observer never changes its accept types and thus never | 142 // to the callback. An observer never changes its accept types and thus never |
| 142 // needs to "normalize". | 143 // needs to "normalize". |
| 143 function ObserverCreate(callback, acceptList) { | 144 function ObserverCreate(callback, acceptList) { |
| 144 return IS_UNDEFINED(acceptList) ? callback : { | 145 return IS_UNDEFINED(acceptList) ? callback : { |
| 145 __proto__: null, | 146 __proto__: null, |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 345 | 346 |
| 346 var objectInfo = objectInfoMap.get(object); | 347 var objectInfo = objectInfoMap.get(object); |
| 347 if (IS_UNDEFINED(objectInfo)) | 348 if (IS_UNDEFINED(objectInfo)) |
| 348 return object; | 349 return object; |
| 349 | 350 |
| 350 ObjectInfoRemoveObserver(objectInfo, callback); | 351 ObjectInfoRemoveObserver(objectInfo, callback); |
| 351 return object; | 352 return object; |
| 352 } | 353 } |
| 353 | 354 |
| 354 function ArrayObserve(object, callback) { | 355 function ArrayObserve(object, callback) { |
| 355 return ObjectObserve(object, callback, ['new', | 356 return ObjectObserve(object, callback, ['add', |
| 356 'updated', | 357 'update', |
| 357 'deleted', | 358 'delete', |
| 358 'splice']); | 359 'splice']); |
| 359 } | 360 } |
| 360 | 361 |
| 361 function ArrayUnobserve(object, callback) { | 362 function ArrayUnobserve(object, callback) { |
| 362 return ObjectUnobserve(object, callback); | 363 return ObjectUnobserve(object, callback); |
| 363 } | 364 } |
| 364 | 365 |
| 365 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord, | 366 function ObserverEnqueueIfActive(observer, objectInfo, changeRecord, |
| 366 needsAccessCheck) { | 367 needsAccessCheck) { |
| 367 if (!ObserverIsActive(observer, objectInfo) || | 368 if (!ObserverIsActive(observer, objectInfo) || |
| (...skipping 11 matching lines...) Expand all Loading... |
| 379 } | 380 } |
| 380 | 381 |
| 381 var callbackInfo = CallbackInfoNormalize(callback); | 382 var callbackInfo = CallbackInfoNormalize(callback); |
| 382 if (!observationState.pendingObservers) | 383 if (!observationState.pendingObservers) |
| 383 observationState.pendingObservers = { __proto__: null }; | 384 observationState.pendingObservers = { __proto__: null }; |
| 384 observationState.pendingObservers[callbackInfo.priority] = callback; | 385 observationState.pendingObservers[callbackInfo.priority] = callback; |
| 385 callbackInfo.push(changeRecord); | 386 callbackInfo.push(changeRecord); |
| 386 %SetObserverDeliveryPending(); | 387 %SetObserverDeliveryPending(); |
| 387 } | 388 } |
| 388 | 389 |
| 389 function ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord, | 390 function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) { |
| 390 skipAccessCheck) { | 391 if (!ObjectInfoHasActiveObservers(objectInfo)) |
| 392 return; |
| 393 |
| 394 var hasType = !IS_UNDEFINED(type); |
| 395 var newRecord = hasType ? |
| 396 { object: ObjectInfoGetObject(objectInfo), type: type } : |
| 397 { object: ObjectInfoGetObject(objectInfo) }; |
| 398 |
| 399 for (var prop in changeRecord) { |
| 400 if (prop === 'object' || (hasType && prop === 'type')) continue; |
| 401 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], |
| 402 READ_ONLY + DONT_DELETE); |
| 403 } |
| 404 ObjectFreeze(newRecord); |
| 405 |
| 406 ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord, |
| 407 true /* skip access check */); |
| 408 } |
| 409 |
| 410 function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord, |
| 411 skipAccessCheck) { |
| 391 // TODO(rossberg): adjust once there is a story for symbols vs proxies. | 412 // TODO(rossberg): adjust once there is a story for symbols vs proxies. |
| 392 if (IS_SYMBOL(changeRecord.name)) return; | 413 if (IS_SYMBOL(changeRecord.name)) return; |
| 393 | 414 |
| 394 var needsAccessCheck = !skipAccessCheck && | 415 var needsAccessCheck = !skipAccessCheck && |
| 395 %IsAccessCheckNeeded(changeRecord.object); | 416 %IsAccessCheckNeeded(changeRecord.object); |
| 396 | 417 |
| 397 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { | 418 if (ChangeObserversIsOptimized(objectInfo.changeObservers)) { |
| 398 var observer = objectInfo.changeObservers; | 419 var observer = objectInfo.changeObservers; |
| 399 ObserverEnqueueIfActive(observer, objectInfo, changeRecord, | 420 ObserverEnqueueIfActive(observer, objectInfo, changeRecord, |
| 400 needsAccessCheck); | 421 needsAccessCheck); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 428 var changeRecord = { | 449 var changeRecord = { |
| 429 type: 'splice', | 450 type: 'splice', |
| 430 object: array, | 451 object: array, |
| 431 index: index, | 452 index: index, |
| 432 removed: removed, | 453 removed: removed, |
| 433 addedCount: addedCount | 454 addedCount: addedCount |
| 434 }; | 455 }; |
| 435 | 456 |
| 436 ObjectFreeze(changeRecord); | 457 ObjectFreeze(changeRecord); |
| 437 ObjectFreeze(changeRecord.removed); | 458 ObjectFreeze(changeRecord.removed); |
| 438 ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord); | 459 ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord); |
| 439 } | 460 } |
| 440 | 461 |
| 441 function NotifyChange(type, object, name, oldValue) { | 462 function NotifyChange(type, object, name, oldValue) { |
| 442 var objectInfo = objectInfoMap.get(object); | 463 var objectInfo = objectInfoMap.get(object); |
| 443 if (!ObjectInfoHasActiveObservers(objectInfo)) | 464 if (!ObjectInfoHasActiveObservers(objectInfo)) |
| 444 return; | 465 return; |
| 445 | 466 |
| 446 var changeRecord = (arguments.length < 4) ? | 467 var changeRecord; |
| 447 { type: type, object: object, name: name } : | 468 if (arguments.length == 2) { |
| 448 { type: type, object: object, name: name, oldValue: oldValue }; | 469 changeRecord = { type: type, object: object }; |
| 470 } else if (arguments.length == 3) { |
| 471 changeRecord = { type: type, object: object, name: name }; |
| 472 } else { |
| 473 changeRecord = { |
| 474 type: type, |
| 475 object: object, |
| 476 name: name, |
| 477 oldValue: oldValue |
| 478 }; |
| 479 } |
| 480 |
| 449 ObjectFreeze(changeRecord); | 481 ObjectFreeze(changeRecord); |
| 450 ObjectInfoEnqueueChangeRecord(objectInfo, changeRecord); | 482 ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord); |
| 451 } | 483 } |
| 452 | 484 |
| 453 var notifierPrototype = {}; | 485 var notifierPrototype = {}; |
| 454 | 486 |
| 455 function ObjectNotifierNotify(changeRecord) { | 487 function ObjectNotifierNotify(changeRecord) { |
| 456 if (!IS_SPEC_OBJECT(this)) | 488 if (!IS_SPEC_OBJECT(this)) |
| 457 throw MakeTypeError("called_on_non_object", ["notify"]); | 489 throw MakeTypeError("called_on_non_object", ["notify"]); |
| 458 | 490 |
| 459 var objectInfo = ObjectInfoGetFromNotifier(this); | 491 var objectInfo = ObjectInfoGetFromNotifier(this); |
| 460 if (IS_UNDEFINED(objectInfo)) | 492 if (IS_UNDEFINED(objectInfo)) |
| 461 throw MakeTypeError("observe_notify_non_notifier"); | 493 throw MakeTypeError("observe_notify_non_notifier"); |
| 462 if (!IS_STRING(changeRecord.type)) | 494 if (!IS_STRING(changeRecord.type)) |
| 463 throw MakeTypeError("observe_type_non_string"); | 495 throw MakeTypeError("observe_type_non_string"); |
| 464 | 496 |
| 465 if (!ObjectInfoHasActiveObservers(objectInfo)) | 497 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord); |
| 466 return; | |
| 467 | |
| 468 var newRecord = { object: ObjectInfoGetObject(objectInfo) }; | |
| 469 for (var prop in changeRecord) { | |
| 470 if (prop === 'object') continue; | |
| 471 %DefineOrRedefineDataProperty(newRecord, prop, changeRecord[prop], | |
| 472 READ_ONLY + DONT_DELETE); | |
| 473 } | |
| 474 ObjectFreeze(newRecord); | |
| 475 | |
| 476 ObjectInfoEnqueueChangeRecord(objectInfo, newRecord, | |
| 477 true /* skip access check */); | |
| 478 } | 498 } |
| 479 | 499 |
| 480 function ObjectNotifierPerformChange(changeType, changeFn) { | 500 function ObjectNotifierPerformChange(changeType, changeFn) { |
| 481 if (!IS_SPEC_OBJECT(this)) | 501 if (!IS_SPEC_OBJECT(this)) |
| 482 throw MakeTypeError("called_on_non_object", ["performChange"]); | 502 throw MakeTypeError("called_on_non_object", ["performChange"]); |
| 483 | 503 |
| 484 var objectInfo = ObjectInfoGetFromNotifier(this); | 504 var objectInfo = ObjectInfoGetFromNotifier(this); |
| 485 | 505 |
| 486 if (IS_UNDEFINED(objectInfo)) | 506 if (IS_UNDEFINED(objectInfo)) |
| 487 throw MakeTypeError("observe_notify_non_notifier"); | 507 throw MakeTypeError("observe_notify_non_notifier"); |
| 488 if (!IS_STRING(changeType)) | 508 if (!IS_STRING(changeType)) |
| 489 throw MakeTypeError("observe_perform_non_string"); | 509 throw MakeTypeError("observe_perform_non_string"); |
| 490 if (!IS_SPEC_FUNCTION(changeFn)) | 510 if (!IS_SPEC_FUNCTION(changeFn)) |
| 491 throw MakeTypeError("observe_perform_non_function"); | 511 throw MakeTypeError("observe_perform_non_function"); |
| 492 | 512 |
| 493 ObjectInfoAddPerformingType(objectInfo, changeType); | 513 ObjectInfoAddPerformingType(objectInfo, changeType); |
| 514 |
| 515 var changeRecord; |
| 494 try { | 516 try { |
| 495 %_CallFunction(UNDEFINED, changeFn); | 517 changeRecord = %_CallFunction(UNDEFINED, changeFn); |
| 496 } finally { | 518 } finally { |
| 497 ObjectInfoRemovePerformingType(objectInfo, changeType); | 519 ObjectInfoRemovePerformingType(objectInfo, changeType); |
| 498 } | 520 } |
| 521 |
| 522 if (IS_SPEC_OBJECT(changeRecord)) |
| 523 ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType); |
| 499 } | 524 } |
| 500 | 525 |
| 501 function ObjectGetNotifier(object) { | 526 function ObjectGetNotifier(object) { |
| 502 if (!IS_SPEC_OBJECT(object)) | 527 if (!IS_SPEC_OBJECT(object)) |
| 503 throw MakeTypeError("observe_non_object", ["getNotifier"]); | 528 throw MakeTypeError("observe_non_object", ["getNotifier"]); |
| 504 | 529 |
| 505 if (ObjectIsFrozen(object)) return null; | 530 if (ObjectIsFrozen(object)) return null; |
| 506 | 531 |
| 507 var objectInfo = ObjectInfoGet(object); | 532 var objectInfo = ObjectInfoGet(object); |
| 508 return ObjectInfoGetNotifier(objectInfo); | 533 return ObjectInfoGetNotifier(objectInfo); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 "observe", ArrayObserve, | 584 "observe", ArrayObserve, |
| 560 "unobserve", ArrayUnobserve | 585 "unobserve", ArrayUnobserve |
| 561 )); | 586 )); |
| 562 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( | 587 InstallFunctions(notifierPrototype, DONT_ENUM, $Array( |
| 563 "notify", ObjectNotifierNotify, | 588 "notify", ObjectNotifierNotify, |
| 564 "performChange", ObjectNotifierPerformChange | 589 "performChange", ObjectNotifierPerformChange |
| 565 )); | 590 )); |
| 566 } | 591 } |
| 567 | 592 |
| 568 SetupObjectObserve(); | 593 SetupObjectObserve(); |
| OLD | NEW |