From 1f40a39bc2644e67087b9068dabd865171ec75b5 Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 11:53:47 -0500 Subject: [PATCH 1/7] fix(filter): Add test for filtering circular references --- test/ng/filter/filterSpec.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/ng/filter/filterSpec.js b/test/ng/filter/filterSpec.js index 7679136ac9f9..d5cfed7835a0 100644 --- a/test/ng/filter/filterSpec.js +++ b/test/ng/filter/filterSpec.js @@ -34,6 +34,16 @@ describe('Filter: filter', function() { expect(filter(items, 'misko').length).toBe(0); }); + it('should not re-evaluate circular references', function() { + var originalItem = {name: 'misko'}; + var referencedItem = {originalItem: originalItem}; + originalItem.referencedItem = referencedItem; + + var items = [originalItem]; + expect(function() { filter(items, 'not misko') }) + .not.toThrow(new Error("Maximum call stack size exceeded")); + }); + it('should filter on specific property', function() { var items = [{ignore: 'a', name: 'a'}, {ignore: 'a', name: 'abc'}]; expect(filter(items, {}).length).toBe(2); From 7cec2f6b6f05eb550ec041ddb8a3ce00a7101338 Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 11:54:41 -0500 Subject: [PATCH 2/7] fix(filter): End filtering recursion for circular references --- src/ng/filter/filter.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index 5c6bf2d79e7e..ba6e3b260db7 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -151,7 +151,7 @@ function filterFilter() { } } - var search = function(obj, text){ + var search = function(obj, text, orig){ if (typeof text == 'string' && text.charAt(0) === '!') { return !search(obj, text.substr(1)); } @@ -166,7 +166,8 @@ function filterFilter() { return comparator(obj, text); default: for ( var objKey in obj) { - if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { + if (obj[objKey] === orig) return; + if (objKey.charAt(0) !== '$' && search(obj[objKey], text, obj)) { return true; } } From 07d4d14dc25ee220b87ccdfd1cf81edcdc01a558 Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 13:46:04 -0500 Subject: [PATCH 3/7] fix(filter): Add final results of filtering to expectation --- test/ng/filter/filterSpec.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/ng/filter/filterSpec.js b/test/ng/filter/filterSpec.js index d5cfed7835a0..8f01f0f4f04b 100644 --- a/test/ng/filter/filterSpec.js +++ b/test/ng/filter/filterSpec.js @@ -41,7 +41,9 @@ describe('Filter: filter', function() { var items = [originalItem]; expect(function() { filter(items, 'not misko') }) - .not.toThrow(new Error("Maximum call stack size exceeded")); + .not.toThrow(); + + expect(filter(items, 'misko')).toEqual([originalItem]); }); it('should filter on specific property', function() { From 3b96b4e1c5cadf88c1a44dab827407929d71ebcb Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 13:46:52 -0500 Subject: [PATCH 4/7] fix(filter): Use stack of evaluated objects to prevent circular references from re-evaluating --- src/ng/filter/filter.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index ba6e3b260db7..8ddb0856a5d5 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -118,7 +118,8 @@ function filterFilter() { if (!isArray(array)) return array; var comparatorType = typeof(comparator), - predicates = []; + predicates = [], + evaluatedObjects = []; predicates.check = function(value) { for (var j = 0; j < predicates.length; j++) { @@ -151,7 +152,7 @@ function filterFilter() { } } - var search = function(obj, text, orig){ + var search = function(obj, text){ if (typeof text == 'string' && text.charAt(0) === '!') { return !search(obj, text.substr(1)); } @@ -166,9 +167,14 @@ function filterFilter() { return comparator(obj, text); default: for ( var objKey in obj) { - if (obj[objKey] === orig) return; - if (objKey.charAt(0) !== '$' && search(obj[objKey], text, obj)) { - return true; + var value = obj[objKey]; + if (evaluatedObjects.indexOf(value) == -1) { + if (typeof value == 'object') { + evaluatedObjects.push(value); + } + if (objKey.charAt(0) !== '$' && search(value, text)) { + return true; + } } } break; From ca217987934a87f76427630d97bf47ec6ce4bd6e Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 13:55:42 -0500 Subject: [PATCH 5/7] fix(filter): Only perform circular reference check on non-primitive types --- src/ng/filter/filter.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index 8ddb0856a5d5..5747948c1099 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -166,15 +166,15 @@ function filterFilter() { case "object": return comparator(obj, text); default: - for ( var objKey in obj) { + for (var objKey in obj) { var value = obj[objKey]; - if (evaluatedObjects.indexOf(value) == -1) { - if (typeof value == 'object') { + if (typeof value == 'object') { + if (evaluatedObjects.indexOf(value) == -1) { evaluatedObjects.push(value); + if (objKey.charAt(0) !== '$' && search(value, text)) { return true; } } - if (objKey.charAt(0) !== '$' && search(value, text)) { - return true; - } + } else { + if (objKey.charAt(0) !== '$' && search(value, text)) { return true; } } } break; From 245721e994d25cb6b1b18f8d6850233aa20ff1b4 Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 13:58:38 -0500 Subject: [PATCH 6/7] fix(filter): Also perform circular reference check on functions; they can have properties --- src/ng/filter/filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index 5747948c1099..7ad2713b0c24 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -168,7 +168,7 @@ function filterFilter() { default: for (var objKey in obj) { var value = obj[objKey]; - if (typeof value == 'object') { + if (typeof value == 'object' || typeof value == 'function') { if (evaluatedObjects.indexOf(value) == -1) { evaluatedObjects.push(value); if (objKey.charAt(0) !== '$' && search(value, text)) { return true; } From a9d814abd96913fad5d735694778ac6ca624cd97 Mon Sep 17 00:00:00 2001 From: Brett Shollenberger Date: Tue, 18 Feb 2014 16:18:42 -0500 Subject: [PATCH 7/7] fix(filters): Simplify block --- src/ng/filter/filter.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ng/filter/filter.js b/src/ng/filter/filter.js index 7ad2713b0c24..158c848bb737 100644 --- a/src/ng/filter/filter.js +++ b/src/ng/filter/filter.js @@ -167,14 +167,15 @@ function filterFilter() { return comparator(obj, text); default: for (var objKey in obj) { - var value = obj[objKey]; - if (typeof value == 'object' || typeof value == 'function') { - if (evaluatedObjects.indexOf(value) == -1) { + if (objKey.charAt(0) !== '$') { + var value = obj[objKey]; + if (isObject(value) || isFunction(value)) { + if (evaluatedObjects.indexOf(value) >= 0) continue; evaluatedObjects.push(value); - if (objKey.charAt(0) !== '$' && search(value, text)) { return true; } } - } else { - if (objKey.charAt(0) !== '$' && search(value, text)) { return true; } + if (search(value, text)) { + return true; + } } } break;