Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

fix(parse): dirty checking support for objects with null prototype #9568

Closed
wants to merge 8 commits into from
12 changes: 9 additions & 3 deletions src/ng/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,12 @@ function getterFn(path, options, fullExp) {
return fn;
}

var objectValueOf = Object.prototype.valueOf.call;

function getValueOf(value) {
return isFunction(value.valueOf) ? value.valueOf() : objectValueOf(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't do this this way, it needs to be

var objectValueOf = Object.prototype.valueOf;

// ...
objectValueOf.call(value);
// ...

}

///////////////////////////////////

/**
Expand Down Expand Up @@ -1092,7 +1098,7 @@ function $ParseProvider() {
// attempt to convert the value to a primitive type
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
// be cheaply dirty-checked
newValue = newValue.valueOf();
newValue = getValueOf(newValue);

if (typeof newValue === 'object') {
// objects/arrays are not supported - deep-watching them would be too expensive
Expand All @@ -1119,7 +1125,7 @@ function $ParseProvider() {
var newInputValue = inputExpressions(scope);
if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
lastResult = parsedExpression(scope);
oldInputValue = newInputValue && newInputValue.valueOf();
oldInputValue = newInputValue && getValueOf(newInputValue);
}
return lastResult;
}, listener, objectEquality);
Expand All @@ -1136,7 +1142,7 @@ function $ParseProvider() {
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
var newInputValue = inputExpressions[i](scope);
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
oldInputValueOfValues[i] = newInputValue && newInputValue.valueOf();
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
}
}

Expand Down
26 changes: 26 additions & 0 deletions test/ng/parseSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,32 @@ describe('parser', function() {
expect(watcherCalls).toBe(1);
}));

it("should always reevaluate filters with non-primitive input created with Object.create(null)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't call this created with Object.create(null), it's more like with null internal prototype

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or even without internal

inject(function($parse) {
var filterCalls = 0;
$filterProvider.register('foo', valueFn(function(input) {
filterCalls++;
return input;
}));

var parsed = $parse('obj | foo');
var obj = scope.obj = Object.create(null);

var watcherCalls = 0;
scope.$watch(parsed, function(input) {
expect(input).toBe(obj);
watcherCalls++;
});

scope.$digest();
expect(filterCalls).toBe(2);
expect(watcherCalls).toBe(1);

scope.$digest();
expect(filterCalls).toBe(3);
expect(watcherCalls).toBe(1);
}));

it("should not reevaluate filters with non-primitive input that does support valueOf()",
inject(function($parse) {
var filterCalls = 0;
Expand Down