Skip to content

Commit 95a7d65

Browse files
d10cMathiasVP
authored andcommitted
Swift: initial version of a swift port of most of the java code
1 parent 3253c04 commit 95a7d65

File tree

2 files changed

+177
-8
lines changed

2 files changed

+177
-8
lines changed

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
1010
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
1111
private import codeql.swift.frameworks.StandardLibrary.Array
1212
private import codeql.swift.frameworks.StandardLibrary.Dictionary
13+
private import codeql.dataflow.VariableCapture as VariableCapture
1314

1415
/** Gets the callable in which this node occurs. */
1516
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() }
@@ -98,6 +99,16 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl {
9899
}
99100
}
100101

102+
private class CaptureNodeImpl extends CaptureNode, NodeImpl {
103+
override Location getLocationImpl() { result = this.getSynthesizedCaptureNode().getLocation() }
104+
105+
override string toStringImpl() { result = this.getSynthesizedCaptureNode().toString() }
106+
107+
override DataFlowCallable getEnclosingCallable() {
108+
result.asSourceCallable() = this.getSynthesizedCaptureNode().getEnclosingCallable()
109+
}
110+
}
111+
101112
private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) {
102113
exists(BasicBlock bb, int i | def.lastRefRedef(bb, i, next) |
103114
def.definesAt(_, bb, i) and
@@ -145,7 +156,8 @@ private module Cached {
145156
} or
146157
TDictionarySubscriptNode(SubscriptExpr e) {
147158
e.getBase().getType().getCanonicalType() instanceof CanonicalDictionaryType
148-
}
159+
} or
160+
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn)
149161

150162
private predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
151163
def.adjacentReadPair(nodeFrom.getCfgNode(), nodeTo.getCfgNode()) and
@@ -305,7 +317,8 @@ private module Cached {
305317
TFieldContent(FieldDecl f) or
306318
TTupleContent(int index) { exists(any(TupleExpr te).getElement(index)) } or
307319
TEnumContent(ParamDecl f) { exists(EnumElementDecl d | d.getAParam() = f) } or
308-
TCollectionContent()
320+
TCollectionContent() or
321+
TCapturedVariableContent(CapturedVariable v)
309322
}
310323

311324
/**
@@ -371,7 +384,7 @@ private module ParameterNodes {
371384
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
372385

373386
/** Gets the parameter associated with this node, if any. */
374-
ParamDecl getParameter() { none() }
387+
override ParamDecl getParameter() { none() }
375388
}
376389

