Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d31499d

Browse files
committed
JS: introduce implicit this uses in general
1 parent 8dc0505 commit d31499d

6 files changed

Lines changed: 115 additions & 50 deletions

File tree

javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ private import internal.AnalyzedParameters
2626
private import internal.PreCallGraphStep
2727
private import semmle.javascript.internal.CachedStages
2828
private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private
29+
private import semmle.javascript.dataflow.internal.VariableOrThis
2930

3031
module DataFlow {
3132
/**
@@ -729,9 +730,7 @@ module DataFlow {
729730
private class ParameterFieldAsPropWrite extends PropWrite, PropNode {
730731
override ParameterField prop;
731732

732-
override Node getBase() {
733-
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
734-
}
733+
override Node getBase() { result = TImplicitThisUse(prop, false) }
735734

736735
override Expr getPropertyNameExpr() {
737736
none() // The parameter value is not the name of the field
@@ -758,9 +757,7 @@ module DataFlow {
758757
exists(prop.getInit())
759758
}
760759

761-
override Node getBase() {
762-
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
763-
}
760+
override Node getBase() { result = TImplicitThisUse(prop, false) }
764761

765762
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
766763

@@ -1045,12 +1042,12 @@ module DataFlow {
10451042
}
10461043

10471044
/**
1048-
* A node representing the value passed as `this` argument in a `new` call or a `super` call.
1045+
* A node representing the value passed as `this` argument in a `new` call.
10491046
*/
1050-
class ConstructorThisArgumentNode extends TConstructorThisArgumentNode, DataFlow::Node {
1051-
private InvokeExpr expr;
1047+
class NewCallThisArgumentNode extends TNewCallThisArgument, DataFlow::Node {
1048+
private NewExpr expr;
10521049

1053-
ConstructorThisArgumentNode() { this = TConstructorThisArgumentNode(expr) }
1050+
NewCallThisArgumentNode() { this = TNewCallThisArgument(expr) }
10541051

10551052
override string toString() { result = "implicit 'this' argument of " + expr }
10561053

@@ -1060,18 +1057,23 @@ module DataFlow {
10601057
}
10611058

10621059
/**
1063-
* A node representing the post-update node corresponding to implicit uses of `this` in a constructor.
1060+
* A node representing an implicit use of `this` or its post-update node.
10641061
*/
1065-
private class ConstructorThisPostUpdateNode extends TConstructorThisPostUpdate, DataFlow::Node {
1066-
private Function constructor;
1062+
private class ImplicitThisUseNode extends TImplicitThisUse, DataFlow::Node {
1063+
private ImplicitThisUse use;
1064+
private boolean isPost;
10671065

1068-
ConstructorThisPostUpdateNode() { this = TConstructorThisPostUpdate(constructor) }
1066+
ImplicitThisUseNode() { this = TImplicitThisUse(use, isPost) }
10691067

1070-
override string toString() { result = "[post-update] 'this' parameter of " + constructor }
1068+
override string toString() {
1069+
if isPost = false
1070+
then result = "implicit 'this'"
1071+
else result = "[post-update] implicit 'this'"
1072+
}
10711073

1072-
override StmtContainer getContainer() { result = constructor }
1074+
override StmtContainer getContainer() { result = use.getUseContainer() }
10731075

1074-
override Location getLocation() { result = constructor.getLocation() }
1076+
override Location getLocation() { result = use.getLocation() }
10751077
}
10761078

10771079
/**
@@ -1682,6 +1684,12 @@ module DataFlow {
16821684
pred = TReflectiveCallNode(call, _) and
16831685
succ = TValueNode(call)
16841686
)
1687+
or
1688+
// Pass 'this' into implicit uses of 'this'
1689+
exists(ImplicitThisUse use |
1690+
pred = TThisNode(use.getBindingContainer()) and
1691+
succ = TImplicitThisUse(use, false)
1692+
)
16851693
}
16861694

16871695
pragma[nomagic]
@@ -1772,12 +1780,6 @@ module DataFlow {
17721780
pred = TReflectiveParametersNode(f) and
17731781
succ = TValueNode(f.getArgumentsVariable().getAnAccess())
17741782
)
1775-
or
1776-
// Pass 'this' into super calls
1777-
exists(SuperCall call |
1778-
pred = TThisNode(call.getBinder()) and
1779-
succ = TConstructorThisArgumentNode(call)
1780-
)
17811783
}
17821784

17831785
private class ReflectiveParamsStep extends LegacyPreCallGraphStep {

javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
private import javascript
8+
private import codeql.util.Boolean
89
private import semmle.javascript.dataflow.internal.AdditionalFlowInternal
910
private import semmle.javascript.dataflow.internal.Contents::Private
1011
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
@@ -13,6 +14,7 @@ private import semmle.javascript.dataflow.internal.DataFlowPrivate as DataFlowPr
1314
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as FlowSummaryImpl
1415
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
1516
private import semmle.javascript.dataflow.internal.VariableCapture as VariableCapture
17+
private import semmle.javascript.dataflow.internal.VariableOrThis
1618

1719
cached
1820
private module Cached {
@@ -31,7 +33,7 @@ private module Cached {
3133
/** An SSA node from the legacy SSA library */
3234
TSsaDefNode(SsaDefinition d) or
3335
/** Use of a variable or 'this', with flow from a post-update node (from an earlier use) */
34-
TSsaUseNode(Expr use) { use = any(Ssa2::SsaConfig::SourceVariable v).getAnAccess() } or
36+
TSsaUseNode(ControlFlowNode use) { use = any(Ssa2::SsaConfig::SourceVariable v).getAUse() } or
3537
/** Phi-read node (new SSA library). Ordinary phi nodes are represented by TSsaDefNode. */
3638
TSsaPhiReadNode(Ssa2::PhiReadNode phi) or
3739
/** Input to a phi node (new SSA library) */
@@ -88,8 +90,8 @@ private module Cached {
8890
// The RHS of an assignment can be an argument to a setter-call, so it needs a post-update node
8991
e = any(Assignment asn | asn.getTarget() instanceof PropAccess).getRhs()
9092
} or
91-
TConstructorThisArgumentNode(InvokeExpr e) { e instanceof NewExpr or e instanceof SuperCall } or
92-
TConstructorThisPostUpdate(Constructor ctor) or
93+
TNewCallThisArgument(NewExpr e) or
94+
TImplicitThisUse(ImplicitThisUse use, Boolean isPost) or
9395
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
9496
TFlowSummaryDynamicParameterArrayNode(FlowSummaryImpl::Public::SummarizedCallable callable) or
9597
TFlowSummaryIntermediateAwaitStoreNode(FlowSummaryImpl::Private::SummaryNode sn) {
@@ -130,8 +132,9 @@ private class TEarlyStageNode =
130132
TFunctionSelfReferenceNode or TDestructuredModuleImportNode or THtmlAttributeNode or
131133
TFunctionReturnNode or TExceptionalFunctionReturnNode or TExceptionalInvocationReturnNode or
132134
TGlobalAccessPathRoot or TTemplatePlaceholderTag or TReflectiveParametersNode or
133-
TExprPostUpdateNode or TConstructorThisArgumentNode or TStaticArgumentArrayNode or
134-
TDynamicArgumentArrayNode or TStaticParameterArrayNode or TDynamicParameterArrayNode;
135+
TExprPostUpdateNode or TNewCallThisArgument or TStaticArgumentArrayNode or
136+
TDynamicArgumentArrayNode or TStaticParameterArrayNode or TDynamicParameterArrayNode or
137+
TImplicitThisUse;
135138

136139
/**
137140
* A data-flow node that is not a flow summary node.

javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ private class Node = DataFlow::Node;
2121
class PostUpdateNode = DataFlow::PostUpdateNode;
2222

2323
class SsaUseNode extends DataFlow::Node, TSsaUseNode {
24-
private Expr expr;
24+
private ControlFlowNode expr;
2525

2626
SsaUseNode() { this = TSsaUseNode(expr) }
2727

@@ -333,18 +333,13 @@ predicate postUpdatePair(Node pre, Node post) {
333333
)
334334
or
335335
exists(NewExpr expr |
336-
pre = TConstructorThisArgumentNode(expr) and
336+
pre = TNewCallThisArgument(expr) and
337337
post = TValueNode(expr)
338338
)
339339
or
340-
exists(SuperCall expr |
341-
pre = TConstructorThisArgumentNode(expr) and
342-
post = TConstructorThisPostUpdate(expr.getBinder())
343-
)
344-
or
345-
exists(Function constructor |
346-
pre = TThisNode(constructor) and
347-
post = TConstructorThisPostUpdate(constructor)
340+
exists(ImplicitThisUse use |
341+
pre = TImplicitThisUse(use, false) and
342+
post = TImplicitThisUse(use, true)
348343
)
349344
or
350345
FlowSummaryImpl::Private::summaryPostUpdateNode(post.(FlowSummaryNode).getSummaryNode(),
@@ -473,7 +468,7 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
473468
pos.isThis()
474469
)
475470
or
476-
pos.isThis() and n = TConstructorThisArgumentNode(call.asOrdinaryCall().asExpr())
471+
pos.isThis() and n = TNewCallThisArgument(call.asOrdinaryCall().asExpr())
477472
or
478473
// receiver of accessor call
479474
pos.isThis() and n = call.asAccessorCall().getBase()
@@ -1068,6 +1063,11 @@ private Node getNodeFromSsa2(Ssa2::Node node) {
10681063
or
10691064
result = TExprPostUpdateNode(node.(Ssa2::ExprPostUpdateNode).getExpr())
10701065
or
1066+
exists(ImplicitThisUse use |
1067+
node.(Ssa2::ExprPostUpdateNode).getExpr() = use and
1068+
result = TImplicitThisUse(use, true)
1069+
)
1070+
or
10711071
result = TSsaPhiReadNode(node.(Ssa2::SsaDefinitionExtNode).getDefinitionExt())
10721072
or
10731073
result = TSsaInputNode(node.(Ssa2::SsaInputNode))
@@ -1091,6 +1091,11 @@ private predicate useUseFlow(Node node1, Node node2) {
10911091
node1 = TSsaUseNode(use) and
10921092
node2 = TValueNode(use)
10931093
)
1094+
or
1095+
exists(ImplicitThisUse use |
1096+
node1 = TSsaUseNode(use) and
1097+
node2 = TImplicitThisUse(use, false)
1098+
)
10941099
}
10951100

10961101
predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
@@ -1298,11 +1303,16 @@ private Node getPostUpdateForStore(Node base) {
12981303
expr instanceof VarAccess or
12991304
expr instanceof ThisExpr
13001305
)
1306+
or
1307+
exists(ImplicitThisUse use |
1308+
base = TImplicitThisUse(use, false) and
1309+
result = TImplicitThisUse(use, true)
1310+
)
13011311
}
13021312

13031313
/** Gets node to target with a store to the given `base` object.. */
13041314
pragma[inline]
1305-
private Node getStoreTarget(Node base) {
1315+
private Node getStoreTarget(DataFlow::Node base) {
13061316
result = getPostUpdateForStore(base)
13071317
or
13081318
not exists(getPostUpdateForStore(base)) and

javascript/ql/lib/semmle/javascript/dataflow/internal/VariableOrThis.qll

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
private import javascript
2+
private import DataFlowNode
23

34
cached
45
private newtype TLocalVariableOrThis =
@@ -44,14 +45,17 @@ class LocalVariableOrThis extends TLocalVariableOrThis {
4445
result = this.asThisContainer()
4546
}
4647

47-
/** Gets an access to `this` represented by this value. */
48-
ThisExpr getAThisAccess() { result.getBindingContainer() = this.asThisContainer() }
48+
/** Gets an explicit access to `this` represented by this value. */
49+
ThisExpr getAThisExpr() { result.getBindingContainer() = this.asThisContainer() }
4950

50-
/** Gets an access to variable or `this`. */
51-
Expr getAnAccess() {
51+
/** Gets an implicit or explicit use of the `this` represented by this value. */
52+
ThisUse getAThisUse() { result.getBindingContainer() = this.asThisContainer() }
53+
54+
/** Gets an expression that accesses this variable or `this`. */
55+
ControlFlowNode getAUse() {
5256
result = this.asLocalVariable().getAnAccess()
5357
or
54-
result = this.getAThisAccess()
58+
result = this.getAThisUse()
5559
}
5660
}
5761

@@ -74,3 +78,50 @@ module LocalVariableOrThis {
7478
/** Gets the representation of `this` in the given container. */
7579
LocalVariableOrThis thisInContainer(StmtContainer c) { result = TThis(c) }
7680
}
81+
82+
/**
83+
* An explicit or implicit use of `this`.
84+
*
85+
* Implicit uses include `super()` calls and instance field initializers (which includes TypeScript parameter fields).
86+
*/
87+
abstract class ThisUse instanceof ControlFlowNode {
88+
/** Gets the container binding the `this` being accessed */
89+
abstract StmtContainer getBindingContainer();
90+
91+
abstract StmtContainer getUseContainer();
92+
93+
string toString() { result = super.toString() }
94+
95+
DbLocation getLocation() { result = super.getLocation() }
96+
}
97+
98+
private predicate implicitThisUse(ControlFlowNode node, StmtContainer thisBinder) {
99+
thisBinder = node.(SuperExpr).getBinder()
100+
or
101+
exists(FieldDefinition field |
102+
not field.isStatic() and
103+
node = field and
104+
thisBinder = field.getDeclaringClass().getConstructor().getBody()
105+
)
106+
}
107+
108+
class ImplicitThisUse extends ThisUse {
109+
ImplicitThisUse() { implicitThisUse(this, _) }
110+
111+
override StmtContainer getBindingContainer() { implicitThisUse(this, result) }
112+
113+
override StmtContainer getUseContainer() {
114+
// The following differs from FieldDefinition.getContainer() which returns the container enclosing
115+
// the class, not the class constructor.
116+
// TODO: consider changing this in FieldDefinition.getContainer()
117+
result = this.(FieldDefinition).getDeclaringClass().getConstructor().getBody()
118+
or
119+
result = this.(SuperExpr).getContainer()
120+
}
121+
}
122+
123+
private class ExplicitThisUse extends ThisUse instanceof ThisExpr {
124+
override StmtContainer getBindingContainer() { result = ThisExpr.super.getBindingContainer() }
125+
126+
override StmtContainer getUseContainer() { result = ThisExpr.super.getContainer() }
127+
}

javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ module SsaConfig implements InputSig<js::DbLocation> {
4242
bb.useAt(i, v.asLocalVariable(), _) and certain = true
4343
or
4444
certain = true and
45-
bb.getNode(i) = v.getAThisAccess()
46-
// TODO: also account for: super() and field initialisers
45+
bb.getNode(i).(ThisUse).getBindingContainer() = v.asThisContainer()
4746
}
4847

4948
predicate getImmediateBasicBlockDominator = BasicBlockInternal::immediateDominator/1;
@@ -55,8 +54,8 @@ module SsaConfig implements InputSig<js::DbLocation> {
5554
import Make<js::DbLocation, SsaConfig>
5655

5756
private module SsaDataflowInput implements DataFlowIntegrationInputSig {
58-
class Expr extends js::Expr {
59-
Expr() { this = any(SsaConfig::SourceVariable v).getAnAccess() }
57+
class Expr extends js::ControlFlowNode {
58+
Expr() { this = any(SsaConfig::SourceVariable v).getAUse() }
6059

6160
predicate hasCfgNode(js::BasicBlock bb, int i) { this = bb.getNode(i) }
6261
}

javascript/ql/test/library-tests/TripleDot/useuse.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,5 @@ function t5() {
6060
}
6161
}
6262
const c = new C();
63-
sink(c.field); // $ MISSING: hasValueFlow=t5.1
63+
sink(c.field); // $ hasValueFlow=t5.1
6464
}

0 commit comments

Comments
 (0)