@@ -125,216 +125,159 @@ class AggregateES2015PromiseDefinition extends PromiseCreationCall {
125125 */
126126private module PromiseFlow {
127127 /**
128- * A promise from which data-flow can flow into or out of.
129- *
130- * This promise can both be a promise created by e.g. `new Promise(..)` or `Promise.resolve(..)`,
131- * or the result from calling a method on a promise e.g. `promise.then(..)`.
132- *
133- * The 4 methods in this class describe that ordinary and exceptional flow can flow into and out of this promise.
128+ * Gets the pseudo-field used to describe resolved values in a promise.
134129 */
135- private abstract class PromiseNode extends DataFlow:: SourceNode {
136-
137- /**
138- * Get a DataFlow::Node for a value that this promise is resolved with.
139- * The value is sent either to a chained promise, or to an `await` expression.
140- *
141- * The value is e.g. an argument to `resolve(..)`, or a return value from a `.then(..)` handler.
142- */
143- DataFlow:: Node getASentResolveValue ( ) { none ( ) }
144-
145- /**
146- * Get the DataFlow::Node that receives the value that this promise has been resolved with.
147- *
148- * E.g. the `x` in `promise.then((x) => ..)`.
149- */
150- DataFlow:: Node getReceivedResolveValue ( ) { none ( ) }
151-
152- /**
153- * Get a DataFlow::Node for a value that this promise is rejected with.
154- * The value is sent either to a chained promise, or thrown by an `await` expression.
155- *
156- * The value is e.g. an argument to `reject(..)`, or an exception thrown by the promise executor.
157- */
158- DataFlow:: Node getASentRejectValue ( ) { none ( ) }
159-
160- /**
161- * Get the DataFlow::Node that receives the value that this promise has been rejected with.
162- *
163- * E.g. the `x` in `promise.catch((x) => ..)`.
164- */
165- DataFlow:: Node getReceivedRejectValue ( ) { none ( ) }
130+ string resolveField ( ) {
131+ result = "$PromiseResolveField$"
166132 }
167-
133+
134+ /**
135+ * Gets the pseudo-field used to describe rejected values in a promise.
136+ */
137+ string rejectField ( ) {
138+ result = "$PromiseRejectField$"
139+ }
140+
168141 /**
169- * A PromiseNode for a PromiseDefinition.
170- * E.g. `new Promise(..)`.
142+ * A flow step describing a promise definition.
143+ *
144+ * The resolved/rejected value is written to a pseudo-field on the promise.
171145 */
172- private class PromiseDefinitionNode extends PromiseNode {
146+ class PromiseDefitionStep extends DataFlow :: AdditionalFlowStep {
173147 PromiseDefinition promise ;
174-
175- PromiseDefinitionNode ( ) { this = promise }
176-
177- override DataFlow:: Node getASentResolveValue ( ) {
178- result = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 )
148+ PromiseDefitionStep ( ) {
149+ this = promise
179150 }
180151
181- override DataFlow:: Node getASentRejectValue ( ) {
182- result = promise .getRejectParameter ( ) .getACall ( ) .getArgument ( 0 )
152+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
153+ prop = resolveField ( ) and
154+ pred = promise .getResolveParameter ( ) .getACall ( ) .getArgument ( 0 ) and
155+ succ = this
183156 or
184- result = promise .getExecutor ( ) .getExceptionalReturn ( )
157+ prop = rejectField ( ) and
158+ (
159+ pred = promise .getRejectParameter ( ) .getACall ( ) .getArgument ( 0 ) or
160+ pred = promise .getExecutor ( ) .getExceptionalReturn ( )
161+ ) and
162+ succ = this
185163 }
186164 }
187165
188166 /**
189- * A PromiseNode for a call that creates a promise.
190- * E.g. `Promise.resolve(..)` or `Promise.all(..)`.
167+ * A flow step describing the a Promise.resolve (and similar) call.
191168 */
192- private class PromiseCreationNode extends PromiseNode {
169+ class CreationStep extends DataFlow :: AdditionalFlowStep {
193170 PromiseCreationCall promise ;
194-
195- PromiseCreationNode ( ) { this = promise }
196-
197- override DataFlow:: Node getASentResolveValue ( ) {
198- exists ( DataFlow:: Node value | value = promise .getValue ( ) |
199- not value instanceof PromiseNode and
200- result = value
201- or
202- result = value .( PromiseNode ) .getASentResolveValue ( )
203- )
171+ CreationStep ( ) {
172+ this = promise
204173 }
205174
206- override DataFlow:: Node getASentRejectValue ( ) {
207- result = promise .getValue ( ) .( PromiseNode ) .getASentRejectValue ( )
175+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
176+ prop = resolveField ( ) and
177+ pred = promise .getValue ( ) and
178+ succ = this
208179 }
209180 }
210181
211182 /**
212- * A node referring to a PromiseNode through type-tracking.
183+ * A load step loading the pseudo-field describing that the promise is either resolved or rejected.
184+ * A resolved value is forwarding as the resulting value of the `await` expression,
185+ * and a rejected value is thrown as a exception.
213186 */
214- private class TrackedPromiseNode extends PromiseNode {
215- PromiseNode base ;
216- TrackedPromiseNode ( ) {
217- this = trackPromise ( DataFlow :: TypeTracker :: end ( ) , base ) and
218- not this instanceof PromiseDefinitionNode and
219- not this instanceof PromiseCreationNode
187+ class AwaitStep extends DataFlow :: AdditionalFlowStep {
188+ DataFlow :: Node operand ;
189+ AwaitExpr await ;
190+ AwaitStep ( ) {
191+ this . getEnclosingExpr ( ) = await and
192+ operand . getEnclosingExpr ( ) = await . getOperand ( )
220193 }
221194
222- override DataFlow:: Node getASentResolveValue ( ) { result = base .getASentResolveValue ( ) }
223- override DataFlow:: Node getReceivedResolveValue ( ) { result = base .getReceivedResolveValue ( ) }
224- override DataFlow:: Node getASentRejectValue ( ) { result = base .getASentRejectValue ( ) }
225- override DataFlow:: Node getReceivedRejectValue ( ) { result = base .getReceivedRejectValue ( ) }
226- }
227-
228- private DataFlow:: SourceNode trackPromise ( DataFlow:: TypeTracker t , PromiseNode promise ) {
229- t .start ( ) and result = promise
230- or
231- exists ( DataFlow:: TypeTracker t2 | result = trackPromise ( t2 , promise ) .track ( t2 , t ) )
232- }
233-
234- /**
235- * A PromiseNode that is a method call on an existing PromiseNode.
236- * E.g. `promise.then(..)`.
237- */
238- private abstract class ChainedPromiseNode extends PromiseNode , DataFlow:: MethodCallNode {
239- PromiseNode base ;
240-
241- ChainedPromiseNode ( ) { this = base .getAMethodCall ( _) }
242-
243- PromiseNode getBase ( ) { result = base }
195+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
196+ prop = resolveField ( ) and
197+ succ = this and
198+ pred = operand .getALocalSource ( )
199+ or
200+ prop = rejectField ( ) and
201+ succ = await .getExceptionTarget ( ) and
202+ pred = operand .getALocalSource ( )
203+ }
244204 }
245205
246206 /**
247- * A PromiseNode for the `.then(..) ` method on an existing promise.
207+ * A flow step describing the data-flow related to the `.then` method of a promise.
248208 */
249- private class PromiseThenNode extends ChainedPromiseNode {
250- PromiseThenNode ( ) { this = base .getAMethodCall ( "then" ) }
251-
252- override DataFlow:: Node getASentResolveValue ( ) {
253- exists ( DataFlow:: Node ret | ret = this .getCallback ( 0 ) .getAReturn ( ) |
254- if ret instanceof PromiseNode
255- then result = ret .( PromiseNode ) .getReceivedResolveValue ( )
256- else result = ret
257- )
209+ class ThenStep extends DataFlow:: AdditionalFlowStep , DataFlow:: MethodCallNode {
210+ ThenStep ( ) {
211+ this .getMethodName ( ) = "then"
258212 }
259213
260- override DataFlow:: Node getASentRejectValue ( ) {
261- not exists ( this .getCallback ( 1 ) ) and result = base .getASentRejectValue ( )
214+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
215+ prop = resolveField ( ) and
216+ pred = getReceiver ( ) .getALocalSource ( ) and
217+ succ = getCallback ( 0 ) .getParameter ( 0 )
262218 or
263- result = this .getCallback ( [ 0 ..1 ] ) .getExceptionalReturn ( )
219+ prop = rejectField ( ) and
220+ pred = getReceiver ( ) .getALocalSource ( ) and
221+ succ = getCallback ( 1 ) .getParameter ( 0 )
264222 }
265-
266- override DataFlow:: Node getReceivedResolveValue ( ) { result = this .getCallback ( 0 ) .getParameter ( 0 ) }
267-
268- override DataFlow:: Node getReceivedRejectValue ( ) { result = this .getCallback ( 1 ) .getParameter ( 0 ) }
269- }
270-
271- /**
272- * A PromiseNode for the `.finally(..)` method on an existing promise.
273- */
274- private class PromiseFinallyNode extends ChainedPromiseNode {
275- PromiseFinallyNode ( ) { this = base .getAMethodCall ( "finally" ) }
276-
277- override DataFlow:: Node getASentResolveValue ( ) { result = base .getASentResolveValue ( ) }
278-
279- override DataFlow:: Node getASentRejectValue ( ) {
280- result = base .getASentRejectValue ( )
223+
224+ override predicate copyProperty ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
225+ not exists ( this .getArgument ( 1 ) ) and
226+ prop = rejectField ( ) and
227+ pred = getReceiver ( ) .getALocalSource ( ) and
228+ succ = this
229+ }
230+
231+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
232+ prop = resolveField ( ) and
233+ pred = getCallback ( [ 0 ..1 ] ) .getAReturn ( ) and
234+ succ = this
281235 or
282- result = this .getCallback ( 0 ) .getExceptionalReturn ( )
236+ prop = rejectField ( ) and
237+ pred = getCallback ( [ 0 ..1 ] ) .getExceptionalReturn ( ) and
238+ succ = this
283239 }
284240 }
285-
241+
286242 /**
287- * A PromiseNode for the `.catch(..) ` method on an existing promise.
243+ * A flow step describing the data-flow related to the `.catch` method of a promise.
288244 */
289- private class PromiseCatchNode extends ChainedPromiseNode {
290- PromiseCatchNode ( ) { this = base .getAMethodCall ( "catch" ) }
291-
292- override DataFlow:: Node getASentResolveValue ( ) {
293- exists ( DataFlow:: Node ret | ret = this .getCallback ( 0 ) .getAReturn ( ) |
294- if ret instanceof PromiseNode
295- then result = ret .( PromiseNode ) .getReceivedResolveValue ( )
296- else result = ret
297- )
298- or
299- result = base .getASentResolveValue ( )
245+ class CatchStep extends DataFlow:: AdditionalFlowStep , DataFlow:: MethodCallNode {
246+ CatchStep ( ) {
247+ this .getMethodName ( ) = "catch"
300248 }
301249
302- override DataFlow:: Node getASentRejectValue ( ) { result = this .getCallback ( 0 ) .getExceptionalReturn ( ) }
250+ override predicate load ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
251+ prop = rejectField ( ) and
252+ pred = getReceiver ( ) .getALocalSource ( ) and
253+ succ = getCallback ( 0 ) .getParameter ( 0 )
254+ }
303255
304- override DataFlow:: Node getReceivedResolveValue ( ) { none ( ) }
256+ override predicate copyProperty ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
257+ prop = resolveField ( ) and
258+ pred = getReceiver ( ) .getALocalSource ( ) and
259+ succ = this
260+ }
305261
306- override DataFlow:: Node getReceivedRejectValue ( ) { result = this .getCallback ( 0 ) .getParameter ( 0 ) }
262+ override predicate store ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
263+ prop = rejectField ( ) and
264+ pred = getCallback ( [ 0 ..1 ] ) .getExceptionalReturn ( ) and
265+ succ = this
266+ }
307267 }
308268
309-
310- private ChainedPromiseNode getAChainedPromise ( PromiseNode p ) { result .getBase ( ) = p }
311-
312269 /**
313- * A data flow edge from a promise resolve/reject to the corresponding handler (or `await` expression) .
270+ * A flow step describing the data-flow related to the `.finally` method of a promise .
314271 */
315- private class PromiseFlowStep extends DataFlow:: AdditionalFlowStep {
316- PromiseNode promise ;
317-
318- PromiseFlowStep ( ) { this = promise }
272+ class FinallyStep extends DataFlow:: AdditionalFlowStep , DataFlow :: MethodCallNode {
273+ FinallyStep ( ) {
274+ this . getMethodName ( ) = "finally"
275+ }
319276
320- override predicate step ( DataFlow:: Node pred , DataFlow:: Node succ ) {
321- pred = promise .getASentResolveValue ( ) and
322- succ = getAChainedPromise ( promise ) .getReceivedResolveValue ( )
323- or
324- pred = promise .getASentRejectValue ( ) and
325- succ = getAChainedPromise ( promise ) .getReceivedRejectValue ( )
326- or
327- pred = promise .getASentResolveValue ( ) and
328- exists ( DataFlow:: SourceNode awaitNode |
329- awaitNode .asExpr ( ) .( AwaitExpr ) .getOperand ( ) = promise .asExpr ( ) and
330- succ = awaitNode
331- )
332- or
333- pred = promise .getASentRejectValue ( ) and
334- exists ( DataFlow:: SourceNode awaitNode |
335- awaitNode .asExpr ( ) .( AwaitExpr ) .getOperand ( ) = promise .asExpr ( ) and
336- succ = awaitNode .asExpr ( ) .getExceptionTarget ( )
337- )
277+ override predicate copyProperty ( DataFlow:: Node pred , DataFlow:: Node succ , string prop ) {
278+ ( prop = resolveField ( ) or prop = rejectField ( ) ) and
279+ pred = getReceiver ( ) .getALocalSource ( ) and
280+ succ = this
338281 }
339282 }
340283}
0 commit comments