@@ -622,15 +622,51 @@ 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 isShallowState ( stack ) {
627
+ for ( var i = 0 ; i < stack . length ; i ++ ) {
628
+ var node = stack [ i ] ;
629
+ switch ( node . type ) {
630
+ // Computed members might invoke a stateful toString()
631
+ case AST . MemberExpression :
632
+ if ( node . computed ) {
633
+ return false ;
634
+ }
635
+ break ;
636
+
637
+ // The + operator can invoke a stateful toString().
638
+ // Otherwise operators can not access any state within objects.
639
+ case AST . UnaryExpression :
640
+ case AST . BinaryExpression :
641
+ if ( node . operator === '+' ) {
642
+ return false ;
643
+ }
644
+ break ;
645
+
646
+ // Functions / filters probably read state from within objects
647
+ case AST . CallExpression :
648
+ return false ;
649
+ }
650
+ }
651
+
652
+ return true ;
653
+ }
654
+
655
+ function findConstantAndWatchExpressions ( ast , $filter , stack ) {
626
656
var allConstants ;
627
657
var argsToWatch ;
628
658
var isStatelessFilter ;
659
+
660
+ stack = stack || [ ] ;
661
+ stack . push ( ast ) ;
662
+
663
+ ast . isShallowState = isShallowState ( stack ) ;
664
+
629
665
switch ( ast . type ) {
630
666
case AST . Program :
631
667
allConstants = true ;
632
668
forEach ( ast . body , function ( expr ) {
633
- findConstantAndWatchExpressions ( expr . expression , $filter ) ;
669
+ findConstantAndWatchExpressions ( expr . expression , $filter , stack ) ;
634
670
allConstants = allConstants && expr . expression . constant ;
635
671
} ) ;
636
672
ast . constant = allConstants ;
@@ -640,26 +676,26 @@ function findConstantAndWatchExpressions(ast, $filter) {
640
676
ast . toWatch = [ ] ;
641
677
break ;
642
678
case AST . UnaryExpression :
643
- findConstantAndWatchExpressions ( ast . argument , $filter ) ;
679
+ findConstantAndWatchExpressions ( ast . argument , $filter , stack ) ;
644
680
ast . constant = ast . argument . constant ;
645
681
ast . toWatch = ast . argument . toWatch ;
646
682
break ;
647
683
case AST . BinaryExpression :
648
- findConstantAndWatchExpressions ( ast . left , $filter ) ;
649
- findConstantAndWatchExpressions ( ast . right , $filter ) ;
684
+ findConstantAndWatchExpressions ( ast . left , $filter , stack ) ;
685
+ findConstantAndWatchExpressions ( ast . right , $filter , stack ) ;
650
686
ast . constant = ast . left . constant && ast . right . constant ;
651
687
ast . toWatch = ast . left . toWatch . concat ( ast . right . toWatch ) ;
652
688
break ;
653
689
case AST . LogicalExpression :
654
- findConstantAndWatchExpressions ( ast . left , $filter ) ;
655
- findConstantAndWatchExpressions ( ast . right , $filter ) ;
690
+ findConstantAndWatchExpressions ( ast . left , $filter , stack ) ;
691
+ findConstantAndWatchExpressions ( ast . right , $filter , stack ) ;
656
692
ast . constant = ast . left . constant && ast . right . constant ;
657
693
ast . toWatch = ast . constant ? [ ] : [ ast ] ;
658
694
break ;
659
695
case AST . ConditionalExpression :
660
- findConstantAndWatchExpressions ( ast . test , $filter ) ;
661
- findConstantAndWatchExpressions ( ast . alternate , $filter ) ;
662
- findConstantAndWatchExpressions ( ast . consequent , $filter ) ;
696
+ findConstantAndWatchExpressions ( ast . test , $filter , stack ) ;
697
+ findConstantAndWatchExpressions ( ast . alternate , $filter , stack ) ;
698
+ findConstantAndWatchExpressions ( ast . consequent , $filter , stack ) ;
663
699
ast . constant = ast . test . constant && ast . alternate . constant && ast . consequent . constant ;
664
700
ast . toWatch = ast . constant ? [ ] : [ ast ] ;
665
701
break ;
@@ -668,9 +704,9 @@ function findConstantAndWatchExpressions(ast, $filter) {
668
704
ast . toWatch = [ ast ] ;
669
705
break ;
670
706
case AST . MemberExpression :
671
- findConstantAndWatchExpressions ( ast . object , $filter ) ;
707
+ findConstantAndWatchExpressions ( ast . object , $filter , stack ) ;
672
708
if ( ast . computed ) {
673
- findConstantAndWatchExpressions ( ast . property , $filter ) ;
709
+ findConstantAndWatchExpressions ( ast . property , $filter , stack ) ;
674
710
}
675
711
ast . constant = ast . object . constant && ( ! ast . computed || ast . property . constant ) ;
676
712
ast . toWatch = [ ast ] ;
@@ -680,7 +716,7 @@ function findConstantAndWatchExpressions(ast, $filter) {
680
716
allConstants = isStatelessFilter ;
681
717
argsToWatch = [ ] ;
682
718
forEach ( ast . arguments , function ( expr ) {
683
- findConstantAndWatchExpressions ( expr , $filter ) ;
719
+ findConstantAndWatchExpressions ( expr , $filter , stack ) ;
684
720
allConstants = allConstants && expr . constant ;
685
721
if ( ! expr . constant ) {
686
722
argsToWatch . push . apply ( argsToWatch , expr . toWatch ) ;
@@ -690,16 +726,16 @@ function findConstantAndWatchExpressions(ast, $filter) {
690
726
ast . toWatch = isStatelessFilter ? argsToWatch : [ ast ] ;
691
727
break ;
692
728
case AST . AssignmentExpression :
693
- findConstantAndWatchExpressions ( ast . left , $filter ) ;
694
- findConstantAndWatchExpressions ( ast . right , $filter ) ;
729
+ findConstantAndWatchExpressions ( ast . left , $filter , stack ) ;
730
+ findConstantAndWatchExpressions ( ast . right , $filter , stack ) ;
695
731
ast . constant = ast . left . constant && ast . right . constant ;
696
732
ast . toWatch = [ ast ] ;
697
733
break ;
698
734
case AST . ArrayExpression :
699
735
allConstants = true ;
700
736
argsToWatch = [ ] ;
701
737
forEach ( ast . elements , function ( expr ) {
702
- findConstantAndWatchExpressions ( expr , $filter ) ;
738
+ findConstantAndWatchExpressions ( expr , $filter , stack ) ;
703
739
allConstants = allConstants && expr . constant ;
704
740
if ( ! expr . constant ) {
705
741
argsToWatch . push . apply ( argsToWatch , expr . toWatch ) ;
@@ -712,13 +748,13 @@ function findConstantAndWatchExpressions(ast, $filter) {
712
748
allConstants = true ;
713
749
argsToWatch = [ ] ;
714
750
forEach ( ast . properties , function ( property ) {
715
- findConstantAndWatchExpressions ( property . value , $filter ) ;
751
+ findConstantAndWatchExpressions ( property . value , $filter , stack ) ;
716
752
allConstants = allConstants && property . value . constant && ! property . computed ;
717
753
if ( ! property . value . constant ) {
718
754
argsToWatch . push . apply ( argsToWatch , property . value . toWatch ) ;
719
755
}
720
756
if ( property . computed ) {
721
- findConstantAndWatchExpressions ( property . key , $filter ) ;
757
+ findConstantAndWatchExpressions ( property . key , $filter , stack ) ;
722
758
if ( ! property . key . constant ) {
723
759
argsToWatch . push . apply ( argsToWatch , property . key . toWatch ) ;
724
760
}
@@ -737,6 +773,8 @@ function findConstantAndWatchExpressions(ast, $filter) {
737
773
ast . toWatch = [ ] ;
738
774
break ;
739
775
}
776
+
777
+ stack . pop ( ) ;
740
778
}
741
779
742
780
function getInputs ( body ) {
@@ -781,7 +819,7 @@ ASTCompiler.prototype = {
781
819
filters : { } ,
782
820
fn : { vars : [ ] , body : [ ] , own : { } } ,
783
821
assign : { vars : [ ] , body : [ ] , own : { } } ,
784
- inputs : [ ]
822
+ inputs : { }
785
823
} ;
786
824
findConstantAndWatchExpressions ( ast , self . $filter ) ;
787
825
var extra = '' ;
@@ -803,7 +841,7 @@ ASTCompiler.prototype = {
803
841
var intoId = self . nextId ( ) ;
804
842
self . recurse ( watch , intoId ) ;
805
843
self . return_ ( intoId ) ;
806
- self . state . inputs . push ( fnKey ) ;
844
+ self . state . inputs [ fnKey ] = ! ! watch . isShallowState ;
807
845
watch . watchId = key ;
808
846
} ) ;
809
847
this . state . computing = 'fn' ;
@@ -840,12 +878,16 @@ ASTCompiler.prototype = {
840
878
watchFns : function ( ) {
841
879
var result = [ ] ;
842
880
var fns = this . state . inputs ;
881
+ var fnNames = Object . keys ( fns ) ;
843
882
var self = this ;
844
- forEach ( fns , function ( name ) {
883
+ forEach ( fnNames , function ( name ) {
845
884
result . push ( 'var ' + name + '=' + self . generateFunction ( name , 's' ) ) ;
885
+ if ( fns [ name ] ) {
886
+ result . push ( name , ".isShallowState=true;" ) ;
887
+ }
846
888
} ) ;
847
- if ( fns . length ) {
848
- result . push ( 'fn.inputs=[' + fns . join ( ',' ) + '];' ) ;
889
+ if ( fnNames . length ) {
890
+ result . push ( 'fn.inputs=[' + fnNames . join ( ',' ) + '];' ) ;
849
891
}
850
892
return result . join ( '' ) ;
851
893
} ,
@@ -1251,6 +1293,7 @@ ASTInterpreter.prototype = {
1251
1293
inputs = [ ] ;
1252
1294
forEach ( toWatch , function ( watch , key ) {
1253
1295
var input = self . recurse ( watch ) ;
1296
+ input . isShallowState = watch . isShallowState ;
1254
1297
watch . input = input ;
1255
1298
inputs . push ( input ) ;
1256
1299
watch . watchId = key ;
@@ -1817,7 +1860,7 @@ function $ParseProvider() {
1817
1860
inputExpressions = inputExpressions [ 0 ] ;
1818
1861
return scope . $watch ( function expressionInputWatch ( scope ) {
1819
1862
var newInputValue = inputExpressions ( scope ) ;
1820
- if ( ! expressionInputDirtyCheck ( newInputValue , oldInputValueOf , parsedExpression . literal ) ) {
1863
+ if ( ! expressionInputDirtyCheck ( newInputValue , oldInputValueOf , inputExpressions . isShallowState ) ) {
1821
1864
lastResult = parsedExpression ( scope , undefined , undefined , [ newInputValue ] ) ;
1822
1865
oldInputValueOf = newInputValue && getValueOf ( newInputValue ) ;
1823
1866
}
@@ -1837,7 +1880,7 @@ function $ParseProvider() {
1837
1880
1838
1881
for ( var i = 0 , ii = inputExpressions . length ; i < ii ; i ++ ) {
1839
1882
var newInputValue = inputExpressions [ i ] ( scope ) ;
1840
- if ( changed || ( changed = ! expressionInputDirtyCheck ( newInputValue , oldInputValueOfValues [ i ] , parsedExpression . literal ) ) ) {
1883
+ if ( changed || ( changed = ! expressionInputDirtyCheck ( newInputValue , oldInputValueOfValues [ i ] , inputExpressions [ i ] . isShallowState ) ) ) {
1841
1884
oldInputValues [ i ] = newInputValue ;
1842
1885
oldInputValueOfValues [ i ] = newInputValue && getValueOf ( newInputValue ) ;
1843
1886
}
0 commit comments