Merge pull request #85 from microsoft/powershell-cfg-for-function-bodies-and-loops

PS: Control-flow for function bodies and loops
This commit is contained in:
Mathias Vorreiter Pedersen
2024-09-03 19:51:22 +01:00
committed by GitHub
27 changed files with 1558 additions and 112 deletions

View File

@@ -22,6 +22,7 @@ import semmle.code.powershell.ScriptBlock
import semmle.code.powershell.StringLiteral
import semmle.code.powershell.AssignmentStatement
import semmle.code.powershell.BinaryExpression
import semmle.code.powershell.UnaryExpression
import semmle.code.powershell.ScriptBlockExpr
import semmle.code.powershell.TernaryExpression
import semmle.code.powershell.UsingExpression
@@ -47,7 +48,7 @@ import semmle.code.powershell.UsingStmt
import semmle.code.powershell.Type
import semmle.code.powershell.Member
import semmle.code.powershell.PropertyMember
import semmle.code.powershell.FunctionMember
import semmle.code.powershell.Function
import semmle.code.powershell.TryStmt
import semmle.code.powershell.IfStmt
import semmle.code.powershell.ExitStmt
@@ -65,6 +66,5 @@ import semmle.code.powershell.ParenExpression
import semmle.code.powershell.Chainable
import semmle.code.powershell.Pipeline
import semmle.code.powershell.StringConstantExpression
import semmle.code.powershell.FunctionDefinition
import semmle.code.powershell.InvokeMemberExpression
import semmle.code.powershell.CommentEntity

View File

@@ -7,5 +7,5 @@ class ArrayLiteral extends @array_literal, Expr {
Expr getAnElement() { array_literal_element(this, _, result) }
override string toString() { result = "ArrayLiteral at: " + this.getLocation().toString() }
override string toString() { result = "...,..." }
}

View File

@@ -1,15 +1,255 @@
import powershell
class BinaryExpr extends @binary_expression, Expr {
override string toString() {
result = "...+..." // TODO
}
override SourceLocation getLocation() { binary_expression_location(this, result) }
private int getKind() { binary_expression(this, result, _, _) }
int getKind() { binary_expression(this, result, _, _) }
Expr getLeft() { binary_expression(this, _, result, _) }
Expr getRight() { binary_expression(this, _, _, result) }
}
abstract private class AbstractArithmeticExpr extends BinaryExpr { }
final class ArithmeticExpr = AbstractArithmeticExpr;
class AddExpr extends AbstractArithmeticExpr {
AddExpr() { this.getKind() = 40 }
final override string toString() { result = "...+..." }
}
class SubExpr extends AbstractArithmeticExpr {
SubExpr() { this.getKind() = 41 }
final override string toString() { result = "...-..." }
}
class MulExpr extends AbstractArithmeticExpr {
MulExpr() { this.getKind() = 37 }
final override string toString() { result = "...*..." }
}
class DivExpr extends AbstractArithmeticExpr {
DivExpr() { this.getKind() = 38 }
final override string toString() { result = ".../..." }
}
class RemExpr extends AbstractArithmeticExpr {
RemExpr() { this.getKind() = 39 }
final override string toString() { result = "...%..." }
}
abstract private class AbstractBitwiseExpr extends BinaryExpr { }
final class BitwiseExpr = AbstractBitwiseExpr;
class BitwiseAndExpr extends AbstractBitwiseExpr {
BitwiseAndExpr() { this.getKind() = 56 }
final override string toString() { result = "...&..." }
}
class BitwiseOrExpr extends AbstractBitwiseExpr {
BitwiseOrExpr() { this.getKind() = 57 }
final override string toString() { result = "...|..." }
}
class BitwiseXorExpr extends AbstractBitwiseExpr {
BitwiseXorExpr() { this.getKind() = 58 }
final override string toString() { result = "...^..." }
}
class ShiftLeftExpr extends AbstractBitwiseExpr {
ShiftLeftExpr() { this.getKind() = 97 }
final override string toString() { result = "...<<..." }
}
class ShiftRightExpr extends AbstractBitwiseExpr {
ShiftRightExpr() { this.getKind() = 98 }
final override string toString() { result = "...>>..." }
}
abstract private class AbstractComparisonExpr extends BinaryExpr { }
final class ComparisonExpr = AbstractComparisonExpr;
abstract private class AbstractCaseInsensitiveComparisonExpr extends AbstractComparisonExpr { }
final class CaseInsensitiveComparisonExpr = AbstractCaseInsensitiveComparisonExpr;
class EqExpr extends AbstractCaseInsensitiveComparisonExpr {
EqExpr() { this.getKind() = 60 }
final override string toString() { result = "... -eq ..." }
}
class NeExpr extends AbstractCaseInsensitiveComparisonExpr {
NeExpr() { this.getKind() = 61 }
final override string toString() { result = "... -ne ..." }
}
abstract private class AbstractRelationalExpr extends AbstractComparisonExpr { }
final class RelationalExpr = AbstractRelationalExpr;
abstract private class AbstractCaseInsensitiveRelationalExpr extends AbstractRelationalExpr { }
class GeExpr extends AbstractCaseInsensitiveRelationalExpr {
GeExpr() { this.getKind() = 62 }
final override string toString() { result = "... -ge ..." }
}
class GtExpr extends AbstractCaseInsensitiveRelationalExpr {
GtExpr() { this.getKind() = 63 }
final override string toString() { result = "... -gt ..." }
}
class LtExpr extends AbstractCaseInsensitiveRelationalExpr {
LtExpr() { this.getKind() = 64 }
final override string toString() { result = "... -lt ..." }
}
class LeExpr extends AbstractCaseInsensitiveRelationalExpr {
LeExpr() { this.getKind() = 65 }
final override string toString() { result = "... -le ..." }
}
class LikeExpr extends AbstractCaseInsensitiveComparisonExpr {
LikeExpr() { this.getKind() = 66 }
final override string toString() { result = "... -like ..." }
}
class NotLikeExpr extends AbstractCaseInsensitiveComparisonExpr {
NotLikeExpr() { this.getKind() = 67 }
final override string toString() { result = "... -notlike ..." }
}
class MatchExpr extends AbstractCaseInsensitiveComparisonExpr {
MatchExpr() { this.getKind() = 68 }
final override string toString() { result = "... -match ..." }
}
class NotMatchExpr extends AbstractCaseInsensitiveComparisonExpr {
NotMatchExpr() { this.getKind() = 69 }
final override string toString() { result = "... -notmatch ..." }
}
class ReplaceExpr extends AbstractCaseInsensitiveComparisonExpr {
ReplaceExpr() { this.getKind() = 70 }
final override string toString() { result = "... -replace ..." }
}
abstract class AbstractTypeExpr extends BinaryExpr { }
final class TypeExpr = AbstractTypeExpr;
abstract class AbstractTypeComparisonExpr extends AbstractTypeExpr { }
final class TypeComparisonExpr = AbstractTypeComparisonExpr;
class IsExpr extends AbstractTypeComparisonExpr {
IsExpr() { this.getKind() = 92 }
final override string toString() { result = "... -is ..." }
}
class IsNotExpr extends AbstractTypeComparisonExpr {
IsNotExpr() { this.getKind() = 93 }
final override string toString() { result = "... -isnot ..." }
}
class AsExpr extends AbstractTypeExpr {
AsExpr() { this.getKind() = 94 }
final override string toString() { result = "... -as ..." }
}
abstract private class AbstractContainmentExpr extends BinaryExpr { }
final class ContainmentExpr = AbstractContainmentExpr;
abstract private class AbstractCaseInsensitiveContainmentExpr extends AbstractContainmentExpr { }
final class CaseInsensitiveContainmentExpr = AbstractCaseInsensitiveContainmentExpr;
class ContainsExpr extends AbstractCaseInsensitiveContainmentExpr {
ContainsExpr() { this.getKind() = 71 }
final override string toString() { result = "... -contains ..." }
}
class NotContainsExpr extends AbstractCaseInsensitiveContainmentExpr {
NotContainsExpr() { this.getKind() = 72 }
final override string toString() { result = "... -notcontains ..." }
}
class InExpr extends AbstractCaseInsensitiveContainmentExpr {
InExpr() { this.getKind() = 73 }
final override string toString() { result = "... -in ..." }
}
class NotInExpr extends AbstractCaseInsensitiveContainmentExpr {
NotInExpr() { this.getKind() = 74 }
final override string toString() { result = "... -notin ..." }
}
abstract private class AbstractLogicalBinaryExpr extends BinaryExpr { }
final class LogicalBinaryExpr = AbstractLogicalBinaryExpr;
class LogicalAndExpr extends AbstractLogicalBinaryExpr {
LogicalAndExpr() { this.getKind() = 53 }
final override string toString() { result = "... -and ..." }
}
class LogicalOrExpr extends AbstractLogicalBinaryExpr {
LogicalOrExpr() { this.getKind() = 54 }
final override string toString() { result = "... -or ..." }
}
class LogicalXorExpr extends AbstractLogicalBinaryExpr {
LogicalXorExpr() { this.getKind() = 55 }
final override string toString() { result = "... -xor ..." }
}
abstract private class AbstractStringExpr extends BinaryExpr { }
final class StringExpr = AbstractStringExpr;
class JoinExpr extends AbstractStringExpr {
JoinExpr() { this.getKind() = 59 }
final override string toString() { result = "... -join ..." }
}
class SplitExpr extends AbstractStringExpr {
SplitExpr() { this.getKind() = 75 }
final override string toString() { result = "... -split ..." }
}

