Merge pull request #3237 from asger-semmle/js/sparse-capture

JS: Add CapturedVariableNode to avoid N^2 edges
This commit is contained in:
Asger F
2020-04-15 10:42:48 +01:00
committed by GitHub
8 changed files with 145 additions and 93 deletions

View File

@@ -317,6 +317,24 @@ class LocalVariable extends Variable {
else result = d.getContainer()
)
}
/**
* Gets the location of a declaration of this variable.
*
* If the variable has one or more declarations, the location of the first declaration is used.
* If the variable has no declaration, the entry point of its declaring container is used.
*/
Location getLocation() {
result =
min(Location loc |
loc = getADeclaration().getLocation()
|
loc order by loc.getStartLine(), loc.getStartColumn()
)
or
not exists(getADeclaration()) and
result = getDeclaringContainer().getEntry().getLocation()
}
}
/** A local variable that is not captured. */

View File

@@ -1462,6 +1462,9 @@ class MidPathNode extends PathNode, MkMidNode {
or
// Skip the synthetic 'this' node, as a ThisExpr will be the next node anyway
nd = DataFlow::thisNode(_)
or
// Skip captured variable nodes as the successor will be a use of that variable anyway.
nd = DataFlow::capturedVariableNode(_)
}
}

View File

@@ -27,6 +27,7 @@ module DataFlow {
private newtype TNode =
TValueNode(AST::ValueNode nd) or
TSsaDefNode(SsaDefinition d) or
TCapturedVariableNode(LocalVariable v) { v.isCaptured() } or
TPropNode(@property p) or
TRestPatternNode(DestructuringPattern dp, Expr rest) { rest = dp.getRest() } or
TDestructuringPatternNode(DestructuringPattern dp) or
@@ -1221,6 +1222,32 @@ module DataFlow {
}
}
/**
* A data flow node representing a captured variable.
*/
private class CapturedVariableNode extends Node, TCapturedVariableNode {
LocalVariable variable;
CapturedVariableNode() { this = TCapturedVariableNode(variable) }
override BasicBlock getBasicBlock() { result = variable.getDeclaringContainer().getStartBB() }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
variable.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
override string toString() { result = variable.getName() }
}
/**
* INTERNAL. DO NOT USE.
*
* Gets a data flow node representing the given captured variable.
*/
Node capturedVariableNode(LocalVariable variable) { result = TCapturedVariableNode(variable) }
/**
* Gets the data flow node corresponding to `nd`.
*
@@ -1434,19 +1461,23 @@ module DataFlow {
or
immediateFlowStep(pred, succ)
or
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node.
exists(SsaDefinition predDef |
pred = TSsaDefNode(predDef) and
succ = TCapturedVariableNode(predDef.getSourceVariable())
|
predDef instanceof SsaExplicitDefinition or
predDef instanceof SsaImplicitInit
)
or
// From a captured variable node to its flow-sensitive capture nodes
exists(SsaVariableCapture ssaCapture |
pred = TCapturedVariableNode(ssaCapture.getSourceVariable()) and
succ = TSsaDefNode(ssaCapture)
)
or
// Flow through implicit SSA nodes
exists(SsaImplicitDefinition ssa | succ = TSsaDefNode(ssa) |
// from any explicit definition or implicit init of a captured variable into
// the capturing definition
exists(SsaSourceVariable v, SsaDefinition predDef |
v = ssa.(SsaVariableCapture).getSourceVariable() and
predDef.getSourceVariable() = v and
pred = TSsaDefNode(predDef)
|
predDef instanceof SsaExplicitDefinition or
predDef instanceof SsaImplicitInit
)
or
// from the inputs of phi and pi nodes into the node itself
pred = TSsaDefNode(ssa.(SsaPseudoDefinition).getAnInput().getDefinition())
)

View File

@@ -13,10 +13,11 @@
| sources.js:11:12:11:18 | key | sources.js:11:32:11:34 | key |
| sources.js:11:14:11:16 | key | sources.js:11:12:11:18 | key |
| tst2.ts:1:1:1:1 | A | tst2.ts:1:18:1:18 | A |
| tst2.ts:1:1:1:1 | A | tst2.ts:7:1:7:0 | A |
| tst2.ts:1:8:5:1 | A | tst2.ts:7:1:7:0 | A |
| tst2.ts:1:1:1:1 | A | tst2.ts:1:18:1:18 | A |
| tst2.ts:1:8:5:1 | A | tst2.ts:1:18:1:18 | A |
| tst2.ts:1:8:5:1 | A | tst2.ts:11:11:11:11 | A |
| tst2.ts:1:8:5:1 | namespa ... lysed\\n} | tst2.ts:1:8:5:1 | A |
| tst2.ts:1:18:1:18 | A | tst2.ts:7:1:7:0 | A |
| tst2.ts:2:14:2:19 | x | tst2.ts:4:3:4:3 | x |
| tst2.ts:2:18:2:19 | 42 | tst2.ts:2:14:2:19 | x |
| tst2.ts:7:1:7:0 | A | tst2.ts:8:3:8:3 | A |
@@ -26,19 +27,19 @@
| tst2.ts:11:11:11:13 | A.x | tst2.ts:11:11:11:23 | A.x as number |
| tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:37 | List<string> |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
| tst.js:1:1:1:1 | x | tst.js:28:2:28:1 | x |
| tst.js:1:1:1:1 | x | tst.js:32:1:32:0 | x |
| tst.js:1:1:1:1 | x | tst.js:3:5:3:5 | x |
| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs |
| tst.js:1:10:1:11 | fs | tst.js:7:1:7:2 | fs |
| tst.js:1:10:1:11 | fs | tst.js:22:24:22:25 | fs |
| tst.js:3:5:3:5 | x | tst.js:28:2:28:1 | x |
| tst.js:3:5:3:5 | x | tst.js:32:1:32:0 | x |
| tst.js:3:5:3:10 | x | tst.js:3:5:3:5 | x |
| tst.js:3:5:3:10 | x | tst.js:8:1:8:1 | x |
| tst.js:3:5:3:10 | x | tst.js:9:2:9:2 | x |
| tst.js:3:5:3:10 | x | tst.js:10:1:10:1 | x |
| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x |
| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x |
| tst.js:3:5:3:10 | x | tst.js:11:1:11:1 | x |
| tst.js:3:5:3:10 | x | tst.js:28:2:28:1 | x |
| tst.js:3:5:3:10 | x | tst.js:32:1:32:0 | x |
| tst.js:3:9:3:10 | 42 | tst.js:3:5:3:10 | x |
| tst.js:4:5:4:12 | y | tst.js:10:4:10:4 | y |
| tst.js:4:5:4:12 | y | tst.js:11:6:11:6 | y |
@@ -75,9 +76,8 @@
| tst.js:22:5:22:25 | readFileSync | tst.js:23:1:23:12 | readFileSync |
| tst.js:22:7:22:18 | readFileSync | tst.js:22:5:22:25 | readFileSync |
| tst.js:22:24:22:25 | fs | tst.js:22:5:22:20 | { readFileSync } |
| tst.js:25:1:25:3 | x | tst.js:3:5:3:5 | x |
| tst.js:25:1:25:3 | x | tst.js:26:1:26:1 | x |
| tst.js:25:1:25:3 | x | tst.js:28:2:28:1 | x |
| tst.js:25:1:25:3 | x | tst.js:32:1:32:0 | x |
| tst.js:25:1:25:3 | x | tst.js:57:7:57:7 | x |
| tst.js:25:1:25:3 | x | tst.js:58:11:58:11 | x |
| tst.js:25:1:25:3 | x | tst.js:105:1:105:1 | x |

View File

@@ -1,2 +1,2 @@
| tst.js:4:3:4:27 | (typeof ... nction' | x |
| tst.js:8:3:8:23 | (typeof ... === 'u' | x |
| tst.js:4:3:4:27 | (typeof ... nction' | tst.js:3:12:3:12 | x |
| tst.js:8:3:8:23 | (typeof ... === 'u' | tst.js:3:12:3:12 | x |

View File

@@ -1,14 +1,14 @@
| tst.js:2:7:2:13 | a = g() | a | tst.js:2:11:2:13 | g() |
| tst.js:4:7:4:24 | { propB: b } = g() | b | tst.js:4:9:4:16 | propB: b |
| tst.js:6:7:6:34 | { propC ... } = g() | c | tst.js:6:9:6:16 | propC: c |
| tst.js:6:7:6:34 | { propC ... } = g() | d | tst.js:6:19:6:26 | propD: d |
| tst.js:8:7:8:41 | { array ... } = g() | elm1 | tst.js:8:22:8:25 | elm1 |
| tst.js:8:7:8:41 | { array ... } = g() | elm2 | tst.js:8:28:8:31 | elm2 |
| tst.js:17:3:17:22 | ({ propB: b }) = g() | b | tst.js:17:6:17:13 | propB: b |
| tst.js:19:3:19:32 | ({ prop ... ) = g() | c | tst.js:19:6:19:13 | propC: c |
| tst.js:19:3:19:32 | ({ prop ... ) = g() | d | tst.js:19:16:19:23 | propD: d |
| tst.js:21:3:21:22 | [ elm1, elm2 ] = g() | elm1 | tst.js:21:5:21:8 | elm1 |
| tst.js:21:3:21:22 | [ elm1, elm2 ] = g() | elm2 | tst.js:21:11:21:14 | elm2 |
| tst.js:31:12:31:23 | [elm1, elm2] | elm1 | tst.js:31:13:31:16 | elm1 |
| tst.js:31:12:31:23 | [elm1, elm2] | elm2 | tst.js:31:19:31:22 | elm2 |
| tst.js:31:26:31:40 | { prop: value } | value | tst.js:31:28:31:38 | prop: value |
| tst.js:2:7:2:13 | a = g() | tst.js:2:7:2:7 | a | tst.js:2:11:2:13 | g() |
| tst.js:4:7:4:24 | { propB: b } = g() | tst.js:4:16:4:16 | b | tst.js:4:9:4:16 | propB: b |
| tst.js:6:7:6:34 | { propC ... } = g() | tst.js:6:16:6:16 | c | tst.js:6:9:6:16 | propC: c |
| tst.js:6:7:6:34 | { propC ... } = g() | tst.js:6:26:6:26 | d | tst.js:6:19:6:26 | propD: d |
| tst.js:8:7:8:41 | { array ... } = g() | tst.js:8:22:8:25 | elm1 | tst.js:8:22:8:25 | elm1 |
| tst.js:8:7:8:41 | { array ... } = g() | tst.js:8:28:8:31 | elm2 | tst.js:8:28:8:31 | elm2 |
| tst.js:17:3:17:22 | ({ propB: b }) = g() | tst.js:4:16:4:16 | b | tst.js:17:6:17:13 | propB: b |
| tst.js:19:3:19:32 | ({ prop ... ) = g() | tst.js:6:16:6:16 | c | tst.js:19:6:19:13 | propC: c |
| tst.js:19:3:19:32 | ({ prop ... ) = g() | tst.js:6:26:6:26 | d | tst.js:19:16:19:23 | propD: d |
| tst.js:21:3:21:22 | [ elm1, elm2 ] = g() | tst.js:8:22:8:25 | elm1 | tst.js:21:5:21:8 | elm1 |
| tst.js:21:3:21:22 | [ elm1, elm2 ] = g() | tst.js:8:28:8:31 | elm2 | tst.js:21:11:21:14 | elm2 |
| tst.js:31:12:31:23 | [elm1, elm2] | tst.js:31:13:31:16 | elm1 | tst.js:31:13:31:16 | elm1 |
| tst.js:31:12:31:23 | [elm1, elm2] | tst.js:31:19:31:22 | elm2 | tst.js:31:19:31:22 | elm2 |
| tst.js:31:26:31:40 | { prop: value } | tst.js:31:34:31:38 | value | tst.js:31:28:31:38 | prop: value |

View File

@@ -1,12 +1,12 @@
| cm1 |
| cm2 |
| cm3 |
| cm4 |
| cm5 |
| cm6 |
| cm7 |
| cm8 |
| dm1 |
| m |
| mcm1 |
| rm1 |
| module-refs.js:3:9:3:11 | dm1 |
| module-refs.js:6:9:6:11 | rm1 |
| module-refs.js:9:9:9:11 | cm1 |
| module-refs.js:10:9:10:11 | cm2 |
| module-refs.js:11:9:11:11 | cm3 |
| module-refs.js:12:9:12:11 | cm4 |
| module-refs.js:13:9:13:11 | cm5 |
| module-refs.js:14:9:14:11 | cm6 |
| module-refs.js:15:9:15:11 | cm7 |
| module-refs.js:16:9:16:11 | cm8 |
| module-refs.js:22:9:22:12 | mcm1 |
| tst.js:19:7:19:7 | m |

View File

@@ -1,46 +1,46 @@
| arguments | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} |
| arguments | assignments.js:3:1:6:1 | functio ... = 56;\\n} |
| arguments | assignments.js:4:10:4:24 | function h() {} |
| arguments | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| arguments | defaultargs.js:3:3:3:25 | functio ... = x) {} |
| arguments | defaultargs.js:4:3:4:51 | functio ... [0]) {} |
| arguments | for.js:1:2:5:1 | functio ... x;\\n} |
| arguments | let.js:14:1:21:1 | functio ... }\\n} |
| arguments | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} |
| arguments | typeoftype.ts:3:3:5:3 | functio ... e x\\n } |
| arguments | variables.js:8:1:12:1 | functio ... ar x;\\n} |
| arguments | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| arguments | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |
| f | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| g | assignments.js:3:1:6:1 | functio ... = 56;\\n} |
| g | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| g | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} |
| h | assignments.js:4:10:4:24 | function h() {} |
| h | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| o | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} |
| o | for.js:1:2:5:1 | functio ... x;\\n} |
| x | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} |
| x | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| x | defaultargs.js:3:3:3:25 | functio ... = x) {} |
| x | defaultargs.js:4:3:4:51 | functio ... [0]) {} |
| x | for.js:1:2:5:1 | functio ... x;\\n} |
| x | legacyletstmt.js:1:1:8:0 | <toplevel> |
| x | let.js:1:1:22:0 | <toplevel> |
| x | let.js:1:1:22:0 | <toplevel> |
| x | let.js:1:1:22:0 | <toplevel> |
| x | let.js:1:1:22:0 | <toplevel> |
| x | let.js:1:1:22:0 | <toplevel> |
| x | let.js:14:1:21:1 | functio ... }\\n} |
| x | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} |
| x | variables.js:8:1:12:1 | functio ... ar x;\\n} |
| x | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| y | defaultargs.js:3:3:3:25 | functio ... = x) {} |
| y | defaultargs.js:4:3:4:51 | functio ... [0]) {} |
| y | legacyletstmt.js:1:1:8:0 | <toplevel> |
| y | let.js:1:1:22:0 | <toplevel> |
| y | let.js:14:1:21:1 | functio ... }\\n} |
| y | typeoftype.ts:3:3:5:3 | functio ... e x\\n } |
| y | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| y | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |
| z | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| z | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |
| arrayPatternDefault.js:1:2:1:1 | arguments | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} |
| arrayPatternDefault.js:1:11:1:11 | o | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} |
| arrayPatternDefault.js:2:8:2:8 | x | arrayPatternDefault.js:1:2:4:1 | functio ... bal2;\\n} |
| assignments.js:3:1:3:0 | arguments | assignments.js:3:1:6:1 | functio ... = 56;\\n} |
| assignments.js:4:6:4:6 | g | assignments.js:3:1:6:1 | functio ... = 56;\\n} |
| assignments.js:4:10:4:9 | arguments | assignments.js:4:10:4:24 | function h() {} |
| assignments.js:4:19:4:19 | h | assignments.js:4:10:4:24 | function h() {} |
| defaultargs.js:2:7:2:7 | x | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| defaultargs.js:2:10:2:18 | arguments | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| defaultargs.js:3:3:3:2 | arguments | defaultargs.js:3:3:3:25 | functio ... = x) {} |
| defaultargs.js:3:12:3:12 | f | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| defaultargs.js:3:14:3:14 | x | defaultargs.js:3:3:3:25 | functio ... = x) {} |
| defaultargs.js:3:17:3:17 | y | defaultargs.js:3:3:3:25 | functio ... = x) {} |
| defaultargs.js:4:3:4:2 | arguments | defaultargs.js:4:3:4:51 | functio ... [0]) {} |
| defaultargs.js:4:12:4:12 | g | defaultargs.js:1:2:5:1 | functio ... ]) {}\\n} |
| defaultargs.js:4:14:4:14 | x | defaultargs.js:4:3:4:51 | functio ... [0]) {} |
| defaultargs.js:4:32:4:32 | y | defaultargs.js:4:3:4:51 | functio ... [0]) {} |
| for.js:1:2:1:1 | arguments | for.js:1:2:5:1 | functio ... x;\\n} |
| for.js:1:11:1:11 | o | for.js:1:2:5:1 | functio ... x;\\n} |
| for.js:2:7:2:7 | x | for.js:1:2:5:1 | functio ... x;\\n} |
| legacyletstmt.js:3:6:3:6 | x | legacyletstmt.js:1:1:8:0 | <toplevel> |
| legacyletstmt.js:3:14:3:14 | y | legacyletstmt.js:1:1:8:0 | <toplevel> |
| let.js:2:9:2:9 | x | let.js:1:1:22:0 | <toplevel> |
| let.js:4:13:4:13 | x | let.js:1:1:22:0 | <toplevel> |
| let.js:5:18:5:18 | x | let.js:1:1:22:0 | <toplevel> |
| let.js:5:26:5:26 | y | let.js:1:1:22:0 | <toplevel> |
| let.js:6:17:6:17 | x | let.js:1:1:22:0 | <toplevel> |
| let.js:9:18:9:18 | x | let.js:1:1:22:0 | <toplevel> |
| let.js:14:1:14:0 | arguments | let.js:14:1:21:1 | functio ... }\\n} |
| let.js:14:14:14:14 | x | let.js:14:1:21:1 | functio ... }\\n} |
| let.js:17:11:17:11 | y | let.js:14:1:21:1 | functio ... }\\n} |
| typeoftype.ts:1:1:1:0 | arguments | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} |
| typeoftype.ts:2:7:2:7 | x | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} |
| typeoftype.ts:3:3:3:2 | arguments | typeoftype.ts:3:3:5:3 | functio ... e x\\n } |
| typeoftype.ts:3:12:3:12 | g | typeoftype.ts:1:1:6:1 | functio ... x\\n }\\n} |
| typeoftype.ts:4:9:4:9 | y | typeoftype.ts:3:3:5:3 | functio ... e x\\n } |
| variables.js:8:1:8:0 | arguments | variables.js:8:1:12:1 | functio ... ar x;\\n} |
| variables.js:9:6:9:6 | x | variables.js:8:1:12:1 | functio ... ar x;\\n} |
| variables.js:13:1:13:0 | arguments | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| variables.js:13:12:13:12 | y | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| variables.js:13:15:13:15 | z | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| variables.js:15:6:15:6 | x | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| variables.js:16:2:16:1 | arguments | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |
| variables.js:16:11:16:11 | h | variables.js:13:1:23:1 | functio ... z;\\n\\t}\\n} |
| variables.js:16:13:16:13 | z | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |
| variables.js:18:11:18:11 | y | variables.js:16:2:22:2 | functio ... y+z;\\n\\t} |