Skip to content

Commit

Permalink
wip3
Browse files Browse the repository at this point in the history
  • Loading branch information
hvitved committed Jun 27, 2024
1 parent 90ff4bf commit 8be274e
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 135 deletions.
9 changes: 3 additions & 6 deletions csharp/ql/lib/semmle/code/csharp/Location.qll
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,9 @@ class Location extends @location {
bindingset[this, other]
pragma[inline_late]
predicate before(Location other) {
this.getFile() = other.getFile() and
(
this.getStartLine() < other.getStartLine()
or
this.getStartLine() = other.getStartLine() and this.getStartColumn() <= other.getStartColumn()
)
this.getStartLine() < other.getStartLine()
or
this.getStartLine() = other.getStartLine() and this.getStartColumn() <= other.getStartColumn()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ module ControlFlow {
class BooleanSplit = Splitting::BooleanSplitting::BooleanSplit;

class LoopSplit = Splitting::LoopSplitting::LoopSplit;

class InitializerSplit = Splitting::InitializerSplitting::InitializerSplit;
}

class BasicBlock = BBs::BasicBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,26 +96,6 @@ private module CfgInput implements CfgShared::InputSig<Location> {

import CfgShared::Make<Location, CfgInput>

/**
* A compilation.
*
* Unlike the standard `Compilation` class, this class also supports buildless
* extraction.
*/
newtype CompilationExt =
TCompilation(Compilation c) { not extractionIsStandalone() } or
TBuildless() { extractionIsStandalone() }

/** Gets the compilation that source file `f` belongs to. */
CompilationExt getCompilation(File f) {
exists(Compilation c |
f = c.getAFileCompiled() and
result = TCompilation(c)
)
or
result = TBuildless()
}

/**
* The `expr_parent_top_level_adjusted()` relation restricted to exclude relations
* between properties and their getters' expression bodies in properties such as
Expand All @@ -142,7 +122,7 @@ predicate scopeFirst(CfgScope scope, AstNode first) {
then first(c.(Constructor).getInitializer(), first)
else
if InitializerSplitting::constructorInitializes(c, _)
then first(InitializerSplitting::constructorInitializeOrder(c, _, 0), first)
then first(InitializerSplitting::constructorInitializeOrder(c, 0), first)
else first(c.getBody(), first)
)
or
Expand All @@ -157,7 +137,7 @@ predicate scopeLast(CfgScope scope, AstNode last, Completion c) {
last(callable.getBody(), last, c) and
not c instanceof GotoCompletion
or
last(InitializerSplitting::lastConstructorInitializer(scope, _), last, c) and
last(InitializerSplitting::lastConstructorInitializer(scope), last, c) and
not callable.hasBody()
)
or
Expand All @@ -172,25 +152,18 @@ private class ConstructorTree extends ControlFlowTree instanceof Constructor {

final override predicate last(AstNode last, Completion c) { none() }

/** Gets the body of this constructor belonging to compilation `comp`. */
pragma[noinline]
AstNode getBody(CompilationExt comp) {
result = super.getBody() and
comp = getCompilation(result.getFile())
}

final override predicate succ(AstNode pred, AstNode succ, Completion c) {
exists(CompilationExt comp, int i, AssignExpr ae |
ae = InitializerSplitting::constructorInitializeOrder(this, comp, i) and
exists(int i, AssignExpr ae |
ae = InitializerSplitting::constructorInitializeOrder(this, i) and
last(ae, pred, c) and
c instanceof NormalCompletion
|
// Flow from one member initializer to the next
first(InitializerSplitting::constructorInitializeOrder(this, comp, i + 1), succ)
first(InitializerSplitting::constructorInitializeOrder(this, i + 1), succ)
or
// Flow from last member initializer to constructor body
ae = InitializerSplitting::lastConstructorInitializer(this, comp) and
first(this.getBody(comp), succ)
ae = InitializerSplitting::lastConstructorInitializer(this) and
first(super.getBody(), succ)
)
}
}
Expand Down Expand Up @@ -825,18 +798,23 @@ module Expressions {
first(this.getChildNode(i + 1), succ)
)
or
exists(ConstructorTree con, CompilationExt comp |
exists(ConstructorTree con |
last(this, pred, c) and
con = super.getConstructor() and
comp = getCompilation(this.getFile()) and
c instanceof NormalCompletion
|
// Flow from constructor initializer to first member initializer
first(InitializerSplitting::constructorInitializeOrder(con, comp, 0), succ)
first(InitializerSplitting::constructorInitializeOrder(con, 0), succ)
or
// Flow from constructor initializer to first element of constructor body
not exists(InitializerSplitting::constructorInitializeOrder(con, comp, _)) and
first(con.getBody(comp), succ)
exists(ControlFlowElement body |
body = con.(Constructor).getBody() and
not exists(InitializerSplitting::constructorInitializeOrder(con, _)) and
first(body, succ)
|
InitializerSplitting::nearestBody(this, body) or
con instanceof PrimaryConstructor
)
)
}
}
Expand Down
62 changes: 46 additions & 16 deletions csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ private module Cached {

cached
newtype TSplit =
TInitializerSplit(Constructor c) { InitializerSplitting::constructorInitializes(c, _) } or
TInitializerSplit(Element constructorOrBody) {
exists(Constructor c | InitializerSplitting::constructorInitializes(c, _) |
constructorOrBody = c.getBody()
or
not exists(c.getBody()) and
constructorOrBody = c
)
} or
TConditionalCompletionSplit(ConditionalCompletion c) or
TAssertionSplit(AssertionSplitting::Assertion a, int i, boolean success) {
exists(a.getExpr(i)) and
Expand Down Expand Up @@ -103,29 +110,26 @@ module InitializerSplitting {
}

/**
* Gets the `i`th member initializer expression for non-static constructor `c`
* in compilation `comp`.
* Gets the `i`th member initializer expression for non-static constructor `c`.
*/
AssignExpr constructorInitializeOrder(Constructor c, CompilationExt comp, int i) {
AssignExpr constructorInitializeOrder(Constructor c, int i) {
constructorInitializes(c, result) and
result =
rank[i + 1](AssignExpr ae0, Location l |
constructorInitializes(c, ae0) and
l = ae0.getLocation() and
getCompilation(l.getFile()) = comp
l = ae0.getLocation()
|
ae0 order by l.getStartLine(), l.getStartColumn(), l.getFile().getAbsolutePath()
)
}

/**
* Gets the last member initializer expression for non-static constructor `c`
* in compilation `comp`.
* Gets the last member initializer expression for non-static constructor `c`.
*/
AssignExpr lastConstructorInitializer(Constructor c, CompilationExt comp) {
AssignExpr lastConstructorInitializer(Constructor c) {
exists(int i |
result = constructorInitializeOrder(c, comp, i) and
not exists(constructorInitializeOrder(c, comp, i + 1))
result = constructorInitializeOrder(c, i) and
not exists(constructorInitializeOrder(c, i + 1))
)
}

Expand Down Expand Up @@ -178,13 +182,19 @@ module InitializerSplitting {
*
* respectively.
*/
private class InitializerSplit extends Split, TInitializerSplit {
private Constructor c;
class InitializerSplit extends Split, TInitializerSplit {
private Element constructorOrBody;

InitializerSplit() { this = TInitializerSplit(constructorOrBody) }

InitializerSplit() { this = TInitializerSplit(c) }
/** Gets the constructor/body. */
Element getConstructorOrBody() { result = constructorOrBody }

/** Gets the constructor. */
Constructor getConstructor() { result = c }
Constructor getConstructor() {
result.getBody() = constructorOrBody or
result = constructorOrBody
}

override string toString() { result = "" }
}
Expand All @@ -199,6 +209,22 @@ module InitializerSplitting {

int getNextListOrder() { result = 1 }

private import semmle.code.csharp.internal.Location

private module NearestLocationInput implements NearestLocationInputSig {
class C = ConstructorInitializer;

predicate relevantLocations(ConstructorInitializer ci, Location l1, Location l2) {
l1 = ci.getLocation() and
l2 = ci.getConstructor().getBody().getLocation()
}
}

predicate nearestBody(ConstructorInitializer ci, ControlFlowElement body) {
body = ci.getConstructor().getBody() and
NearestLocation<NearestLocationInput>::nearestLocation(ci, _, body.getLocation())
}

private class InitializerSplitImpl extends SplitImpl instanceof InitializerSplit {
override InitializerSplitKind getKind() { any() }

Expand All @@ -208,6 +234,10 @@ module InitializerSplitting {
succ(pred, succ, c) and
succ = any(InitializedInstanceMember m).getAnInitializerDescendant() and
super.getConstructor() = ci.getConstructor()
|
nearestBody(ci, super.getConstructorOrBody())
or
super.getConstructorOrBody() instanceof Constructor
)
}

Expand All @@ -221,7 +251,7 @@ module InitializerSplitting {
this.appliesTo(pred) and
succ(pred, succ, c) and
not succ = any(InitializedInstanceMember m).getAnInitializerDescendant() and
succ.(ControlFlowElement).getEnclosingCallable() = super.getConstructor()
first(super.getConstructorOrBody().(ControlFlowElement), succ)
}

override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,14 @@ class RefReturnKind extends OutRefReturnKind, TRefReturnKind {
override string toString() { result = "ref parameter " + pos }
}

private module NearestLocationInput implements NearestLocationInputSig {
class C = ControlFlow::BasicBlock;
private module NearestMultiBodyLocationInput implements NearestLocationInputSig {
class C = ControlFlowElement;

predicate relevantLocations(ControlFlow::BasicBlock bb, Location l1, Location l2) {
not bb instanceof ControlFlow::BasicBlocks::EntryBlock and
predicate relevantLocations(ControlFlowElement body, Location l1, Location l2) {
exists(DataFlowCallable c |
bb.getCallable() = c.asCallable(l1) and
l2 = bb.getLocation()
c.isMultiBodied() and
body = c.asCallable(l1).getBody() and
l2 = body.getLocation()
)
}
}
Expand All @@ -178,15 +178,50 @@ class DataFlowCallable extends TDataFlowCallable {
/** Gets the underlying source code callable, if any. */
Callable asCallable(Location l) { this = TCallable(result, l) }

/** Gets a basic block belonging to this callable. */
ControlFlow::BasicBlock getABasicBlock() {
exists(Location l | result.getCallable() = this.asCallable(l) |
result instanceof ControlFlow::BasicBlocks::EntryBlock
or
NearestLocation<NearestLocationInput>::nearestLocation(result, l, _)
predicate isMultiBodied() {
exists(DataFlowCallable other | other.asCallable(_) = this.asCallable(_) and other != this)
}

private ControlFlow::Nodes::ElementNode getMultiBodyEntry() {
exists(ControlFlowElement body, Location l |
body = this.asCallable(l).getBody() and
NearestLocation<NearestMultiBodyLocationInput>::nearestLocation(body, l, _) and
result = body.getAControlFlowEntryNode()
)
}

pragma[nomagic]
private ControlFlow::Nodes::ElementNode getAMultiBodyControlFlowNodePred() {
result = this.getMultiBodyEntry().getAPredecessor()
or
result = this.getAMultiBodyControlFlowNodePred().getAPredecessor()
}

pragma[nomagic]
private ControlFlow::Nodes::ElementNode getAMultiBodyControlFlowNodeSucc() {
result = this.getMultiBodyEntry().getASuccessor()
or
result = this.getAMultiBodyControlFlowNodeSucc().getASuccessor()
}

pragma[nomagic]
ControlFlow::Nodes::ElementNode getAMultiBodyControlFlowNode() {
result =
[
this.getMultiBodyEntry(), this.getAMultiBodyControlFlowNodePred(),
this.getAMultiBodyControlFlowNodeSucc()
]
}

/** Gets a control flow node belonging to this callable. */
pragma[inline]
ControlFlow::Node getAControlFlowNode() {
result = this.getAMultiBodyControlFlowNode()
or
not this.isMultiBodied() and
result.getEnclosingCallable() = this.asCallable(_)
}

/** Gets the underlying summarized callable, if any. */
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }

Expand Down Expand Up @@ -377,7 +412,7 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {

override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }

override DataFlowCallable getEnclosingCallable() { result.getABasicBlock() = cfn.getBasicBlock() }
override DataFlowCallable getEnclosingCallable() { result.getAControlFlowNode() = cfn }

override string toString() { result = cfn.toString() }

Expand Down Expand Up @@ -405,7 +440,7 @@ class ExplicitDelegateLikeDataFlowCall extends DelegateDataFlowCall, TExplicitDe

override DataFlow::ExprNode getNode() { result.getControlFlowNode() = cfn }

override DataFlowCallable getEnclosingCallable() { result.getABasicBlock() = cfn.getBasicBlock() }
override DataFlowCallable getEnclosingCallable() { result.getAControlFlowNode() = cfn }

override string toString() { result = cfn.toString() }

Expand Down
Loading

0 comments on commit 8be274e

Please sign in to comment.