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