mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
250 lines
8.8 KiB
Plaintext
250 lines
8.8 KiB
Plaintext
/** Provides classes and predicates for working with variable definitions and uses. */
|
|
overlay[local?]
|
|
module;
|
|
|
|
import javascript
|
|
|
|
/**
|
|
* Holds if `def` is a CFG node that assigns the value of `rhs` to `lhs`.
|
|
*
|
|
* This predicate covers four kinds of definitions:
|
|
*
|
|
* <table border="1">
|
|
* <tr><th>Example</th><th><code>def</code></th><th><code>lhs</code></th><th><code>rhs</code></th></tr>
|
|
* <tr><td><code>x = y</code><td><code>x = y</code><td><code>x</code><td><code>y</code></tr>
|
|
* <tr><td><code>var a = b</code><td><code>var a = b</code><td><code>a</code><td><code>b</code></tr>
|
|
* <tr><td><code>function f { ... }</code><td><code>f</code><td><code>f</code><td><code>function f { ... }</code></tr>
|
|
* <tr><td><code>function f ( x = y ){ ... }</code><td><code>x</code><td><code>x</code><td><code>y</code></tr>
|
|
* <tr><td><code>class C { ... }</code><td><code>C</code><td><code>C</code><td><code>class C { ... }</code></tr>
|
|
* <tr><td><code>namespace N { ... }</code><td><code>N</code><td><code>N</code><td><code>namespace N { ... }</code></tr>
|
|
* <tr><td><code>enum E { ... }</code><td><code>E</code><td><code>E</code><td><code>enum E { ... }</code></tr>
|
|
* <tr><td><code>import x = y</code><td><code>x</code><td><code>x</code><td><code>y</code></tr>
|
|
* <tr><td><code>enum { x = y }</code><td><code>x</code><td><code>x</code><td><code>y</code></tr>
|
|
* </table>
|
|
*
|
|
* Note that `def` and `lhs` are not in general the same: the latter
|
|
* represents the point where `lhs` is evaluated to an assignable reference,
|
|
* the former the point where the value of `rhs` is actually assigned
|
|
* to that reference.
|
|
*/
|
|
private predicate defn(ControlFlowNode def, Expr lhs, AST::ValueNode rhs) {
|
|
exists(AssignExpr assgn | def = assgn | lhs = assgn.getTarget() and rhs = assgn.getRhs())
|
|
or
|
|
exists(VariableDeclarator vd | def = vd | lhs = vd.getBindingPattern() and rhs = vd.getInit())
|
|
or
|
|
exists(Function f | def = f.getIdentifier() | lhs = def and rhs = f)
|
|
or
|
|
exists(ClassDefinition c | lhs = c.getIdentifier() | def = c and rhs = c and not c.isAmbient())
|
|
or
|
|
exists(NamespaceDeclaration n | def = n | lhs = n.getIdentifier() and rhs = n)
|
|
or
|
|
exists(EnumDeclaration ed | def = ed.getIdentifier() | lhs = def and rhs = ed)
|
|
or
|
|
exists(ImportEqualsDeclaration i | def = i |
|
|
lhs = i.getIdentifier() and rhs = i.getImportedEntity()
|
|
)
|
|
or
|
|
exists(ImportSpecifier i | def = i and not i.isTypeOnly() | lhs = i.getLocal() and rhs = i)
|
|
or
|
|
exists(EnumMember member | def = member.getIdentifier() |
|
|
lhs = def and rhs = member.getInitializer()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `def` is a CFG node that assigns to `lhs`.
|
|
*
|
|
* This predicate extends the three-argument version of `defn` to also cover definitions
|
|
* where there is no explicit right hand side:
|
|
*
|
|
* <table border="1">
|
|
* <tr><th>Example</th><th><code>def</code></th><th><code>lhs</code></th></tr>
|
|
* <tr><td><code>x += y</code><td><code>x += y</code><td><code>x</code></tr>
|
|
* <tr><td><code>++z.q</code><td><code>++z.q</code><td><code>z.q</code></tr>
|
|
* <tr><td><code>import { a as b } from 'm'</code><td><code>a as b</code><td><code>b</code></tr>
|
|
* <tr><td><code>for (var p in o) ...</code><td><code>var p</code><td><code>p</code></tr>
|
|
* <tr><td><code>enum { x }</code><td><code>x</code><td><code>x</code></tr>
|
|
* </table>
|
|
*
|
|
* Additionally, parameters are also considered definitions, which are their own `lhs`.
|
|
*/
|
|
private predicate defn(ControlFlowNode def, Expr lhs) {
|
|
defn(def, lhs, _)
|
|
or
|
|
lhs = def.(CompoundAssignExpr).getTarget()
|
|
or
|
|
lhs = def.(UpdateExpr).getOperand().getUnderlyingReference()
|
|
or
|
|
exists(EnhancedForLoop efl | def = efl.getIteratorExpr() |
|
|
lhs = def.(Expr).stripParens() or
|
|
lhs = def.(VariableDeclarator).getBindingPattern()
|
|
)
|
|
or
|
|
lhs = def and
|
|
(
|
|
def instanceof Parameter or
|
|
def = any(ComprehensionBlock cb).getIterator()
|
|
)
|
|
or
|
|
exists(EnumMember member | def = member.getIdentifier() |
|
|
lhs = def and not exists(member.getInitializer())
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `l` is one of the lvalues in the assignment `def`, or
|
|
* a destructuring pattern that contains some of the lvalues.
|
|
*
|
|
* For example, if `def` is `[{ x: y }] = e`, then `l` can be any
|
|
* of `y`, `{ x: y }` and `[{ x: y }]`.
|
|
*/
|
|
private predicate lvalAux(Expr l, ControlFlowNode def) {
|
|
defn(def, l)
|
|
or
|
|
exists(ArrayPattern ap | lvalAux(ap, def) | l = ap.getAnElement().stripParens())
|
|
or
|
|
exists(ObjectPattern op | lvalAux(op, def) |
|
|
l = op.getAPropertyPattern().getValuePattern().stripParens() or
|
|
l = op.getRest().stripParens()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* An expression that can be evaluated to a reference, that is,
|
|
* a variable reference or a property access.
|
|
*/
|
|
class RefExpr extends Expr {
|
|
RefExpr() {
|
|
this instanceof VarRef or
|
|
this instanceof PropAccess
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A variable reference or property access that is written to.
|
|
*
|
|
* For instance, in the assignment `x.p = x.q`, `x.p` is written to
|
|
* and `x.q` is not; in the expression `++i`, `i` is written to
|
|
* (and also read from).
|
|
*/
|
|
class LValue extends RefExpr {
|
|
LValue() { lvalAux(this, _) }
|
|
|
|
/** Gets the definition in which this lvalue occurs. */
|
|
ControlFlowNode getDefNode() { lvalAux(this, result) }
|
|
|
|
/** Gets the source of the assignment. */
|
|
AST::ValueNode getRhs() { defn(_, this, result) }
|
|
}
|
|
|
|
/**
|
|
* A variable reference or property access that is read from.
|
|
*
|
|
* For instance, in the assignment `x.p = x.q`, `x.q` is read from
|
|
* and `x.p` is not; in the expression `++i`, `i` is read from
|
|
* (and also written to).
|
|
*/
|
|
class RValue extends RefExpr {
|
|
RValue() {
|
|
not this instanceof LValue and not this instanceof VarDecl
|
|
or
|
|
// in `x++` and `x += 1`, `x` is both RValue and LValue
|
|
this = any(CompoundAssignExpr a).getTarget()
|
|
or
|
|
this = any(UpdateExpr u).getOperand().getUnderlyingReference()
|
|
or
|
|
this = any(NamespaceDeclaration decl).getIdentifier()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A ControlFlowNode that defines (that is, initializes or updates) variables or properties.
|
|
*
|
|
* The following program elements are definitions:
|
|
*
|
|
* - assignment expressions (`x = 42`)
|
|
* - update expressions (`++x`)
|
|
* - variable declarators with an initializer (`var x = 42`)
|
|
* - for-in and for-of statements (`for (x in o) { ... }`)
|
|
* - parameters of functions or catch clauses (`function (x) { ... }`)
|
|
* - named functions (`function x() { ... }`)
|
|
* - named classes (`class x { ... }`)
|
|
* - import specifiers (`import { x } from 'm'`)
|
|
*
|
|
* Note that due to destructuring, a single `VarDef` may define multiple
|
|
* variables and/or properties; for example, `{ x, y: z.p } = e` defines variable
|
|
* `x` as well as property `p` of `z`.
|
|
*/
|
|
class VarDef extends ControlFlowNode {
|
|
VarDef() { defn(this, _) }
|
|
|
|
/**
|
|
* Gets the target of this definition, which is either a simple variable
|
|
* reference, a destructuring pattern, or a property access.
|
|
*/
|
|
Expr getTarget() { defn(this, result) }
|
|
|
|
/** Gets a variable defined by this node, if any. */
|
|
Variable getAVariable() { result = this.getTarget().(BindingPattern).getAVariable() }
|
|
|
|
/**
|
|
* Gets the source of this definition, that is, the data flow node representing
|
|
* the value that this definition assigns to its target.
|
|
*
|
|
* This predicate is not defined for `VarDef`s where the source is implicit,
|
|
* such as `for-in` loops, parameters or destructuring assignments.
|
|
*/
|
|
AST::ValueNode getSource() {
|
|
exists(Expr target | not target instanceof DestructuringPattern and defn(this, target, result))
|
|
}
|
|
|
|
/**
|
|
* Gets the source that this definition destructs, that is, the
|
|
* right hand side of a destructuring assignment.
|
|
*/
|
|
AST::ValueNode getDestructuringSource() {
|
|
exists(Expr target | target instanceof DestructuringPattern and defn(this, target, result))
|
|
}
|
|
|
|
/**
|
|
* Holds if this definition of `v` is overwritten by another definition, that is,
|
|
* another definition of `v` is reachable from it in the CFG.
|
|
*/
|
|
predicate isOverwritten(Variable v) {
|
|
exists(BasicBlock bb, int i | bb.defAt(i, v, this) |
|
|
exists(int j | bb.defAt(j, v, _) and j > i)
|
|
or
|
|
bb.getASuccessor+().defAt(_, v, _)
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A ControlFlowNode that uses (that is, reads from) a single variable.
|
|
*
|
|
* Some variable definitions are also uses, notably the operands of update expressions.
|
|
*/
|
|
class VarUse extends ControlFlowNode, @varref instanceof RValue {
|
|
/** Gets the variable this use refers to. */
|
|
Variable getVariable() { result = this.(VarRef).getVariable() }
|
|
|
|
/**
|
|
* Gets a definition that may reach this use.
|
|
*
|
|
* For global variables, each definition is considered to reach each use.
|
|
*/
|
|
overlay[global]
|
|
VarDef getADef() {
|
|
result = this.getSsaVariable().getDefinition().getAContributingVarDef() or
|
|
result.getAVariable() = this.getVariable().(GlobalVariable)
|
|
}
|
|
|
|
/**
|
|
* Gets the unique SSA variable this use refers to.
|
|
*
|
|
* This predicate is only defined for variables that can be SSA-converted.
|
|
*/
|
|
overlay[global]
|
|
SsaVariable getSsaVariable() { result.getAUse() = this }
|
|
}
|