@@ -622,15 +622,44 @@ function isStateless($filter, filterName) {
622
622
return ! fn . $stateful ;
623
623
}
624
624
625
- function findConstantAndWatchExpressions ( ast , $filter ) {
625
+ // Detect nodes which could depend on non-shallow state of objects
626
+ function isPure ( node , parentIsPure ) {
627
+ switch ( node . type ) {
628
+ // Computed members might invoke a stateful toString()
629
+ case AST . MemberExpression :
630
+ if ( node . computed ) {
631
+ return false ;
632
+ }
633
+ break ;
634
+
635
+ // Unary always convert to primative
636
+ case AST . UnaryExpression :
637
+ return true ;
638
+
639
+ // The binary + operator can invoke a stateful toString().
640
+ case AST . BinaryExpression :
641
+ return node . operator !== '+' ;
642
+
643
+ // Functions / filters probably read state from within objects
644
+ case AST . CallExpression :
645
+ return false ;
646
+ }
647
+
648
+ return ( undefined === parentIsPure ) || parentIsPure ;
649
+ }
650
+
651
+ function findConstantAndWatchExpressions ( ast , $filter , parentIsPure ) {
626
652
var allConstants ;
627
653
var argsToWatch ;
628
654
var isStatelessFilter ;
655
+
656
+ var astIsPure = ast . isPure = isPure ( ast , parentIsPure ) ;
657
+
629
658
switch ( ast . type ) {
630
659
case AST . Program :
631
660
allConstants = true ;
632
661
forEach ( ast . body , function ( expr ) {
633
- findConstantAndWatchExpressions ( expr . expression , $filter ) ;
662
+ findConstantAndWatchExpressions ( expr . expression , $filter , astIsPure ) ;
634
663
allConstants = allConstants && expr . expression . constant ;
635
664
} ) ;
636
665
ast . constant = allConstants ;
@@ -640,26 +669,26 @@ function findConstantAndWatchExpressions(ast, $filter) {
640
669
ast . toWatch = [ ] ;
641
670
break ;
642
671
case AST . UnaryExpression :
643
- findConstantAndWatchExpressions ( ast . argument , $filter ) ;
672
+ findConstantAndWatchExpressions ( ast . argument , $filter , astIsPure ) ;
644
673
ast . constant = ast . argument . constant ;
645
674
ast . toWatch = ast . argument . toWatch ;
646
675
break ;
647
676
case AST . BinaryExpression :
648
- findConstantAndWatchExpressions ( ast . left , $filter ) ;
649
- findConstantAndWatchExpressions ( ast . right , $filter ) ;
677
+ findConstantAndWatchExpressions ( ast . left , $filter , astIsPure ) ;
678
+ findConstantAndWatchExpressions ( ast . right , $filter , astIsPure ) ;
650
679
ast . constant = ast . left . constant && ast . right . constant ;
651
680
ast . toWatch = ast . left . toWatch . concat ( ast . right . toWatch ) ;
652
681
break ;
653
682
case AST . LogicalExpression :
654
- findConstantAndWatchExpressions ( ast . left , $filter ) ;
655
- findConstantAndWatchExpressions ( ast . right , $filter ) ;
683
+ findConstantAndWatchExpressions ( ast . left , $filter , astIsPure ) ;
684
+ findConstantAndWatchExpressions ( ast . right , $filter , astIsPure ) ;
656
685
ast . constant = ast . left . constant && ast . right . constant ;
657
686
ast . toWatch = ast . constant ? [ ] : [ ast ] ;
658
687
break ;
659
688
case AST . ConditionalExpression :
660
- findConstantAndWatchExpressions ( ast . test , $filter ) ;
661
- findConstantAndWatchExpressions ( ast . alternate , $filter ) ;
662
- findConstantAndWatchExpressions ( ast . consequent , $filter ) ;
689
+ findConstantAndWatchExpressions ( ast . test , $filter , astIsPure ) ;
690
+ findConstantAndWatchExpressions ( ast . alternate , $filter , astIsPure ) ;
691
+ findConstantAndWatchExpressions ( ast . consequent , $filter , astIsPure ) ;
663
692
ast . constant = ast . test . constant && ast . alternate . constant && ast . consequent . constant ;
664
693
ast . toWatch = ast . constant ? [ ] : [ ast ] ;
665
694
break ;
@@ -668,9 +697,9 @@ function findConstantAndWatchExpressions(ast, $filter) {
668
697
ast . toWatch = [ ast ] ;
669
698
break ;
670
699
case AST . MemberExpression :
671
- findConstantAndWatchExpressions ( ast . object , $filter ) ;
700
+ findConstantAndWatchExpressions ( ast . object , $filter , astIsPure ) ;
672
701
if ( ast . computed ) {
673
- findConstantAndWatchExpressions ( ast . property , $filter ) ;
702
+ findConstantAndWatchExpressions ( ast . property , $filter , astIsPure ) ;
674
703
}
675
704
ast . constant = ast . object . constant && ( ! ast . computed || ast . property . constant ) ;
676
705
ast . toWatch = [ ast ] ;
@@ -680,7 +709,7 @@ function findConstantAndWatchExpressions(ast, $filter) {
680
709
allConstants = isStatelessFilter ;
681
710
argsToWatch = [ ] ;
682
711
forEach ( ast . arguments , function ( expr ) {
683
- findConstantAndWatchExpressions ( expr , $filter ) ;
712
+ findConstantAndWatchExpressions ( expr , $filter , astIsPure ) ;
684
713
allConstants = allConstants && expr . constant ;
685
714
if ( ! expr . constant ) {
686
715
argsToWatch . push . apply ( argsToWatch , expr . toWatch ) ;
@@ -690,16 +719,16 @@ function findConstantAndWatchExpressions(ast, $filter) {
690
719
ast . toWatch = isStatelessFilter ? argsToWatch : [ ast ] ;
691
720
break ;
692
721
case AST . AssignmentExpression :
693
- findConstantAndWatchExpressions ( ast . left , $filter ) ;
694
- findConstantAndWatchExpressions ( ast . right , $filter ) ;
722
+ findConstantAndWatchExpressions ( ast . left , $filter , astIsPure ) ;
723
+ findConstantAndWatchExpressions ( ast . right , $filter , astIsPure ) ;
695
724
ast . constant = ast . left . constant && ast . right . constant ;
696
725
ast . toWatch = [ ast ] ;
697
726
break ;
698
727
case AST . ArrayExpression :
699
728
allConstants = true ;
700
729
argsToWatch = [ ] ;
701
730
forEach ( ast . elements , function ( expr ) {
702
- findConstantAndWatchExpressions ( expr , $filter ) ;
731
+ findConstantAndWatchExpressions ( expr , $filter , astIsPure ) ;
703
732
allConstants = allConstants && expr . constant ;
704
733
if ( ! expr . constant ) {
705
734
argsToWatch . push . apply ( argsToWatch , expr . toWatch ) ;
@@ -712,13 +741,13 @@ function findConstantAndWatchExpressions(ast, $filter) {
712
741
allConstants = true ;
713
742
argsToWatch = [ ] ;
714
743
forEach ( ast . properties , function ( property ) {
715
- findConstantAndWatchExpressions ( property . value , $filter ) ;
744
+ findConstantAndWatchExpressions ( property . value , $filter , astIsPure ) ;
716
745
allConstants = allConstants && property . value . constant && ! property . computed ;
717
746
if ( ! property . value . constant ) {
718
747
argsToWatch . push . apply ( argsToWatch , property . value . toWatch ) ;
719
748
}
720
749
if ( property . computed ) {
721
- findConstantAndWatchExpressions ( property . key , $filter ) ;
750
+ findConstantAndWatchExpressions ( property . key , $filter , astIsPure ) ;
722
751
if ( ! property . key . constant ) {
723
752
argsToWatch . push . apply ( argsToWatch , property . key . toWatch ) ;
724
753
}
@@ -803,7 +832,7 @@ ASTCompiler.prototype = {
803
832
var intoId = self . nextId ( ) ;
804
833
self . recurse ( watch , intoId ) ;
805
834
self . return_ ( intoId ) ;
806
- self . state . inputs . push ( fnKey ) ;
835
+ self . state . inputs . push ( { name : fnKey , isPure : watch . isPure } ) ;
807
836
watch . watchId = key ;
808
837
} ) ;
809
838
this . state . computing = 'fn' ;
@@ -839,13 +868,16 @@ ASTCompiler.prototype = {
839
868
840
869
watchFns : function ( ) {
841
870
var result = [ ] ;
842
- var fns = this . state . inputs ;
871
+ var inputs = this . state . inputs ;
843
872
var self = this ;
844
- forEach ( fns , function ( name ) {
845
- result . push ( 'var ' + name + '=' + self . generateFunction ( name , 's' ) ) ;
873
+ forEach ( inputs , function ( input ) {
874
+ result . push ( 'var ' + input . name + '=' + self . generateFunction ( input . name , 's' ) ) ;
875
+ if ( input . isPure ) {
876
+ result . push ( input . name , '.isPure=true;' ) ;
877
+ }
846
878
} ) ;
847
- if ( fns . length ) {
848
- result . push ( 'fn.inputs=[' + fns . join ( ',' ) + '];' ) ;
879
+ if ( inputs . length ) {
880
+ result . push ( 'fn.inputs=[' + inputs . map ( function ( i ) { return i . name ; } ) . join ( ',' ) + '];' ) ;
849
881
}
850
882
return result . join ( '' ) ;
851
883
} ,
@@ -1251,6 +1283,7 @@ ASTInterpreter.prototype = {
1251
1283
inputs = [ ] ;
1252
1284
forEach ( toWatch , function ( watch , key ) {
1253
1285
var input = self . recurse ( watch ) ;
1286
+ input . isPure = watch . isPure ;
1254
1287
watch . input = input ;
1255
1288
inputs . push ( input ) ;
1256
1289
watch . watchId = key ;
@@ -1817,7 +1850,7 @@ function $ParseProvider() {
1817
1850
inputExpressions = inputExpressions [ 0 ] ;
1818
1851
return scope . $watch ( function expressionInputWatch ( scope ) {
1819
1852
var newInputValue = inputExpressions ( scope ) ;
1820
- if ( ! expressionInputDirtyCheck ( newInputValue , oldInputValueOf , parsedExpression . literal ) ) {
1853
+ if ( ! expressionInputDirtyCheck ( newInputValue , oldInputValueOf , inputExpressions . isPure ) ) {
1821
1854
lastResult = parsedExpression ( scope , undefined , undefined , [ newInputValue ] ) ;
1822
1855
oldInputValueOf = newInputValue && getValueOf ( newInputValue ) ;
1823
1856
}
@@ -1837,7 +1870,7 @@ function $ParseProvider() {
1837
1870
1838
1871
for ( var i = 0 , ii = inputExpressions . length ; i < ii ; i ++ ) {
1839
1872
var newInputValue = inputExpressions [ i ] ( scope ) ;
1840
- if ( changed || ( changed = ! expressionInputDirtyCheck ( newInputValue , oldInputValueOfValues [ i ] , parsedExpression . literal ) ) ) {
1873
+ if ( changed || ( changed = ! expressionInputDirtyCheck ( newInputValue , oldInputValueOfValues [ i ] , inputExpressions [ i ] . isPure ) ) ) {
1841
1874
oldInputValues [ i ] = newInputValue ;
1842
1875
oldInputValueOfValues [ i ] = newInputValue && getValueOf ( newInputValue ) ;
1843
1876
}
0 commit comments