@@ -113,6 +113,42 @@ class DynamicArgumentArrayNode extends DataFlow::Node, TDynamicArgumentArrayNode
113113 override Location getLocation ( ) { result = invoke .getLocation ( ) }
114114}
115115
116+ /**
117+ * Intermediate node with data that will be stored in `DyanmicArgumentArrayNode`.
118+ */
119+ class DynamicArgumentStoreNode extends DataFlow:: Node , TDynamicArgumentStoreNode {
120+ private InvokeExpr invoke ;
121+ private Content content ;
122+
123+ DynamicArgumentStoreNode ( ) { this = TDynamicArgumentStoreNode ( invoke , content ) }
124+
125+ override StmtContainer getContainer ( ) { result = invoke .getContainer ( ) }
126+
127+ override string toString ( ) { result = "[dynamic argument store node] content=" + content }
128+
129+ override Location getLocation ( ) { result = invoke .getLocation ( ) }
130+ }
131+
132+ /**
133+ * Intermediate node with data that will be stored in the function's rest parameter node.
134+ */
135+ class RestParameterStoreNode extends DataFlow:: Node , TRestParameterStoreNode {
136+ private Function function ;
137+ private Content content ;
138+
139+ RestParameterStoreNode ( ) { this = TRestParameterStoreNode ( function , content ) }
140+
141+ override StmtContainer getContainer ( ) { result = function }
142+
143+ override string toString ( ) {
144+ result =
145+ "[rest parameter store node] '..." + function .getRestParameter ( ) .getName ( ) + "' content=" +
146+ content
147+ }
148+
149+ override Location getLocation ( ) { result = function .getRestParameter ( ) .getLocation ( ) }
150+ }
151+
116152/**
117153 * A parameter containing an array of all positional arguments with an obvious index, i.e. not affected by spread or `.apply()`.
118154 *
@@ -297,8 +333,6 @@ private predicate isParameterNodeImpl(Node p, DataFlowCallable c, ParameterPosit
297333 or
298334 pos .isFunctionSelfReference ( ) and p = TFunctionSelfReferenceNode ( c .asSourceCallable ( ) )
299335 or
300- pos .isArgumentsArray ( ) and p = TReflectiveParametersNode ( c .asSourceCallable ( ) ) // TODO: remove
301- or
302336 pos .isStaticArgumentArray ( ) and p = TStaticParameterArrayNode ( c .asSourceCallable ( ) )
303337 or
304338 pos .isDynamicArgumentArray ( ) and p = TDynamicParameterArrayNode ( c .asSourceCallable ( ) )
@@ -317,6 +351,12 @@ predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition
317351private predicate isArgumentNodeImpl ( Node n , DataFlowCall call , ArgumentPosition pos ) {
318352 n = call .asOrdinaryCall ( ) .getArgument ( pos .asPositional ( ) )
319353 or
354+ exists ( InvokeExpr invoke |
355+ call .asOrdinaryCall ( ) = TReflectiveCallNode ( invoke , "apply" ) and
356+ pos .isDynamicArgumentArray ( ) and
357+ n = TValueNode ( invoke .getArgument ( 1 ) )
358+ )
359+ or
320360 pos .isThis ( ) and n = call .asOrdinaryCall ( ) .( DataFlow:: CallNode ) .getReceiver ( )
321361 or
322362 exists ( DataFlow:: PartialInvokeNode invoke , DataFlow:: Node callback |
@@ -343,10 +383,6 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
343383 or
344384 pos .isThis ( ) and n = TConstructorThisArgumentNode ( call .asOrdinaryCall ( ) .asExpr ( ) )
345385 or
346- // For now, treat all spread argument as flowing into the 'arguments' array, regardless of preceding arguments
347- n = call .asOrdinaryCall ( ) .getASpreadArgument ( ) and
348- pos .isArgumentsArray ( )
349- or
350386 // receiver of accessor call
351387 pos .isThis ( ) and n = call .asAccessorCall ( ) .getBase ( )
352388 or
@@ -953,6 +989,39 @@ predicate simpleLocalFlowStep(Node node1, Node node2) {
953989 or
954990 // NOTE: For consistency with readStep/storeStep, we do not translate these steps to jump steps automatically.
955991 DataFlow:: AdditionalFlowStep:: step ( node1 , node2 )
992+ or
993+ exists ( InvokeExpr invoke |
994+ // When the first argument is a spread argument, flow into the argument array as a local flow step
995+ // to ensure we preserve knowledge about array indices
996+ node1 = TValueNode ( invoke .getArgument ( 0 ) .stripParens ( ) .( SpreadElement ) .getOperand ( ) ) and
997+ node2 = TDynamicArgumentArrayNode ( invoke )
998+ )
999+ or
1000+ exists ( Function f |
1001+ // When the first parameter is a rest parameter, flow into the rest parameter as a local flow step
1002+ // to ensure we preserve knowledge about array indices
1003+ ( node1 = TStaticParameterArrayNode ( f ) or node1 = TDynamicParameterArrayNode ( f ) )
1004+ |
1005+ // rest parameter at initial position
1006+ exists ( Parameter rest |
1007+ rest = f .getParameter ( 0 ) and
1008+ rest .isRestParameter ( ) and
1009+ node2 = TValueNode ( rest )
1010+ )
1011+ or
1012+ // 'arguments' array
1013+ node2 = TReflectiveParametersNode ( f )
1014+ )
1015+ or
1016+ // Prepare to store non-spread arguments after a spread into the dynamic arguments array
1017+ exists ( InvokeExpr invoke , int n , Expr argument , Content storeContent |
1018+ invoke .getArgument ( n ) = argument and
1019+ not argument instanceof SpreadElement and
1020+ n > firstSpreadArgumentIndex ( invoke ) and
1021+ node1 = TValueNode ( argument ) and
1022+ node2 = TDynamicArgumentStoreNode ( invoke , storeContent ) and
1023+ storeContent .isUnknownArrayElement ( )
1024+ )
9561025}
9571026
9581027predicate localMustFlowStep ( Node node1 , Node node2 ) { node1 = node2 .getImmediatePredecessor ( ) }
@@ -1018,6 +1087,42 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
10181087 )
10191088 or
10201089 DataFlow:: AdditionalFlowStep:: readStep ( node1 , c , node2 )
1090+ or
1091+ // Pass dynamic arguments into plain parameters
1092+ exists ( Function function , Parameter param , int n |
1093+ param = function .getParameter ( n ) and
1094+ not param .isRestParameter ( ) and
1095+ node1 = TDynamicParameterArrayNode ( function ) and
1096+ node2 = TValueNode ( param ) and
1097+ c = ContentSet:: arrayElementFromInt ( n )
1098+ )
1099+ or
1100+ // Prepare to store dynamic and static arguments into the rest parameter array when it isn't the first parameter
1101+ exists ( Function function , Content content , int restIndex |
1102+ restIndex = function .getRestParameter ( ) .getIndex ( ) and
1103+ restIndex > 0 and
1104+ ( node1 = TStaticParameterArrayNode ( function ) or node1 = TDynamicParameterArrayNode ( function ) ) and
1105+ node2 = TRestParameterStoreNode ( function , content )
1106+ |
1107+ // shift known array indices
1108+ c .asArrayIndex ( ) = content .asArrayIndex ( ) + restIndex
1109+ or
1110+ content .isUnknownArrayElement ( ) and // TODO: don't read unknown array elements from static array
1111+ c = ContentSet:: arrayElementUnknown ( )
1112+ )
1113+ or
1114+ // Prepare to store spread arguments into the dynamic arguments array, when it isn't the initial spread argument
1115+ exists ( InvokeExpr invoke , int n , Expr argument , Content storeContent |
1116+ invoke .getArgument ( n ) .stripParens ( ) .( SpreadElement ) .getOperand ( ) = argument and
1117+ n > 0 and // n=0 is handled as a value step
1118+ node1 = TValueNode ( argument ) and
1119+ node2 = TDynamicArgumentStoreNode ( invoke , storeContent ) and
1120+ if n > firstSpreadArgumentIndex ( invoke )
1121+ then
1122+ c = ContentSet:: arrayElement ( ) and // unknown start index when not the first spread operator
1123+ storeContent .isUnknownArrayElement ( )
1124+ else storeContent .asArrayIndex ( ) = n + c .asArrayIndex ( )
1125+ )
10211126}
10221127
10231128/** Gets the post-update node for which `node` is the corresponding pre-update node. */
@@ -1032,6 +1137,11 @@ private Node tryGetPostUpdate(Node node) {
10321137 result = node
10331138}
10341139
1140+ pragma [ nomagic]
1141+ private int firstSpreadArgumentIndex ( InvokeExpr expr ) {
1142+ result = min ( int i | expr .isSpreadArgument ( i ) )
1143+ }
1144+
10351145/**
10361146 * Holds if data can flow from `node1` to `node2` via a store into `c`. Thus,
10371147 * `node2` references an object with a content `c.getAStoreContent()` that
@@ -1063,6 +1173,25 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
10631173 )
10641174 or
10651175 DataFlow:: AdditionalFlowStep:: storeStep ( node1 , c , node2 )
1176+ or
1177+ exists ( Function f , Content storeContent |
1178+ node1 = TRestParameterStoreNode ( f , storeContent ) and
1179+ node2 = TValueNode ( f .getRestParameter ( ) ) and
1180+ c .asSingleton ( ) = storeContent
1181+ )
1182+ or
1183+ exists ( InvokeExpr invoke , Content storeContent |
1184+ node1 = TDynamicArgumentStoreNode ( invoke , storeContent ) and
1185+ node2 = TDynamicArgumentArrayNode ( invoke ) and
1186+ c .asSingleton ( ) = storeContent
1187+ )
1188+ or
1189+ exists ( InvokeExpr invoke , int n |
1190+ node1 = TValueNode ( invoke .getArgument ( n ) ) and
1191+ node2 = TStaticArgumentArrayNode ( invoke ) and
1192+ c .asArrayIndex ( ) = n and
1193+ not n >= firstSpreadArgumentIndex ( invoke )
1194+ )
10661195}
10671196
10681197/**
@@ -1112,6 +1241,9 @@ predicate expectsContent(Node n, ContentSet c) {
11121241 c = MkPromiseFilter ( )
11131242 or
11141243 any ( AdditionalFlowInternal flow ) .expectsContent ( n , c )
1244+ or
1245+ c = ContentSet:: arrayElement ( ) and
1246+ n instanceof TDynamicParameterArrayNode
11151247}
11161248
11171249abstract class NodeRegion extends Unit {
0 commit comments