@@ -6,6 +6,8 @@ import javascript
66
77/**
88 * Provides classes implementing data-flow for Immutable.
9+ *
10+ * The implemention rely on the flowsteps implemented in `Collections.qll`.
911 */
1012private module Immutable {
1113 /**
@@ -32,27 +34,49 @@ private module Immutable {
3234 * An instance of any immutable collection.
3335 */
3436 API:: Node immutableCollection ( ) {
35- result = immutableImport ( ) .getMember ( [ "Map" , "fromJS" ] ) .getReturn ( )
37+ // keep this list in sync with the constructors defined in `storeStep`.
38+ result = immutableImport ( ) .getMember ( [ "Map" , "List" , "fromJS" ] ) .getReturn ( )
3639 or
37- result . getAnImmediateUse ( ) = step ( immutableCollection ( ) .getAUse ( ) )
40+ result = immutableCollection ( ) .getMember ( [ "set" , "map" , "filter" , "push" ] ) . getReturn ( )
3841 }
3942
4043 /**
4144 * Gets the immutable collection where `pred` has been stored using the name `prop`.
4245 */
4346 DataFlow:: SourceNode storeStep ( DataFlow:: Node pred , string prop ) {
47+ // Immutable.Map() and Immutable.fromJS().
4448 exists ( DataFlow:: CallNode call |
4549 call = immutableImport ( ) .getMember ( [ "Map" , "fromJS" ] ) .getACall ( )
4650 |
4751 pred = call .getOptionArgument ( 0 , prop ) and
4852 result = call
4953 )
5054 or
55+ // Immutable.List()
56+ exists ( DataFlow:: CallNode call , DataFlow:: ArrayCreationNode arr |
57+ call = immutableImport ( ) .getMember ( "List" ) .getACall ( )
58+ |
59+ arr = call .getArgument ( 0 ) .getALocalSource ( ) and
60+ exists ( int i |
61+ prop = DataFlow:: PseudoProperties:: arrayElement ( i ) and
62+ pred = arr .getElement ( i ) and
63+ result = call
64+ )
65+ )
66+ or
67+ // collection.set(key, value)
5168 exists ( DataFlow:: CallNode call | call = immutableCollection ( ) .getMember ( "set" ) .getACall ( ) |
5269 call .getArgument ( 0 ) .mayHaveStringValue ( prop ) and
5370 pred = call .getArgument ( 1 ) and
5471 result = call
5572 )
73+ or
74+ // list.push(x)
75+ exists ( DataFlow:: CallNode call | call = immutableCollection ( ) .getMember ( "push" ) .getACall ( ) |
76+ pred = call .getArgument ( 0 ) and
77+ result = call and
78+ prop = DataFlow:: PseudoProperties:: arrayElement ( )
79+ )
5680 }
5781
5882 /**
@@ -73,14 +97,18 @@ private module Immutable {
7397 * Gets an immutable collection that contains all the elements from `pred`.
7498 */
7599 DataFlow:: SourceNode step ( DataFlow:: Node pred ) {
76- // map.set() copies all existing values
77- exists ( DataFlow:: CallNode call | call = immutableCollection ( ) .getMember ( "set" ) .getACall ( ) |
100+ // map.set() / list.push() copies all existing values
101+ exists ( DataFlow:: CallNode call |
102+ call = immutableCollection ( ) .getMember ( [ "set" , "push" ] ) .getACall ( )
103+ |
78104 pred = call .getReceiver ( ) and
79105 result = call
80106 )
81107 or
82- // toJS() or any immutable collection converts it to a plain JavaScript object/array (and vice versa for `fromJS`).
83- exists ( DataFlow:: CallNode call | call = immutableCollection ( ) .getMember ( "toJS" ) .getACall ( ) |
108+ // toJS()/toList() on any immutable collection converts it to a plain JavaScript object/array (and vice versa for `fromJS`).
109+ exists ( DataFlow:: CallNode call |
110+ call = immutableCollection ( ) .getMember ( [ "toJS" , "toList" ] ) .getACall ( )
111+ |
84112 pred = call .getReceiver ( ) and
85113 result = call
86114 )
0 commit comments