View File

@@ -1,7 +1,7 @@
import powershell
class BreakStmt extends GotoStmt, Stmt {
class BreakStmt extends GotoStmt, @break_statement {
override SourceLocation getLocation() { break_statement_location(this, result) }
override string toString() { result = "continue" }
override string toString() { result = "break" }
}

View File

@@ -1,6 +1,6 @@
import powershell
class ContinueStmt extends GotoStmt, Stmt {
class ContinueStmt extends GotoStmt, @continue_statement {
override SourceLocation getLocation() { continue_statement_location(this, result) }
override string toString() { result = "continue" }

View File

@@ -7,5 +7,5 @@ class DoUntilStmt extends @do_until_statement, LoopStmt {
PipelineBase getCondition() { do_until_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
StmtBlock getBody() { do_until_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
final override StmtBlock getBody() { do_until_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
}

View File

@@ -7,5 +7,5 @@ class DoWhileStmt extends @do_while_statement, LoopStmt {
PipelineBase getCondition() { do_while_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
StmtBlock getBody() { do_while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
final override StmtBlock getBody() { do_while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
}

View File

@@ -5,12 +5,11 @@ class ForEachStmt extends @foreach_statement, LoopStmt {
override string toString() { result = "forach(... in ...)" }
StmtBlock getBody() { foreach_statement(this, _, _, result, _) } // TODO: Change @ast to @stmt_block in dbscheme
final override StmtBlock getBody() { foreach_statement(this, _, _, result, _) } // TODO: Change @ast to @stmt_block in dbscheme
VarAccess getVariable() { foreach_statement(this, result, _, _, _) } // TODO: Change @ast to @variable_expression in dbscheme
/** ..., if any. */
PipelineBase getCondition() { foreach_statement(this, _, result, _, _) } // TODO: Change @ast to @pipeline_base in dbscheme
PipelineBase getIterableExpr() { foreach_statement(this, _, result, _, _) } // TODO: Change @ast to @pipeline_base in dbscheme
predicate isParallel() { foreach_statement(this, _, _, _, 1) }
}

View File

@@ -11,5 +11,5 @@ class ForStmt extends @for_statement, LoopStmt {
PipelineBase getIterator() { for_statement_iterator(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
StmtBlock getBody() { for_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
final override StmtBlock getBody() { for_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
}

View File

@@ -0,0 +1,69 @@
import powershell
abstract private class AbstractFunction extends Ast {
abstract string getName();
abstract ScriptBlock getBody();
abstract Parameter getFunctionParameter(int i);
final Parameter getAFunctionParameter() { result = this.getFunctionParameter(_) }
final int getNumberOfFunctionParameters() { result = count(this.getAFunctionParameter()) }
final int getNumberOfParameters() { result = count(this.getAParameter()) }
final Parameter getParameter(int i) {
result = this.getFunctionParameter(i)
or
result = this.getBody().getParamBlock().getParameter(i)
}
final Parameter getAParameter() { result = this.getParameter(_) }
}
class NonMemberFunction extends @function_definition, Stmt, AbstractFunction {
override string toString() { result = this.getName() }
override SourceLocation getLocation() { function_definition_location(this, result) }
override string getName() { function_definition(this, _, result, _, _) }
override ScriptBlock getBody() { function_definition(this, result, _, _, _) }
predicate isFilter() { function_definition(this, _, _, true, _) }
predicate isWorkflow() { function_definition(this, _, _, _, true) }
override Parameter getFunctionParameter(int i) { function_definition_parameter(this, i, result) }
}
class MemberFunction extends @function_member, Member, AbstractFunction {
override string getName() { function_member(this, _, _, _, _, _, _, result, _) }
override SourceLocation getLocation() { function_member_location(this, result) }
override string toString() { result = this.getName() }
override ScriptBlock getBody() { function_member(this, result, _, _, _, _, _, _, _) }
override predicate isHidden() { function_member(this, _, _, true, _, _, _, _, _) }
override predicate isPrivate() { function_member(this, _, _, _, true, _, _, _, _) }
override predicate isPublic() { function_member(this, _, _, _, _, true, _, _, _) }
override predicate isStatic() { function_member(this, _, _, _, _, _, true, _, _) }
predicate isConstructor() { function_member(this, _, true, _, _, _, _, _, _) }
override Parameter getFunctionParameter(int i) { function_member_parameter(this, i, result) }
TypeConstraint getTypeConstraint() { function_member_return_type(this, result) }
}
class Constructor extends MemberFunction {
Constructor() { this.isConstructor() }
}
final class Function = AbstractFunction;

View File

@@ -1,19 +0,0 @@
import powershell
class Function extends @function_definition, Stmt {
override string toString() { result = "FunctionDefinition at: " + this.getLocation().toString() }
override SourceLocation getLocation() { function_definition_location(this, result) }
string getName() { function_definition(this, _, result, _, _) }
ScriptBlock getBody() { function_definition(this, result, _, _, _) }
predicate isFilter() { function_definition(this, _, _, true, _) }
predicate isWorkflow() { function_definition(this, _, _, _, true) }
Parameter getParameter(int i) { function_definition_parameter(this, i, result) }
Parameter getAParameter() { result = this.getParameter(_) }
}

View File

@@ -1,31 +0,0 @@
import powershell
class FunctionMember extends @function_member, Member {
override string getName() { function_member(this, _, _, _, _, _, _, result, _) }
override SourceLocation getLocation() { function_member_location(this, result) }
override string toString() { result = this.getName() }
ScriptBlock getBody() { function_member(this, result, _, _, _, _, _, _, _) }
override predicate isHidden() { function_member(this, _, _, true, _, _, _, _, _) }
override predicate isPrivate() { function_member(this, _, _, _, true, _, _, _, _) }
override predicate isPublic() { function_member(this, _, _, _, _, true, _, _, _) }
override predicate isStatic() { function_member(this, _, _, _, _, _, true, _, _) }
predicate isConstructor() { function_member(this, _, true, _, _, _, _, _, _) }
Parameter getParameter(int i) { function_member_parameter(this, i, result) }
Parameter getAParameter() { result = this.getParameter(_) }
TypeConstraint getTypeConstraint() { function_member_return_type(this, result) }
}
class Constructor extends FunctionMember {
Constructor() { this.isConstructor() }
}

View File

@@ -9,6 +9,8 @@ class IfStmt extends @if_statement, Stmt {
PipelineBase getCondition(int i) { if_statement_clause(this, i, result, _) } // TODO: Change @ast to @pipeline_base in dbscheme
PipelineBase getACondition() { result = this.getCondition(_) }
StmtBlock getThen(int i) { if_statement_clause(this, i, _, result) } // TODO: Change @ast to @statement_block in dbscheme
/** ..., if any. */

View File

@@ -1,3 +1,5 @@
import powershell
class LoopStmt extends @loop_statement, LabeledStmt { }
class LoopStmt extends @loop_statement, LabeledStmt {
StmtBlock getBody() { none() }
}

View File

@@ -1,7 +1,7 @@
import powershell
class ParamBlock extends @param_block, Ast {
override string toString() { result = "ParamBlock" }
override string toString() { result = "param(...)" }
override SourceLocation getLocation() { param_block_location(this, result) }

View File

@@ -1,11 +1,15 @@
import powershell
class Pipeline extends @pipeline, Chainable {
override string toString() { result = "...|..." }
override string toString() {
if this.getNumberOfComponents() = 1
then result = this.getComponent(0).toString()
else result = "...|..."
}
override SourceLocation getLocation() { pipeline_location(this, result) }
int getNumComponents() { pipeline(this, result) }
int getNumberOfComponents() { result = count(this.getAComponent()) }
CmdBase getComponent(int i) { pipeline_component(this, i, result) }

View File

@@ -1,7 +1,13 @@
import powershell
class ScriptBlock extends @script_block, Ast {
override string toString() { result = this.getLocation().getFile().getBaseName() }
predicate isTopLevel() { not exists(this.getParent()) }
override string toString() {
if this.isTopLevel()
then result = this.getLocation().getFile().getBaseName()
else result = "{...}"
}
override SourceLocation getLocation() { script_block_location(this, result) }

View File

@@ -3,17 +3,17 @@ import powershell
class StmtBlock extends @statement_block, Ast {
override SourceLocation getLocation() { statement_block_location(this, result) }
int getNumStatements() { statement_block(this, result, _) }
int getNumberOfStmts() { statement_block(this, result, _) }
int getNumTraps() { statement_block(this, _, result) }
Stmt getStatement(int index) { statement_block_statement(this, index, result) }
Stmt getStmt(int index) { statement_block_statement(this, index, result) }
Stmt getAStatement() { result = this.getStatement(_) }
Stmt getAStmt() { result = this.getStmt(_) }
TrapStmt getTrapStatement(int index) { statement_block_trap(this, index, result) }
TrapStmt getTrapStmt(int index) { statement_block_trap(this, index, result) }
TrapStmt getATrapStatement() { result = this.getTrapStatement(_) }
TrapStmt getATrapStmt() { result = this.getTrapStmt(_) }
override string toString() { result = "StatementBlock at: " + this.getLocation().toString() }
override string toString() { result = "{...}" }
}

View File

@@ -10,4 +10,14 @@ class ConditionalExpr extends @ternary_expression, Expr {
Expr getIfFalse() { ternary_expression(this, _, result, _) }
Expr getIfTrue() { ternary_expression(this, _, _, result) }
Expr getBranch(boolean value) {
value = true and
result = this.getIfTrue()
or
value = false and
result = this.getIfFalse()
}
Expr getABranch() { result = this.getBranch(_) }
}

View File

@@ -0,0 +1,15 @@
import powershell
class UnaryExpr extends @unary_expression, Expr {
override SourceLocation getLocation() { unary_expression_location(this, result) }
int getKind() { unary_expression(this, _, result, _) }
Expr getOperand() { unary_expression(this, result, _, _) }
}
class NotExpr extends UnaryExpr {
NotExpr() { this.getKind() = 36 }
final override string toString() { result = "!..." }
}

View File

@@ -7,5 +7,5 @@ class WhileStmt extends @while_statement, LoopStmt {
PipelineBase getCondition() { while_statement_condition(this, result) } // TODO: Change @ast to @pipeline_base in dbscheme
StmtBlock getBody() { while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
final override StmtBlock getBody() { while_statement(this, result) } // TODO: Change @ast to @stmt_block in dbscheme
}

View File

@@ -90,6 +90,10 @@ module SuccessorTypes {
final override string toString() { result = "break" }
}
class ContinueSuccessor extends SuccessorType, CfgImpl::TContinueSuccessor {
final override string toString() { result = "continue" }
}
class RaiseSuccessor extends SuccessorType, CfgImpl::TRaiseSuccessor {
final override string toString() { result = "raise" }
}

View File

@@ -15,11 +15,18 @@ private newtype TCompletion =
TBooleanCompletion(boolean b) { b in [false, true] } or
TReturnCompletion() or
TBreakCompletion() or
TContinueCompletion() or
TRaiseCompletion() or
TExitCompletion()
pragma[noinline]
private predicate completionIsValidForStmt(Ast n, Completion c) { none() }
private predicate completionIsValidForStmt(Ast n, Completion c) {
n instanceof BreakStmt and
c instanceof BreakCompletion
or
n instanceof ContinueStmt and
c instanceof ContinueCompletion
}
/** A completion of a statement or an expression. */
abstract class Completion extends TCompletion {
@@ -49,19 +56,10 @@ abstract class Completion extends TCompletion {
* Holds if this completion will continue a loop when it is the completion
* of a loop body.
*/
predicate continuesLoop() { this instanceof NormalCompletion }
/**
* Gets the inner completion. This is either the inner completion,
* when the completion is nested, or the completion itself.
*/
Completion getInnerCompletion() { result = this }
/**
* Gets the outer completion. This is either the outer completion,
* when the completion is nested, or the completion itself.
*/
Completion getOuterCompletion() { result = this }
predicate continuesLoop() {
this instanceof NormalCompletion or
this instanceof ContinueCompletion
}
/** Gets a successor type that matches this completion. */
abstract SuccessorType getAMatchingSuccessorType();
@@ -85,7 +83,50 @@ private predicate mustHaveBooleanCompletion(Ast n) { inBooleanContext(n) }
* Holds if `n` is used in a Boolean context. That is, the value
* that `n` evaluates to determines a true/false branch successor.
*/
private predicate inBooleanContext(Ast n) { none() }
private predicate inBooleanContext(Ast n) {
n = any(IfStmt ifStmt).getACondition()
or
n = any(WhileStmt whileStmt).getCondition()
or
n = any(DoWhileStmt doWhileStmt).getCondition()
or
n = any(ForStmt forStmt).getCondition()
or
n = any(DoUntilStmt doUntilStmt).getCondition()
or
exists(ConditionalExpr cond |
n = cond.getCondition()
or
inBooleanContext(cond) and
n = cond.getABranch()
)
or
exists(LogicalAndExpr parent |
n = parent.getLeft()
or
inBooleanContext(parent) and
n = parent.getRight()
)
or
exists(LogicalOrExpr parent |
n = parent.getLeft()
or
inBooleanContext(parent) and
n = parent.getRight()
)
or
n = any(NotExpr parent | inBooleanContext(parent)).getOperand()
or
exists(Pipeline pipeline |
inBooleanContext(pipeline) and
n = pipeline.getComponent(pipeline.getNumberOfComponents() - 1)
)
or
exists(CmdExpr cmdExpr |
inBooleanContext(cmdExpr) and
n = cmdExpr.getExpr()
)
}
/**
* A completion that represents normal evaluation of a statement or an
@@ -159,6 +200,16 @@ class BreakCompletion extends Completion, TBreakCompletion {
override string toString() { result = "break" }
}
/**
* A completion that represents evaluation of a statement or an
* expression resulting in a continuation of a loop.
*/
class ContinueCompletion extends Completion, TContinueCompletion {
override ContinueSuccessor getAMatchingSuccessorType() { any() }
override string toString() { result = "continue" }
}
/**
* A completion that represents evaluation of a statement or an
* expression resulting in a thrown exception.

View File

@@ -75,8 +75,29 @@ predicate succExit(CfgScope scope, Ast last, Completion c) { scope.exit(last, c)
/** Defines the CFG by dispatch on the various AST types. */
module Trees {
class ScriptBlockTree extends PreOrderTree instanceof ScriptBlock {
final override predicate last(AstNode last, Completion c) {
class NonDefaultParameterTree extends LeafTree instanceof Parameter {
NonDefaultParameterTree() { not exists(super.getDefaultValue()) }
}
class DefaultParameterTree extends StandardPostOrderTree instanceof Parameter {
DefaultParameterTree() { exists(super.getDefaultValue()) }
override AstNode getChildNode(int i) {
i = 0 and
result = super.getDefaultValue()
}
final override predicate propagatesAbnormal(AstNode child) { child = super.getDefaultValue() }
}
class ParameterBlockTree extends StandardPostOrderTree instanceof ParamBlock {
override AstNode getChildNode(int i) { result = super.getParameter(i) }
}
abstract class ScriptBlockTree extends ControlFlowTree instanceof ScriptBlock {
abstract predicate succEntry(AstNode n, Completion c);
override predicate last(AstNode last, Completion c) {
last(super.getEndBlock(), last, c)
or
not exists(super.getEndBlock()) and
@@ -89,16 +110,36 @@ module Trees {
not exists(super.getEndBlock()) and
not exists(super.getProcessBlock()) and
not exists(super.getBeginBlock()) and
last = this and
completionIsSimple(c)
last(super.getParamBlock(), last, c)
or
not exists(super.getEndBlock()) and
not exists(super.getProcessBlock()) and
not exists(super.getBeginBlock()) and
not exists(super.getParamBlock()) and
// No blocks at all. We end where we started
this.succEntry(last, c)
}
final override predicate propagatesAbnormal(AstNode child) {
child = [super.getBeginBlock(), super.getProcessBlock(), super.getEndBlock()]
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
pred = this and
override predicate succ(AstNode pred, AstNode succ, Completion c) {
this.succEntry(pred, c) and
(
first(super.getParamBlock(), succ)
or
not exists(super.getParamBlock()) and
first(super.getBeginBlock(), succ)
or
not exists(super.getParamBlock()) and
not exists(super.getBeginBlock()) and
first(super.getProcessBlock(), succ)
or
not exists(super.getParamBlock()) and
not exists(super.getBeginBlock()) and
not exists(super.getProcessBlock()) and
first(super.getEndBlock(), succ)
)
or
last(super.getParamBlock(), pred, c) and
completionIsNormal(c) and
(
first(super.getBeginBlock(), succ)
or
@@ -108,24 +149,19 @@ module Trees {
not exists(super.getBeginBlock()) and
not exists(super.getProcessBlock()) and
first(super.getEndBlock(), succ)
) and
completionIsSimple(c)
)
or
last(super.getBeginBlock(), pred, c) and
c instanceof NormalCompletion and
completionIsNormal(c) and
(
first(super.getProcessBlock(), succ)
or
not exists(super.getProcessBlock()) and
first(super.getEndBlock(), succ)
or
not exists(super.getProcessBlock()) and
not exists(super.getEndBlock()) and
succ = this
)
or
last(super.getProcessBlock(), pred, c) and
c instanceof NormalCompletion and
completionIsNormal(c) and
(
// If we process multiple items we will loop back to the process block
first(super.getProcessBlock(), succ)
@@ -134,6 +170,57 @@ module Trees {
first(super.getEndBlock(), succ)
)
}
final override predicate propagatesAbnormal(AstNode child) {
child = super.getParamBlock() or
child = super.getBeginBlock() or
child = super.getProcessBlock() or
child = super.getEndBlock()
}
}
class FunctionScriptBlockTree extends PreOrderTree, ScriptBlockTree {
Function func;
FunctionScriptBlockTree() { func.getBody() = this }
AstNode getParameter(int i) { result = func.getFunctionParameter(i) }
int getNumberOfParameters() { result = func.getNumberOfFunctionParameters() }
override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Step to the first parameter
pred = this and
first(this.getParameter(0), succ) and
completionIsSimple(c)
or
// Step to the next parameter
exists(int i |
last(this.getParameter(i), pred, c) and
completionIsNormal(c) and
first(this.getParameter(i + 1), succ)
)
or
// Body steps
super.succ(pred, succ, c)
}
final override predicate succEntry(AstNode n, Completion c) {
// If there are no paramters we enter the body directly
not exists(this.getParameter(0)) and
n = this and
completionIsSimple(c)
or
// Once we are done with the last parameter we enter the body
last(this.getParameter(this.getNumberOfParameters() - 1), n, c) and
completionIsNormal(c)
}
}
class TopLevelScriptBlockTree extends PreOrderTree, ScriptBlockTree {
TopLevelScriptBlockTree() { this.(ScriptBlock).isTopLevel() }
final override predicate succEntry(Ast n, Completion c) { n = this and completionIsSimple(c) }
}
class NamedBlockTree extends StandardPostOrderTree instanceof NamedBlock {
@@ -149,6 +236,192 @@ module Trees {
}
}
abstract class LoopStmtTree extends ControlFlowTree instanceof LoopStmt {
final AstNode getBody() { result = super.getBody() }
override predicate last(AstNode last, Completion c) {
// Exit the loop body when we encounter a break
last(this.getBody(), last, c) and
c instanceof BreakCompletion
or
// Body exits abnormally
last(this.getBody(), last, c) and
not c instanceof BreakCompletion and
not c.continuesLoop()
}
}
abstract class ConditionalLoopStmtTree extends PreOrderTree, LoopStmtTree {
abstract AstNode getCondition();
abstract predicate entersLoopWhenConditionIs(boolean value);
final override predicate propagatesAbnormal(AstNode child) { child = this.getCondition() }
override predicate last(AstNode last, Completion c) {
// Exit the loop body when the condition is false
last(this.getCondition(), last, c) and
this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot())
or
super.last(last, c)
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Condition -> body
last(this.getCondition(), pred, c) and
this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and
first(this.getBody(), succ)
or
// Body -> condition
last(this.getBody(), pred, c) and
c.continuesLoop() and
first(this.getCondition(), succ)
}
}
class WhileStmtTree extends ConditionalLoopStmtTree instanceof WhileStmt {
override predicate entersLoopWhenConditionIs(boolean value) { value = true }
override AstNode getCondition() { result = WhileStmt.super.getCondition() }
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Start with the condition
this = pred and
first(this.getCondition(), succ) and
completionIsSimple(c)
or
super.succ(pred, succ, c)
}
}
abstract class DoBasedLoopStmtTree extends ConditionalLoopStmtTree {
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Start with the body
this = pred and
first(this.getBody(), succ) and
completionIsSimple(c)
or
super.succ(pred, succ, c)
}
}
class DoWhileStmtTree extends DoBasedLoopStmtTree instanceof DoWhileStmt {
override AstNode getCondition() { result = DoWhileStmt.super.getCondition() }
override predicate entersLoopWhenConditionIs(boolean value) { value = true }
}
class DoUntilStmtTree extends DoBasedLoopStmtTree instanceof DoUntilStmt {
override AstNode getCondition() { result = DoUntilStmt.super.getCondition() }
override predicate entersLoopWhenConditionIs(boolean value) { value = false }
}
class ForStmtTree extends PreOrderTree, LoopStmtTree instanceof ForStmt {
final override predicate propagatesAbnormal(AstNode child) {
child = [super.getInitializer(), super.getIterator()]
}
override predicate last(AstNode last, Completion c) {
// Condition returns false
last(super.getCondition(), last, c) and
c instanceof FalseCompletion
or
super.last(last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Start with initialization
this = pred and
first(super.getInitializer(), succ) and
completionIsSimple(c)
or
// Initialization -> condition
last(super.getInitializer(), pred, c) and
completionIsNormal(c) and
first(super.getCondition(), succ)
or
// Condition -> body
last(super.getCondition(), pred, c) and
c instanceof TrueCompletion and
first(this.getBody(), succ)
or
// Body -> iterator
last(this.getBody(), pred, c) and
completionIsNormal(c) and
first(super.getIterator(), succ)
}
}
class ForEachStmtTree extends LoopStmtTree instanceof ForEachStmt {
final override predicate propagatesAbnormal(AstNode child) { child = super.getIterableExpr() }
final override predicate first(AstNode first) {
// Unlike most other statements, `foreach` statements are not modeled in
// pre-order, because we use the `foreach` node itself to represent the
// emptiness test that determines whether to execute the loop body
first(super.getIterableExpr(), first)
}
final override predicate last(AstNode last, Completion c) {
// Emptiness test exits with no more elements
last = this and
completionIsSimple(c)
or
super.last(last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
// Emptiness test
last(super.getIterableExpr(), pred, c) and
completionIsNormal(c) and
succ = this
or
// Emptiness test to variable declaration
pred = this and
first(super.getVariable(), succ) and
completionIsSimple(c)
or
// Variable declaration to body
last(super.getVariable(), succ, c) and
completionIsNormal(c) and
first(this.getBody(), succ)
or
// Body to emptiness test
last(this.getBody(), pred, c) and
c.continuesLoop() and
succ = this
}
}
class StmtBlockTree extends PreOrderTree instanceof StmtBlock {
final override predicate propagatesAbnormal(AstNode child) { child = super.getAStmt() }
final override predicate last(AstNode last, Completion c) {
last(super.getStmt(super.getNumberOfStmts() - 1), last, c)
or
not exists(super.getAStmt()) and
last = this and
completionIsSimple(c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {
this = pred and
first(super.getStmt(0), succ) and
completionIsSimple(c)
or
exists(int i |
last(super.getStmt(i), pred, c) and
completionIsNormal(c) and
first(super.getStmt(i + 1), succ)
)
}
}
class GotoStmtTree extends LeafTree instanceof GotoStmt { }
class FunctionStmtTree extends LeafTree instanceof Function { }
class VarAccessTree extends LeafTree instanceof VarAccess { }
class BinaryExprTree extends StandardPostOrderTree instanceof BinaryExpr {
@@ -159,11 +432,23 @@ module Trees {
}
}
class UnaryExprTree extends StandardPostOrderTree instanceof UnaryExpr {
override AstNode getChildNode(int i) { i = 0 and result = super.getOperand() }
}
class ArrayLiteralTree extends StandardPostOrderTree instanceof ArrayLiteral {
override AstNode getChildNode(int i) { result = super.getElement(i) }
}
class ConstExprTree extends LeafTree instanceof ConstExpr { }
class CmdExprTree extends StandardPreOrderTree instanceof CmdExpr {
override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() }
}
class PipelineTree extends StandardPreOrderTree instanceof Pipeline {
override AstNode getChildNode(int i) { result = super.getComponent(i) }
}
}
private import Scope
@@ -188,6 +473,7 @@ private module Cached {
TBooleanSuccessor(boolean b) { b in [false, true] } or
TReturnSuccessor() or
TBreakSuccessor() or
TContinueSuccessor() or
TRaiseSuccessor() or
TExitSuccessor()
}

View File

@@ -1,3 +1,260 @@
functions.ps1:
# 1| Add-Numbers-Arguments
#-----| -> foo
# 1| {...}
#-----| -> exit functions.ps1 (normal)
# 1| enter functions.ps1
#-----| -> functions.ps1
# 1| exit functions.ps1
# 1| exit functions.ps1 (normal)
#-----| -> exit functions.ps1
# 1| functions.ps1
#-----| -> Add-Numbers-Arguments
# 1| enter {...}
#-----| -> {...}
# 1| exit {...}
# 1| exit {...} (normal)
#-----| -> exit {...}
# 1| {...}
#-----| -> number1
# 3| param(...)
#-----| -> ...+...
# 3| {...}
#-----| -> exit {...} (normal)
# 4| number1
#-----| -> number2
# 5| number2
#-----| -> param(...)
# 8| number1
#-----| -> number2
# 8| ...+...
#-----| -> number1
# 8| ...+...
#-----| -> {...}
# 8| ...+...
#-----| -> ...+...
# 8| number2
#-----| -> ...+...
# 11| foo
#-----| -> Default-Arguments
# 11| enter {...}
#-----| -> {...}
# 11| exit {...}
# 11| exit {...} (normal)
#-----| -> exit {...}
# 11| {...}
#-----| -> a
# 11| param(...)
#-----| -> {...}
# 11| {...}
#-----| -> exit {...} (normal)
# 11| a
#-----| -> param(...)
# 13| Default-Arguments
#-----| -> Add-Numbers-From-Array
# 13| enter {...}
#-----| -> {...}
# 13| exit {...}
# 13| exit {...} (normal)
#-----| -> exit {...}
# 13| {...}
#-----| -> name0
# 14| param(...)
#-----| -> ...+...
# 14| {...}
#-----| -> exit {...} (normal)
# 15| name0
#-----| -> 0
# 16| name1
#-----| -> name1
# 16| 0
#-----| -> name1
# 17| name2
#-----| -> param(...)
# 17| name1
#-----| -> 1
# 17| ...+...
#-----| -> name2
# 17| 1
#-----| -> ...+...
# 19| name
#-----| -> name2
# 19| ...+...
#-----| -> name
# 19| ...+...
#-----| -> {...}
# 19| ...+...
#-----| -> ...+...
# 19| name2
#-----| -> ...+...
# 22| Add-Numbers-From-Array
#-----| -> Add-Numbers-From-Pipeline
# 22| enter {...}
#-----| -> {...}
# 22| exit {...}
# 22| exit {...} (normal)
#-----| -> exit {...}
# 22| {...}
#-----| -> numbers
# 24| param(...)
#-----| -> sum
# 24| {...}
#-----| -> exit {...} (normal)
# 25| numbers
#-----| -> param(...)
# 28| sum
#-----| -> 0
# 28| ...=...
#-----| -> numbers
# 28| 0
#-----| -> ...=...
# 28| 0
#-----| -> 0
# 29| forach(... in ...)
#-----| -> number
#-----| -> sum
# 29| number
# 29| numbers
#-----| -> numbers
# 29| numbers
#-----| -> forach(... in ...)
# 29| numbers
#-----| -> numbers
# 33| sum
#-----| -> sum
# 33| sum
#-----| -> {...}
# 33| sum
#-----| -> sum
# 36| Add-Numbers-From-Pipeline
#-----| -> {...}
# 36| enter {...}
#-----| -> {...}
# 36| exit {...}
# 36| exit {...} (normal)
#-----| -> exit {...}
# 36| {...}
#-----| -> numbers
# 38| param(...)
#-----| -> sum
# 39| numbers
#-----| -> param(...)
# 41| {...}
#-----| -> sum
# 42| sum
#-----| -> 0
# 42| ...=...
#-----| -> {...}
# 42| 0
#-----| -> ...=...
# 42| 0
#-----| -> 0
# 44| {...}
#-----| -> sum
#-----| -> sum
# 46| sum
#-----| -> _
# 46| ...=...
#-----| -> {...}
# 46| _
#-----| -> ...=...
# 46| _
#-----| -> _
# 48| {...}
#-----| -> exit {...} (normal)
# 50| sum
#-----| -> sum
# 50| sum
#-----| -> {...}
# 50| sum
#-----| -> sum
global.ps1:
# 1| {...}
#-----| -> c
@@ -57,3 +314,445 @@ global.ps1:
# 6| b
#-----| -> ...+...
loops.ps1:
# 1| Test-While
#-----| -> Test-Break
# 1| enter loops.ps1
#-----| -> loops.ps1
# 1| exit loops.ps1
# 1| exit loops.ps1 (normal)
#-----| -> exit loops.ps1
# 1| loops.ps1
#-----| -> Test-While
# 1| {...}
#-----| -> exit loops.ps1 (normal)
# 1| enter {...}
#-----| -> {...}
# 1| exit {...}
# 1| exit {...} (normal)
#-----| -> exit {...}
# 1| {...}
#-----| -> a
# 2| a
#-----| -> 0
# 2| ...=...
#-----| -> while(...) {...}
# 2| {...}
#-----| -> exit {...} (normal)
# 2| 0
#-----| -> ...=...
# 2| 0
#-----| -> 0
# 4| while(...) {...}
#-----| -> ... -le ...
# 4| a
#-----| -> 10
# 4| ... -le ...
#-----| -> a
# 4| ... -le ...
#-----| false -> {...}
#-----| true -> {...}
# 4| ... -le ...
#-----| -> ... -le ...
# 4| 10
#-----| -> ... -le ...
# 4| {...}
#-----| -> a
# 5| a
#-----| -> ...+...
# 5| ...=...
#-----| -> ... -le ...
# 5| a
#-----| -> 1
# 5| ...+...
#-----| -> ...=...
# 5| ...+...
#-----| -> a
# 5| 1
#-----| -> ...+...
# 9| Test-Break
#-----| -> Test-Continue
# 9| enter {...}
#-----| -> {...}
# 9| exit {...}
# 9| exit {...} (normal)
#-----| -> exit {...}
# 9| {...}
#-----| -> a
# 10| a
#-----| -> 0
# 10| ...=...
#-----| -> while(...) {...}
# 10| {...}
#-----| -> exit {...} (normal)
# 10| 0
#-----| -> ...=...
# 10| 0
#-----| -> 0
# 11| while(...) {...}
#-----| -> ... -le ...
# 11| a
#-----| -> 10
# 11| ... -le ...
#-----| -> a
# 11| ... -le ...
#-----| false -> {...}
#-----| true -> {...}
# 11| ... -le ...
#-----| -> ... -le ...
# 11| 10
#-----| -> ... -le ...
# 11| {...}
#-----| -> break
# 12| break
#-----| break -> exit {...} (normal)
# 17| Test-Continue
#-----| -> Test-DoWhile
# 17| enter {...}
#-----| -> {...}
# 17| exit {...}
# 17| exit {...} (normal)
#-----| -> exit {...}
# 17| {...}
#-----| -> a
# 18| a
#-----| -> 0
# 18| ...=...
#-----| -> while(...) {...}
# 18| {...}
#-----| -> exit {...} (normal)
# 18| 0
#-----| -> ...=...
# 18| 0
#-----| -> 0
# 19| while(...) {...}
#-----| -> ... -le ...
# 19| a
#-----| -> 10
# 19| ... -le ...
#-----| -> a
# 19| ... -le ...
#-----| false -> {...}
#-----| true -> {...}
# 19| ... -le ...
#-----| -> ... -le ...
# 19| 10
#-----| -> ... -le ...
# 19| {...}
#-----| -> continue
# 20| continue
#-----| continue -> ... -le ...
# 25| Test-DoWhile
#-----| -> Test-DoUntil
# 25| enter {...}
#-----| -> {...}
# 25| exit {...}
# 25| exit {...} (normal)
#-----| -> exit {...}
# 25| {...}
#-----| -> a
# 26| a
#-----| -> 0
# 26| ...=...
#-----| -> DoWhile
# 26| {...}
#-----| -> exit {...} (normal)
# 26| 0
#-----| -> ...=...
# 26| 0
#-----| -> 0
# 28| DoWhile
#-----| -> {...}
# 28| {...}
#-----| -> a
# 29| a
#-----| -> ...+...
# 29| ...=...
#-----| -> ... -le ...
# 29| a
#-----| -> 1
# 29| ...+...
#-----| -> ...=...
# 29| ...+...
#-----| -> a
# 29| 1
#-----| -> ...+...
# 30| a
#-----| -> 10
# 30| ... -le ...
#-----| -> a
# 30| ... -le ...
#-----| false -> {...}
#-----| true -> {...}
# 30| ... -le ...
#-----| -> ... -le ...
# 30| 10
#-----| -> ... -le ...
# 33| Test-DoUntil
#-----| -> Tet-For
# 33| enter {...}
#-----| -> {...}
# 33| exit {...}
# 33| exit {...} (normal)
#-----| -> exit {...}
# 33| {...}
#-----| -> a
# 34| a
#-----| -> 0
# 34| ...=...
#-----| -> DoUntil
# 34| {...}
#-----| -> exit {...} (normal)
# 34| 0
#-----| -> ...=...
# 34| 0
#-----| -> 0
# 36| DoUntil
#-----| -> {...}
# 36| {...}
#-----| -> a
# 37| a
#-----| -> ...+...
# 37| ...=...
#-----| -> ... -ge ...
# 37| a
#-----| -> 1
# 37| ...+...
#-----| -> ...=...
# 37| ...+...
#-----| -> a
# 37| 1
#-----| -> ...+...
# 38| a
#-----| -> 10
# 38| ... -ge ...
#-----| -> a
# 38| ... -ge ...
#-----| true -> {...}
#-----| false -> {...}
# 38| ... -ge ...
#-----| -> ... -ge ...
# 38| 10
#-----| -> ... -ge ...
# 41| Tet-For
#-----| -> Test-ForEach
# 41| enter {...}
#-----| -> {...}
# 41| exit {...}
# 41| exit {...} (normal)
#-----| -> exit {...}
# 41| {...}
#-----| -> a
# 42| a
#-----| -> 0
# 42| ...=...
#-----| -> for(...;...;...)
# 42| {...}
#-----| -> exit {...} (normal)
# 42| 0
#-----| -> ...=...
# 42| 0
#-----| -> 0
# 44| for(...;...;...)
#-----| -> i
# 44| i
#-----| -> 0
# 44| ...=...
#-----| -> ... -le ...
# 44| 0
#-----| -> ...=...
# 44| 0
#-----| -> 0
# 44| i
#-----| -> 10
# 44| ... -le ...
#-----| -> i
# 44| ... -le ...
#-----| false -> {...}
#-----| true -> {...}
# 44| ... -le ...
#-----| -> ... -le ...
# 44| 10
#-----| -> ... -le ...
# 44| i
#-----| -> ...+...
# 44| ...=...
# 44| i
#-----| -> 1
# 44| ...+...
#-----| -> ...=...
# 44| ...+...
#-----| -> i
# 44| 1
#-----| -> ...+...
# 44| {...}
#-----| -> a
# 45| a
#-----| -> ...+...
# 45| ...=...
#-----| -> i
# 45| a
#-----| -> 1
# 45| ...+...
#-----| -> ...=...
# 45| ...+...
#-----| -> a
# 45| 1
#-----| -> ...+...
# 49| Test-ForEach
#-----| -> {...}
# 49| enter {...}
#-----| -> {...}
# 49| {...}
#-----| -> letterArray
# 50| letterArray
#-----| -> ...,...
# 50| ...,...

View File

@@ -0,0 +1,53 @@
Function Add-Numbers-Arguments {
# We take in two numbers
param(
[int] $number1,
[int] $number2
)
# We add them together
$number1 + $number2
}
function foo() { param($a) }
Function Default-Arguments {
param(
[int] $name0,
[int] $name1 = 0,
[int] $name2 = $name1 + 1
)
$name + $name2
}
Function Add-Numbers-From-Array {
# We take in a list of numbers
param(
[int[]] $numbers
)
$sum = 0
foreach ($number in $numbers) {
# We add each number to the sum
$sum += $number
}
$sum
}
Function Add-Numbers-From-Pipeline {
# We take in a list of numbers
param(
[int[]] $numbers
)
Begin {
$sum = 0
}
Process {
# We add each number to the sum
$sum += $_
}
End {
# We return the sum
$sum
}
}

View File

@@ -0,0 +1,56 @@
function Test-While {
$a = 0
while($a -le 10) {
$a = $a + 1
}
}
function Test-Break {
$a = 0
while($a -le 10) {
break
$a = $a + 1
}
}
function Test-Continue {
$a = 0
while($a -le 10) {
continue
$a = $a + 1
}
}
function Test-DoWhile {
$a = 0
do {
$a = $a + 1
} while ($a -le 10)
}
function Test-DoUntil {
$a = 0
do {
$a = $a + 1
} until ($a -ge 10)
}
function Tet-For {
$a = 0
for ($i = 0; $i -le 10; $i = $i + 1) {
$a = $a + 1
}
}
function Test-ForEach {
$letterArray = 'a','b','c','d'
$a = 0
foreach ($letter in $letterArray)
{
$a = $a + 1
}
}