@@ -597,111 +597,103 @@ function plusFn(l, r) {
597597 return l + r ;
598598}
599599
600- function isStateless ( $filter , filterName ) {
601- var fn = $filter ( filterName ) ;
602- return ! fn . $stateful ;
603- }
604-
605- function findConstantAndWatchExpressions ( ast , $filter ) {
606- var allConstants ;
607- var argsToWatch ;
608- switch ( ast . type ) {
609- case AST . Literal :
610- ast . constant = true ;
611- ast . toWatch = [ ] ;
612- break ;
613- case AST . UnaryExpression :
614- findConstantAndWatchExpressions ( ast . argument , $filter ) ;
615- ast . constant = ast . argument . constant ;
616- ast . toWatch = ast . argument . toWatch ;
617- break ;
618- case AST . BinaryExpression :
619- findConstantAndWatchExpressions ( ast . left , $filter ) ;
620- findConstantAndWatchExpressions ( ast . right , $filter ) ;
621- ast . constant = ast . left . constant && ast . right . constant ;
622- ast . toWatch = ast . left . toWatch . concat ( ast . right . toWatch ) ;
623- break ;
624- case AST . LogicalExpression :
625- findConstantAndWatchExpressions ( ast . left , $filter ) ;
626- findConstantAndWatchExpressions ( ast . right , $filter ) ;
627- ast . constant = ast . left . constant && ast . right . constant ;
628- ast . toWatch = ast . constant ? [ ] : [ ast ] ;
629- break ;
630- case AST . ConditionalExpression :
631- findConstantAndWatchExpressions ( ast . test , $filter ) ;
632- findConstantAndWatchExpressions ( ast . alternate , $filter ) ;
633- findConstantAndWatchExpressions ( ast . consequent , $filter ) ;
634- ast . constant = ast . test . constant && ast . alternate . constant && ast . consequent . constant ;
635- ast . toWatch = ast . constant ? [ ] : [ ast ] ;
636- break ;
637- case AST . Identifier :
638- ast . constant = false ;
639- ast . toWatch = [ ast ] ;
640- break ;
641- case AST . MemberExpression :
642- findConstantAndWatchExpressions ( ast . object , $filter ) ;
643- if ( ast . computed ) {
644- findConstantAndWatchExpressions ( ast . property , $filter ) ;
600+ function traverse ( ast , visitor ) {
601+ if ( false !== visitor ( ast ) ) {
602+ switch ( ast . type ) {
603+ case AST . UnaryExpression :
604+ traverse ( ast . argument , visitor ) ;
605+ break ;
606+ case AST . ExpressionStatement :
607+ traverse ( ast . expression , visitor ) ;
608+ break ;
609+ case AST . BinaryExpression :
610+ case AST . LogicalExpression :
611+ case AST . AssignmentExpression :
612+ traverse ( ast . left , visitor ) ;
613+ traverse ( ast . right , visitor ) ;
614+ break ;
615+ case AST . ConditionalExpression :
616+ traverse ( ast . test , visitor ) ;
617+ traverse ( ast . alternate , visitor ) ;
618+ traverse ( ast . consequent , visitor ) ;
619+ break ;
620+ case AST . MemberExpression :
621+ traverse ( ast . object , visitor ) ;
622+ traverse ( ast . property , visitor ) ;
623+ break ;
624+ case AST . CallExpression :
625+ if ( ! ast . filter ) {
626+ traverse ( ast . callee , visitor ) ;
627+ }
628+ /* jshint -W086 */
629+ //fall through to the overly complicated child exp loop
630+ case AST . Program :
631+ case AST . ArrayExpression :
632+ case AST . ObjectExpression :
633+ for ( var a = ast . arguments || ast . body || ast . elements || ast . properties , i = 0 , ii = a . length ; i < ii ; i ++ ) {
634+ traverse ( a [ i ] . value || a [ i ] . expression || a [ i ] , visitor ) ;
635+ }
636+ break ;
645637 }
646- ast . constant = ast . object . constant && ( ! ast . computed || ast . property . constant ) ;
647- ast . toWatch = [ ast ] ;
648- break ;
649- case AST . CallExpression :
650- allConstants = ast . filter ? isStateless ( $filter , ast . callee . name ) : false ;
651- argsToWatch = [ ] ;
652- forEach ( ast . arguments , function ( expr ) {
653- findConstantAndWatchExpressions ( expr , $filter ) ;
654- allConstants = allConstants && expr . constant ;
655- argsToWatch . push . apply ( argsToWatch , expr . toWatch ) ;
656- } ) ;
657- ast . constant = allConstants ;
658- ast . toWatch = ast . filter && isStateless ( $filter , ast . callee . name ) ? argsToWatch : [ ast ] ;
659- break ;
660- case AST . AssignmentExpression :
661- findConstantAndWatchExpressions ( ast . left , $filter ) ;
662- findConstantAndWatchExpressions ( ast . right , $filter ) ;
663- ast . constant = ast . left . constant && ast . right . constant ;
664- ast . toWatch = ast . right . toWatch ;
665- break ;
666- case AST . ArrayExpression :
667- allConstants = true ;
668- argsToWatch = [ ] ;
669- forEach ( ast . elements , function ( expr ) {
670- findConstantAndWatchExpressions ( expr , $filter ) ;
671- allConstants = allConstants && expr . constant ;
672- argsToWatch . push . apply ( argsToWatch , expr . toWatch ) ;
673- } ) ;
674- ast . constant = allConstants ;
675- ast . toWatch = argsToWatch ;
676- break ;
677- case AST . ObjectExpression :
678- allConstants = true ;
679- argsToWatch = [ ] ;
680- forEach ( ast . properties , function ( property ) {
681- findConstantAndWatchExpressions ( property . value , $filter ) ;
682- allConstants = allConstants && property . value . constant ;
683- argsToWatch . push . apply ( argsToWatch , property . value . toWatch ) ;
684- } ) ;
685- ast . constant = allConstants ;
686- ast . toWatch = argsToWatch ;
687- break ;
688- case AST . ThisExpression :
689- ast . constant = false ;
690- ast . toWatch = [ ] ;
691- break ;
692638 }
693639}
694640
695- function useInputs ( body ) {
696- if ( ! body . length ) return false ;
697- var lastExpression = body [ body . length - 1 ] ;
698- if ( lastExpression . expression . toWatch . length !== 1 ) return true ;
699- return lastExpression . expression . toWatch [ 0 ] !== lastExpression . expression ;
641+ function astAny ( ast , check ) {
642+ var res = false ;
643+ traverse ( ast , function ( e ) {
644+ res = res || check ( e ) ;
645+ return ! res ;
646+ } ) ;
647+ return res ;
648+ }
649+ function isConstant ( ast , $filter ) {
650+ return ! astAny ( ast , function ( e ) { return isMutator ( e ) || isStateful ( e , $filter ) ; } ) ;
651+ }
652+ function hasMutator ( ast ) {
653+ return astAny ( ast , isMutator ) ;
654+ }
655+ function findInputs ( ast , $filter ) {
656+ //Recurse into the body if htere is only one for the check in the return condition
657+ if ( ast . body && ast . body . length === 1 ) {
658+ ast = ast . body [ 0 ] . expression ;
659+ }
660+
661+ var inputs = [ ] ;
662+ traverse ( ast , function ( e ) {
663+ if ( isStateful ( e , $filter ) || isBranching ( e ) && hasMutator ( e ) ) {
664+ if ( ! isConstant ( e , $filter ) ) {
665+ inputs . push ( e ) ;
666+ }
667+ return false ;
668+ }
669+ } ) ;
670+
671+ //Only return non-empty and non-equevelent inputs
672+ return inputs . length && ! ( inputs . length === 1 && inputs [ 0 ] === ast ) ? inputs : false ;
700673}
701674
702675function isAssignable ( ast ) {
703676 return ast . type === AST . Identifier || ast . type === AST . MemberExpression ;
704677}
678+ function isMutator ( ast ) {
679+ return ast . type === AST . AssignmentExpression ||
680+ ast . type === AST . CallExpression && ! ast . filter ;
681+ }
682+ function isBranching ( ast ) {
683+ return ast . type === AST . ConditionalExpression || ast . type === AST . LogicalExpression ;
684+ }
685+ function isStateful ( ast , $filter ) {
686+ return ast . type === AST . Identifier ||
687+ ast . type === AST . MemberExpression ||
688+ ast . type === AST . CallExpression && ( ! ast . filter || $filter ( ast . callee . name ) . $stateful ) ;
689+ }
690+ function isLiteral ( ast ) {
691+ if ( ast . body && ast . body . length === 1 ) {
692+ ast = ast . body [ 0 ] . expression ;
693+ }
694+ return ast . type === AST . Literal || ast . type === AST . ArrayExpression || ast . type === AST . ObjectExpression ;
695+ }
696+
705697
706698function ASTCompiler ( astBuilder , $filter ) {
707699 this . astBuilder = astBuilder ;
@@ -720,13 +712,7 @@ ASTCompiler.prototype = {
720712 assign : { vars : [ ] , body : [ ] , own : { } } ,
721713 inputs : [ ]
722714 } ;
723- var lastExpression ;
724- var i ;
725- for ( i = 0 ; i < ast . body . length ; ++ i ) {
726- findConstantAndWatchExpressions ( ast . body [ i ] . expression , this . $filter ) ;
727- }
728- var toWatch = useInputs ( ast . body ) ? ast . body [ ast . body . length - 1 ] . expression . toWatch : [ ] ;
729- forEach ( toWatch , function ( watch , key ) {
715+ forEach ( findInputs ( ast , this . $filter ) || [ ] , function ( watch , key ) {
730716 var fnKey = 'fn' + key ;
731717 self . state [ fnKey ] = { vars : [ ] , body : [ ] , own : { } } ;
732718 self . state . computing = fnKey ;
@@ -737,7 +723,8 @@ ASTCompiler.prototype = {
737723 watch . watchId = key ;
738724 } ) ;
739725 this . state . computing = 'fn' ;
740- for ( i = 0 ; i < ast . body . length ; ++ i ) {
726+ var lastExpression ;
727+ for ( var i = 0 ; i < ast . body . length ; ++ i ) {
741728 if ( lastExpression ) this . current ( ) . body . push ( lastExpression , ';' ) ;
742729 this . recurse ( ast . body [ i ] . expression , undefined , undefined , function ( expr ) { lastExpression = expr ; } ) ;
743730 }
@@ -747,7 +734,7 @@ ASTCompiler.prototype = {
747734 this . state . computing = 'assign' ;
748735 var result = this . nextId ( ) ;
749736 this . recurse ( { type : AST . AssignmentExpression , left : ast . body [ 0 ] . expression , right : { type : AST . NGValueParameter } , operator : '=' } , result ) ;
750- extra = 'fn.assign=function(s,v,l){' +
737+ extra = 'fn.assign=function(s,v,l,i ){' +
751738 this . varsPrefix ( 'assign' ) +
752739 this . body ( 'assign' ) +
753740 '};' ;
@@ -763,14 +750,8 @@ ASTCompiler.prototype = {
763750 '};' +
764751 extra +
765752 this . watchFns ( ) +
766- 'fn.literal=literal;fn.constant=constant;' +
767753 'return fn;' ;
768754
769- var isLiteral = ast . body . length === 1 && (
770- ast . body [ 0 ] . expression . type === AST . Literal ||
771- ast . body [ 0 ] . expression . type === AST . ArrayExpression ||
772- ast . body [ 0 ] . expression . type === AST . ObjectExpression ) ;
773- var isConstant = ast . body . length === 1 && ast . body [ 0 ] . expression . constant ;
774755 /* jshint -W054 */
775756 var fn = ( new Function ( '$filter' ,
776757 'ensureSafeMemberName' ,
@@ -780,8 +761,6 @@ ASTCompiler.prototype = {
780761 'ifDefined' ,
781762 'plus' ,
782763 'text' ,
783- 'literal' ,
784- 'constant' ,
785764 fnString ) ) (
786765 this . $filter ,
787766 ensureSafeMemberName ,
@@ -790,9 +769,9 @@ ASTCompiler.prototype = {
790769 isPossiblyDangerousMemberName ,
791770 ifDefined ,
792771 plusFn ,
793- expression ,
794- isLiteral ,
795- isConstant ) ;
772+ expression ) ;
773+ fn . constant = isConstant ( ast , this . $filter ) ;
774+ fn . literal = isLiteral ( ast ) ;
796775 /* jshint +W054 */
797776 this . state = undefined ;
798777 return fn ;
@@ -1219,9 +1198,6 @@ ASTInterpreter.prototype = {
12191198 var ast = this . astBuilder . ast ( expression ) ;
12201199 this . expression = expression ;
12211200 this . expensiveChecks = expensiveChecks ;
1222- forEach ( ast . body , function ( expression ) {
1223- findConstantAndWatchExpressions ( expression . expression , self . $filter ) ;
1224- } ) ;
12251201 var expressions = [ ] ;
12261202 forEach ( ast . body , function ( expression ) {
12271203 expressions . push ( self . recurse ( expression . expression ) ) ;
@@ -1235,25 +1211,18 @@ ASTInterpreter.prototype = {
12351211 } ) ;
12361212 return lastValue ;
12371213 } ;
1238- var toWatch = useInputs ( ast . body ) ? ast . body [ ast . body . length - 1 ] . expression . toWatch : [ ] ;
1239- if ( toWatch . length ) {
1240- var inputs = [ ] ;
1241- forEach ( toWatch , function ( watch , key ) {
1242- inputs . push ( self . recurse ( watch ) ) ;
1243- } ) ;
1244- fn . inputs = inputs ;
1245- }
12461214 if ( ast . body . length === 1 && isAssignable ( ast . body [ 0 ] . expression ) ) {
12471215 var assign = this . recurse ( { type : AST . AssignmentExpression , left : ast . body [ 0 ] . expression , right : { type : AST . NGValueParameter } , operator : '=' } ) ;
12481216 fn . assign = function ( scope , value , locals ) {
12491217 return assign ( scope , locals , value ) ;
12501218 } ;
12511219 }
1252- fn . literal = ast . body . length === 1 && (
1253- ast . body [ 0 ] . expression . type === AST . Literal ||
1254- ast . body [ 0 ] . expression . type === AST . ArrayExpression ||
1255- ast . body [ 0 ] . expression . type === AST . ObjectExpression ) ;
1256- fn . constant = ast . body . length === 1 && ast . body [ 0 ] . expression . constant ;
1220+ fn . literal = isLiteral ( ast ) ;
1221+ fn . constant = isConstant ( ast , this . $filter ) ;
1222+ fn . inputs = findInputs ( ast , this . $filter ) ;
1223+ if ( fn . inputs ) {
1224+ fn . inputs = fn . inputs . map ( function ( input ) { return self . recurse ( input ) ; } ) ;
1225+ }
12571226 return fn ;
12581227 } ,
12591228
0 commit comments