@@ -376,6 +376,10 @@ Lexer.prototype = {
376376} ;
377377
378378
379+ function isConstant ( exp ) {
380+ return exp . constant ;
381+ }
382+
379383/**
380384 * @constructor
381385 */
@@ -493,7 +497,8 @@ Parser.prototype = {
493497 return extend ( function ( self , locals ) {
494498 return fn ( self , locals , right ) ;
495499 } , {
496- constant :right . constant
500+ constant :right . constant ,
501+ inputs : [ right ]
497502 } ) ;
498503 } ,
499504
@@ -505,11 +510,12 @@ Parser.prototype = {
505510 } ) ;
506511 } ,
507512
508- binaryFn : function ( left , fn , right ) {
513+ binaryFn : function ( left , fn , right , isBranching ) {
509514 return extend ( function ( self , locals ) {
510515 return fn ( self , locals , left , right ) ;
511516 } , {
512- constant :left . constant && right . constant
517+ constant : left . constant && right . constant ,
518+ inputs : ! isBranching && [ left , right ]
513519 } ) ;
514520 } ,
515521
@@ -557,7 +563,9 @@ Parser.prototype = {
557563 }
558564 }
559565
560- return function $parseFilter ( self , locals ) {
566+ var inputs = [ inputFn ] . concat ( argsFn || [ ] ) ;
567+
568+ return extend ( function $parseFilter ( self , locals ) {
561569 var input = inputFn ( self , locals ) ;
562570 if ( args ) {
563571 args [ 0 ] = input ;
@@ -571,7 +579,10 @@ Parser.prototype = {
571579 }
572580
573581 return fn ( input ) ;
574- } ;
582+ } , {
583+ constant : ! fn . $stateful && inputs . every ( isConstant ) ,
584+ inputs : ! fn . $stateful && inputs
585+ } ) ;
575586 } ,
576587
577588 expression : function ( ) {
@@ -588,9 +599,11 @@ Parser.prototype = {
588599 this . text . substring ( 0 , token . index ) + '] can not be assigned to' , token ) ;
589600 }
590601 right = this . ternary ( ) ;
591- return function $parseAssignment ( scope , locals ) {
602+ return extend ( function $parseAssignment ( scope , locals ) {
592603 return left . assign ( scope , right ( scope , locals ) , locals ) ;
593- } ;
604+ } , {
605+ inputs : [ left , right ]
606+ } ) ;
594607 }
595608 return left ;
596609 } ,
@@ -615,7 +628,7 @@ Parser.prototype = {
615628 var left = this . logicalAND ( ) ;
616629 var token ;
617630 while ( ( token = this . expect ( '||' ) ) ) {
618- left = this . binaryFn ( left , token . fn , this . logicalAND ( ) ) ;
631+ left = this . binaryFn ( left , token . fn , this . logicalAND ( ) , true ) ;
619632 }
620633 return left ;
621634 } ,
@@ -624,7 +637,7 @@ Parser.prototype = {
624637 var left = this . equality ( ) ;
625638 var token ;
626639 if ( ( token = this . expect ( '&&' ) ) ) {
627- left = this . binaryFn ( left , token . fn , this . logicalAND ( ) ) ;
640+ left = this . binaryFn ( left , token . fn , this . logicalAND ( ) , true ) ;
628641 }
629642 return left ;
630643 } ,
@@ -759,7 +772,6 @@ Parser.prototype = {
759772 // This is used with json array declaration
760773 arrayDeclaration : function ( ) {
761774 var elementFns = [ ] ;
762- var allConstant = true ;
763775 if ( this . peekToken ( ) . text !== ']' ) {
764776 do {
765777 if ( this . peek ( ']' ) ) {
@@ -768,9 +780,6 @@ Parser.prototype = {
768780 }
769781 var elementFn = this . expression ( ) ;
770782 elementFns . push ( elementFn ) ;
771- if ( ! elementFn . constant ) {
772- allConstant = false ;
773- }
774783 } while ( this . expect ( ',' ) ) ;
775784 }
776785 this . consume ( ']' ) ;
@@ -783,13 +792,13 @@ Parser.prototype = {
783792 return array ;
784793 } , {
785794 literal : true ,
786- constant : allConstant
795+ constant : elementFns . every ( isConstant ) ,
796+ inputs : elementFns
787797 } ) ;
788798 } ,
789799
790800 object : function ( ) {
791801 var keys = [ ] , valueFns = [ ] ;
792- var allConstant = true ;
793802 if ( this . peekToken ( ) . text !== '}' ) {
794803 do {
795804 if ( this . peek ( '}' ) ) {
@@ -801,9 +810,6 @@ Parser.prototype = {
801810 this . consume ( ':' ) ;
802811 var value = this . expression ( ) ;
803812 valueFns . push ( value ) ;
804- if ( ! value . constant ) {
805- allConstant = false ;
806- }
807813 } while ( this . expect ( ',' ) ) ;
808814 }
809815 this . consume ( '}' ) ;
@@ -816,7 +822,8 @@ Parser.prototype = {
816822 return object ;
817823 } , {
818824 literal : true ,
819- constant : allConstant
825+ constant : valueFns . every ( isConstant ) ,
826+ inputs : valueFns
820827 } ) ;
821828 }
822829} ;
@@ -1043,6 +1050,8 @@ function $ParseProvider() {
10431050 parsedExpression = wrapSharedExpression ( parsedExpression ) ;
10441051 parsedExpression . $$watchDelegate = parsedExpression . literal ?
10451052 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate ;
1053+ } else if ( parsedExpression . inputs ) {
1054+ parsedExpression . $$watchDelegate = inputsWatchDelegate ;
10461055 }
10471056
10481057 cache [ cacheKey ] = parsedExpression ;
@@ -1057,6 +1066,88 @@ function $ParseProvider() {
10571066 }
10581067 } ;
10591068
1069+ function collectExpressionInputs ( inputs , list ) {
1070+ for ( var i = 0 , ii = inputs . length ; i < ii ; i ++ ) {
1071+ var input = inputs [ i ] ;
1072+ if ( ! input . constant ) {
1073+ if ( input . inputs ) {
1074+ collectExpressionInputs ( input . inputs , list ) ;
1075+ } else if ( list . indexOf ( input ) === - 1 ) { // TODO(perf) can we do better?
1076+ list . push ( input ) ;
1077+ }
1078+ }
1079+ }
1080+
1081+ return list ;
1082+ }
1083+
1084+ function expressionInputDirtyCheck ( newValue , oldValueOfValue ) {
1085+
1086+ if ( newValue == null || oldValueOfValue == null ) { // null/undefined
1087+ return newValue === oldValueOfValue ;
1088+ }
1089+
1090+ if ( typeof newValue === 'object' ) {
1091+
1092+ // attempt to convert the value to a primitive type
1093+ // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
1094+ // be cheaply dirty-checked
1095+ newValue = newValue . valueOf ( ) ;
1096+
1097+ if ( typeof newValue === 'object' ) {
1098+ // objects/arrays are not supported - deep-watching them would be too expensive
1099+ return false ;
1100+ }
1101+
1102+ // fall-through to the primitive equality check
1103+ }
1104+
1105+ //Primitive or NaN
1106+ return newValue === oldValueOfValue || ( newValue !== newValue && oldValueOfValue !== oldValueOfValue ) ;
1107+ }
1108+
1109+ function inputsWatchDelegate ( scope , listener , objectEquality , parsedExpression ) {
1110+ var inputExpressions = parsedExpression . $$inputs ||
1111+ ( parsedExpression . $$inputs = collectExpressionInputs ( parsedExpression . inputs , [ ] ) ) ;
1112+
1113+ var lastResult ;
1114+
1115+ if ( inputExpressions . length === 1 ) {
1116+ var oldInputValue = expressionInputDirtyCheck ; // init to something unique so that equals check fails
1117+ inputExpressions = inputExpressions [ 0 ] ;
1118+ return scope . $watch ( function expressionInputWatch ( scope ) {
1119+ var newInputValue = inputExpressions ( scope ) ;
1120+ if ( ! expressionInputDirtyCheck ( newInputValue , oldInputValue ) ) {
1121+ lastResult = parsedExpression ( scope ) ;
1122+ oldInputValue = newInputValue && newInputValue . valueOf ( ) ;
1123+ }
1124+ return lastResult ;
1125+ } , listener , objectEquality ) ;
1126+ }
1127+
1128+ var oldInputValueOfValues = [ ] ;
1129+ for ( var i = 0 , ii = inputExpressions . length ; i < ii ; i ++ ) {
1130+ oldInputValueOfValues [ i ] = expressionInputDirtyCheck ; // init to something unique so that equals check fails
1131+ }
1132+
1133+ return scope . $watch ( function expressionInputsWatch ( scope ) {
1134+ var changed = false ;
1135+
1136+ for ( var i = 0 , ii = inputExpressions . length ; i < ii ; i ++ ) {
1137+ var newInputValue = inputExpressions [ i ] ( scope ) ;
1138+ if ( changed || ( changed = ! expressionInputDirtyCheck ( newInputValue , oldInputValueOfValues [ i ] ) ) ) {
1139+ oldInputValueOfValues [ i ] = newInputValue && newInputValue . valueOf ( ) ;
1140+ }
1141+ }
1142+
1143+ if ( changed ) {
1144+ lastResult = parsedExpression ( scope ) ;
1145+ }
1146+
1147+ return lastResult ;
1148+ } , listener , objectEquality ) ;
1149+ }
1150+
10601151 function oneTimeWatchDelegate ( scope , listener , objectEquality , parsedExpression ) {
10611152 var unwatch , lastValue ;
10621153 return unwatch = scope . $watch ( function oneTimeWatch ( scope ) {
@@ -1122,7 +1213,18 @@ function $ParseProvider() {
11221213 // initial value is defined (for bind-once)
11231214 return isDefined ( value ) ? result : value ;
11241215 } ;
1125- fn . $$watchDelegate = parsedExpression . $$watchDelegate ;
1216+
1217+ // Propagate $$watchDelegates other then inputsWatchDelegate
1218+ if ( parsedExpression . $$watchDelegate &&
1219+ parsedExpression . $$watchDelegate !== inputsWatchDelegate ) {
1220+ fn . $$watchDelegate = parsedExpression . $$watchDelegate ;
1221+ } else if ( ! interceptorFn . $stateful ) {
1222+ // If there is an interceptor, but no watchDelegate then treat the interceptor like
1223+ // we treat filters - it is assumed to be a pure function unless flagged with $stateful
1224+ fn . $$watchDelegate = inputsWatchDelegate ;
1225+ fn . inputs = [ parsedExpression ] ;
1226+ }
1227+
11261228 return fn ;
11271229 }
11281230 } ] ;
0 commit comments