@@ -597,111 +597,103 @@ function plusFn(l, r) {
597
597
return l + r ;
598
598
}
599
599
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 ;
645
637
}
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 ;
692
638
}
693
639
}
694
640
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 ;
700
673
}
701
674
702
675
function isAssignable ( ast ) {
703
676
return ast . type === AST . Identifier || ast . type === AST . MemberExpression ;
704
677
}
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
+
705
697
706
698
function ASTCompiler ( astBuilder , $filter ) {
707
699
this . astBuilder = astBuilder ;
@@ -720,13 +712,7 @@ ASTCompiler.prototype = {
720
712
assign : { vars : [ ] , body : [ ] , own : { } } ,
721
713
inputs : [ ]
722
714
} ;
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 ) {
730
716
var fnKey = 'fn' + key ;
731
717
self . state [ fnKey ] = { vars : [ ] , body : [ ] , own : { } } ;
732
718
self . state . computing = fnKey ;
@@ -737,7 +723,8 @@ ASTCompiler.prototype = {
737
723
watch . watchId = key ;
738
724
} ) ;
739
725
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 ) {
741
728
if ( lastExpression ) this . current ( ) . body . push ( lastExpression , ';' ) ;
742
729
this . recurse ( ast . body [ i ] . expression , undefined , undefined , function ( expr ) { lastExpression = expr ; } ) ;
743
730
}
@@ -747,7 +734,7 @@ ASTCompiler.prototype = {
747
734
this . state . computing = 'assign' ;
748
735
var result = this . nextId ( ) ;
749
736
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 ){' +
751
738
this . varsPrefix ( 'assign' ) +
752
739
this . body ( 'assign' ) +
753
740
'};' ;
@@ -763,14 +750,8 @@ ASTCompiler.prototype = {
763
750
'};' +
764
751
extra +
765
752
this . watchFns ( ) +
766
- 'fn.literal=literal;fn.constant=constant;' +
767
753
'return fn;' ;
768
754
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 ;
774
755
/* jshint -W054 */
775
756
var fn = ( new Function ( '$filter' ,
776
757
'ensureSafeMemberName' ,
@@ -780,8 +761,6 @@ ASTCompiler.prototype = {
780
761
'ifDefined' ,
781
762
'plus' ,
782
763
'text' ,
783
- 'literal' ,
784
- 'constant' ,
785
764
fnString ) ) (
786
765
this . $filter ,
787
766
ensureSafeMemberName ,
@@ -790,9 +769,9 @@ ASTCompiler.prototype = {
790
769
isPossiblyDangerousMemberName ,
791
770
ifDefined ,
792
771
plusFn ,
793
- expression ,
794
- isLiteral ,
795
- isConstant ) ;
772
+ expression ) ;
773
+ fn . constant = isConstant ( ast , this . $filter ) ;
774
+ fn . literal = isLiteral ( ast ) ;
796
775
/* jshint +W054 */
797
776
this . state = undefined ;
798
777
return fn ;
@@ -1219,9 +1198,6 @@ ASTInterpreter.prototype = {
1219
1198
var ast = this . astBuilder . ast ( expression ) ;
1220
1199
this . expression = expression ;
1221
1200
this . expensiveChecks = expensiveChecks ;
1222
- forEach ( ast . body , function ( expression ) {
1223
- findConstantAndWatchExpressions ( expression . expression , self . $filter ) ;
1224
- } ) ;
1225
1201
var expressions = [ ] ;
1226
1202
forEach ( ast . body , function ( expression ) {
1227
1203
expressions . push ( self . recurse ( expression . expression ) ) ;
@@ -1235,25 +1211,18 @@ ASTInterpreter.prototype = {
1235
1211
} ) ;
1236
1212
return lastValue ;
1237
1213
} ;
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
- }
1246
1214
if ( ast . body . length === 1 && isAssignable ( ast . body [ 0 ] . expression ) ) {
1247
1215
var assign = this . recurse ( { type : AST . AssignmentExpression , left : ast . body [ 0 ] . expression , right : { type : AST . NGValueParameter } , operator : '=' } ) ;
1248
1216
fn . assign = function ( scope , value , locals ) {
1249
1217
return assign ( scope , locals , value ) ;
1250
1218
} ;
1251
1219
}
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
+ }
1257
1226
return fn ;
1258
1227
} ,
1259
1228
0 commit comments