377390
class SourceParameterNode extends ParameterNodeImpl, TSourceParameterNode {
@@ -658,7 +671,7 @@ private module ReturnNodes {
658671
result = TDataFlowFunc(param.getDeclaringFunction())
659672
}
660673

661-
ParamDecl getParameter() { result = param }
674+
override ParamDecl getParameter() { result = param }
662675

663676
override Location getLocationImpl() { result = exit.getLocation() }
664677

@@ -785,6 +798,136 @@ private module OutNodes {
785798

786799
import OutNodes
787800

801+
private predicate closureFlowStep(Expr e1, Expr e2) {
802+
// simpleLocalFlowStep(exprNode(e1), exprNode(e2)) // TODO: find out why the java version uses simpleAstFlowStep... probably due to non-monotonic recursion
803+
// or
804+
exists(Ssa::WriteDefinition def |
805+
def.getARead().getNode().asAstNode() = e2 and
806+
def.assigns(any(CfgNode cfg | cfg.getNode().asAstNode() = e1))
807+
)
808+
}
809+
810+
private module CaptureInput implements VariableCapture::InputSig {
811+
private import swift as S
812+
private import codeql.swift.controlflow.BasicBlocks as B
813+
814+
class Location = S::Location;
815+
816+
class BasicBlock instanceof B::BasicBlock {
817+
string toString() { result = super.toString() }
818+
819+
Callable getEnclosingCallable() { result = super.getScope() }
820+
821+
Location getLocation() { result = super.getLocation() }
822+
}
823+
824+
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.(B::BasicBlock).dominates(bb) }
825+
826+
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.(B::BasicBlock).getASuccessor() }
827+
828+
//TODO: support capture of `this` in lambdas
829+
class CapturedVariable instanceof S::CapturedDecl {
830+
string toString() { result = super.toString() }
831+
832+
Callable getCallable() { result = super.getScope() }
833+
834+
Location getLocation() { result = super.getLocation() }
835+
}
836+
837+
class CapturedParameter extends CapturedVariable {
838+
CapturedParameter() { this.(S::CapturedDecl).getDecl() instanceof S::ParamDecl }
839+
}
840+
841+
class Expr instanceof S::AstNode {
842+
string toString() { result = super.toString() }
843+
844+
Location getLocation() { result = super.getLocation() }
845+
846+
predicate hasCfgNode(BasicBlock bb, int i) {
847+
this = bb.(B::BasicBlock).getNode(i).getNode().asAstNode()
848+
}
849+
}
850+
851+
class VariableWrite extends Expr {
852+
CapturedVariable variable;
853+
Expr source;
854+
855+
VariableWrite() {
856+
exists(S::VarDecl varDecl |
857+
variable.(S::CapturedDecl).getDecl() = varDecl and
858+
variable.getCallable() = this.(S::AstNode).getEnclosingCallable()
859+
|
860+
exists(S::Assignment a | this = a |
861+
a.getDest().(DeclRefExpr).getDecl() = varDecl and
862+
source = a.getSource()
863+
)
864+
or
865+
exists(S::PatternBindingDecl pbd, S::NamedPattern np |
866+
this = pbd and pbd.getAPattern() = np
867+
|
868+
np.getVarDecl() = varDecl and
869+
source = np.getMatchingExpr()
870+
)
871+
// TODO: support multiple variables in LHS of =, in both of above cases.
872+
)
873+
}
874+
875+
CapturedVariable getVariable() { result = variable }
876+
877+
Expr getSource() { result = source }
878+
}
879+
880+
class VariableRead extends Expr instanceof S::DeclRefExpr {
881+
CapturedVariable v;
882+
883+
VariableRead() { this.getCapturedDecl() = v /* TODO: this should be an R-value only. */ }
884+
885+
CapturedVariable getVariable() { result = v }
886+
}
887+
888+
class ClosureExpr extends Expr instanceof S::Callable {
889+
ClosureExpr() { any(S::CapturedDecl c).getScope() = this }
890+
891+
predicate hasBody(Callable body) { this = body }
892+
893+
predicate hasAliasedAccess(Expr f) {
894+
closureFlowStep+(this, f) and not closureFlowStep(f, _)
895+
/* TODO: understand why this is intra-procedural */ }
896+
}
897+
898+
class Callable extends S::Callable {
899+
predicate isConstructor() { this instanceof S::Initializer }
900+
}
901+
}
902+
903+
class CapturedVariable = CaptureInput::CapturedVariable;
904+
905+
class CapturedParameter = CaptureInput::CapturedParameter;
906+
907+
module CaptureFlow = VariableCapture::Flow<CaptureInput>;
908+
909+
private CaptureFlow::ClosureNode asClosureNode(Node n) {
910+
result = n.(CaptureNode).getSynthesizedCaptureNode() or
911+
result.(CaptureFlow::ExprNode).getExpr() = n.asExpr() or
912+
result.(CaptureFlow::ExprPostUpdateNode).getExpr() =
913+
n.(PostUpdateNode).getPreUpdateNode().asExpr() or
914+
result.(CaptureFlow::ParameterNode).getParameter().(CapturedDecl).getDecl() = n.getParameter() or
915+
result.(CaptureFlow::ThisParameterNode).getCallable().getSelfParam() = n.getParameter() or
916+
result.(CaptureFlow::MallocNode).getClosureExpr() = n.getCfgNode().getNode().asAstNode() // TODO: figure out why the java version had PostUpdateNode logic here
917+
}
918+
919+
private predicate captureStoreStep(Node node1, Content::CapturedVariableContent c, Node node2) {
920+
CaptureFlow::storeStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
921+
}
922+
923+
private predicate captureReadStep(Node node1, Content::CapturedVariableContent c, Node node2) {
924+
CaptureFlow::readStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
925+
}
926+
927+
predicate captureValueStep(Node node1, Node node2) {
928+
CaptureFlow::localFlowStep(asClosureNode(node1), asClosureNode(node2))
929+
}
930+
788931
predicate jumpStep(Node pred, Node succ) {
789932
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
790933
succ.(FlowSummaryNode).getSummaryNode())

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPublic.qll

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ class Node extends TNode {
5151
* Gets this node's underlying SSA definition, if any.
5252
*/
5353
Ssa::Definition asDefinition() { none() }
54+
55+
/**
56+
* Gets the parameter that corresponds to this node, if any.
57+
*/
58+
ParamDecl getParameter() { none() }
5459
}
5560

5661
/**
@@ -96,7 +101,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl {
96101
result = this.(ParameterNodeImpl).getEnclosingCallable()
97102
}
98103

99-
ParamDecl getParameter() { result = this.(ParameterNodeImpl).getParameter() }
104+
override ParamDecl getParameter() { result = this.(ParameterNodeImpl).getParameter() }
100105
}
101106

102107
/**
@@ -109,9 +114,7 @@ class SsaDefinitionNode extends Node, TSsaDefinitionNode {
109114
override Ssa::Definition asDefinition() { result = def }
110115
}
111116

112-
class InoutReturnNode extends Node instanceof InoutReturnNodeImpl {
113-
ParamDecl getParameter() { result = super.getParameter() }
114-
}
117+
class InoutReturnNode extends Node instanceof InoutReturnNodeImpl { }
115118

116119
/**
117120
* A node associated with an object after an operation that might have
@@ -129,6 +132,18 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
129132
Node getPreUpdateNode() { result = super.getPreUpdateNode() }
130133
}
131134

135+
/**
136+
* A synthesized data flow node representing a closure object that tracks
137+
* captured variables.
138+
*/
139+
class CaptureNode extends Node, TCaptureNode {
140+
private CaptureFlow::SynthesizedCaptureNode cn;
141+
142+
CaptureNode() { this = TCaptureNode(cn) }
143+
144+
CaptureFlow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn }
145+
}
146+
132147
/**
133148
* Gets a node corresponding to expression `e`.
134149
*/
@@ -234,6 +249,17 @@ module Content {
234249
* DEPRECATED: An element of a collection. This is an alias for the general CollectionContent.
235250
*/
236251
deprecated class ArrayContent = CollectionContent;
252+
253+
/** A captured variable. */
254+
class CapturedVariableContent extends Content, TCapturedVariableContent {
255+
CapturedVariable v;
256+
257+
CapturedVariableContent() { this = TCapturedVariableContent(v) }
258+
259+
CapturedVariable getVariable() { result = v }
260+
261+
override string toString() { result = v.toString() }
262+
}
237263
}
238264

239265
/**

0 commit comments

Comments
 (0)