Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
Decouple observedSet from PathObservers
Browse files Browse the repository at this point in the history
The basic idea here is that the observedSet is basically maintaining a write-barrier on all objects which are deps for a set of Paths. If any changes, then all paths must be checked.

However, when paths are dirty-checked (because of deliver() or discardChanges()), it's not necessary to flush pending records.

Because any dependent object changing will only ever cause the paths to be checked for new values, false positives are filtered out.

This is a precursor to allowing multiple separate observers to use the same observedSet as a write-barrier, which will allow sharing of observation of common objects (Object.proto, etc...).
R=arv
BUG=

Review URL: https://codereview.appspot.com/47630043
  • Loading branch information
rafaelw committed Jan 3, 2014
1 parent b28c2db commit 972b4a8
Showing 1 changed file with 64 additions and 58 deletions.
122 changes: 64 additions & 58 deletions src/observe.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@
this.push(part);
}, this);

if (hasEval && !hasObserve && this.length) {
if (hasEval && this.length) {
this.getValueFrom = this.compiledGetValueFromFn();
}
}
Expand Down Expand Up @@ -222,13 +222,20 @@
for (var i = 0; i < this.length; i++) {
if (obj == null)
return;
if (directObserver)
directObserver.observe(obj);
obj = obj[this[i]];
}
return obj;
},

iterateObjects: function(obj, observe) {
for (var i = 0; i < this.length; i++) {
if (obj == null)
return;
observe(obj);
obj = obj[this[i]];
}
},

compiledGetValueFromFn: function() {
var accessors = this.map(function(ident) {
return isIndex(ident) ? '["' + ident + '"]' : '.' + ident;
Expand Down Expand Up @@ -400,51 +407,52 @@
var toRemove = emptyArray;
var discardRecords = false;

function callback(records) {
if (observer && observer.state_ == OPENED && !discardRecords)
observer.check_(records);
function observe(obj) {
if (!isObject(obj) || obj === objProto || obj === arrayProto)
return;

var index = toRemove.indexOf(obj);
if (index >= 0) {
toRemove[index] = undefined;
objects.push(obj);
} else if (objects.indexOf(obj) < 0) {
objects.push(obj);
Object.observe(obj, callback);
}

observe(Object.getPrototypeOf(obj));
}

return {
open: function(obs) {
observer = obs;
},
observe: function(obj) {
if (!isObject(obj) || obj === objProto || obj === arrayProto)
return;
function reset() {
var objs = toRemove === emptyArray ? [] : toRemove;
toRemove = objects;
objects = objs;
}

var index = toRemove.indexOf(obj);
if (index >= 0) {
toRemove[index] = undefined;
objects.push(obj);
} else if (objects.indexOf(obj) < 0) {
objects.push(obj);
Object.observe(obj, callback);
}
function cleanup() {
for (var i = 0; i < toRemove.length; i++) {
var obj = toRemove[i];
if (obj)
Object.unobserve(obj, callback);
}

this.observe(Object.getPrototypeOf(obj));
},
deliver: function(discard) {
discardRecords = discard;
Object.deliverChangeRecords(callback);
discardRecords = false;
},
reset: function() {
if (!objects.length)
return;
toRemove.length = 0;
}

var objs = toRemove === emptyArray ? [] : toRemove;
toRemove = objects;
objects = objs;
},
cleanup: function() {
for (var i = 0; i < toRemove.length; i++) {
var obj = toRemove[i];
if (obj)
Object.unobserve(obj, callback);
}
function callback(records) {
if (!observer || observer.state_ != OPENED || discardRecords)
return;

toRemove.length = 0;
observer.check_(records);
reset();
observer.iterateObjects_(observe);
cleanup();
}

return {
open: function(obs) {
observer = obs;
observer.iterateObjects_(observe);
},
close: function() {
for (var i = 0; i < objects.length; i++) {
Expand Down Expand Up @@ -766,18 +774,13 @@
}
},

check_: function(changeRecords, skipChanges) {
// Note: Extracting this to a member function for use here and below
// regresses dirty-checking path perf by about 25% =-(.
if (this.directObserver_)
this.directObserver_.reset();
iterateObjects_: function(observe) {
this.path_.iterateObjects(this.object_, observe);
},

check_: function(changeRecords, skipChanges) {
var oldValue = this.value_;
this.value_ = this.path_.getValueFrom(this.object_, this.directObserver_);

if (this.directObserver_)
this.directObserver_.cleanup();

this.value_ = this.path_.getValueFrom(this.object_);
if (skipChanges || areSameValue(this.value_, oldValue))
return false;

Expand Down Expand Up @@ -842,10 +845,16 @@
this.value_.push(value);
},

check_: function(changeRecords, skipChanges) {
if (this.directObserver_)
this.directObserver_.reset();
iterateObjects_: function(observe) {
var object;
for (var i = 0; i < this.observed_.length; i += 2) {
object = this.observed_[i]
if (object !== observerSentinel)
this.observed_[i + 1].iterateObjects(object, observe)
}
},

check_: function(changeRecords, skipChanges) {
var oldValues;
for (var i = 0; i < this.observed_.length; i += 2) {
var pathOrObserver = this.observed_[i+1];
Expand All @@ -867,9 +876,6 @@
this.value_[i / 2] = value;
}

if (this.directObserver_)
this.directObserver_.cleanup();

if (!oldValues)
return false;

Expand Down

0 comments on commit 972b4a8

Please sign in to comment.