mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
Foreach stmt
Addded support for the foreach stmt (for now only the "canonical" desugaring). Added a test and updated the expected output.
This commit is contained in:
@@ -269,7 +269,11 @@ newtype TTranslatedElement =
|
||||
)
|
||||
} or
|
||||
// A local declaration
|
||||
TTranslatedDeclaration(LocalVariableDeclExpr entry) or
|
||||
TTranslatedDeclaration(LocalVariableDeclExpr entry) {
|
||||
// foreach var decl and init is treated separately,
|
||||
// because foreach needs desugaring
|
||||
not entry.getParent() instanceof ForeachStmt
|
||||
} or
|
||||
// A compiler generated element, generated by `generatedBy` during the
|
||||
// desugaring process
|
||||
TTranslatedCompilerGeneratedElement(Element generatedBy, int index) {
|
||||
|
||||
@@ -11,6 +11,7 @@ private import TranslatedInitialization
|
||||
private import common.TranslatedConditionBlueprint
|
||||
private import IRInternal
|
||||
private import semmle.code.csharp.ir.internal.IRUtilities
|
||||
private import desugar.Foreach
|
||||
|
||||
TranslatedStmt getTranslatedStmt(Stmt stmt) { result.getAST() = stmt }
|
||||
|
||||
@@ -853,3 +854,35 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedEnumeratorForeach extends TranslatedLoop {
|
||||
override ForeachStmt stmt;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getTempEnumDecl() or
|
||||
id = 1 and result = getTry()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = getTempEnumDecl().getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
(
|
||||
child = getTempEnumDecl() and
|
||||
result = getTry().getFirstInstruction()
|
||||
) or
|
||||
(
|
||||
child = getTry() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
private TranslatedElement getTry() {
|
||||
result = ForeachElements::getTry(stmt)
|
||||
}
|
||||
|
||||
private TranslatedElement getTempEnumDecl() {
|
||||
result = ForeachElements::getEnumDecl(stmt)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,514 @@
|
||||
/**
|
||||
* File that provides the desugaring of a `Foreach` stmt.
|
||||
* Since Roslyn rewrites it in quite a few ways,
|
||||
* for now I will only desugar it to a "canonical" form.
|
||||
* Also we only deal with foreach stmts where there is only
|
||||
* one declaration (see bellow).
|
||||
* For example the code:
|
||||
* ```
|
||||
* foreach(var item in some_enumerable) {
|
||||
* // body
|
||||
* }
|
||||
* ```
|
||||
* gets desugared to:
|
||||
* ```
|
||||
* Enumerator e = some_enumerable.GetEnumerator();
|
||||
* try
|
||||
* {
|
||||
* while(e.MoveNext())
|
||||
* {
|
||||
* int current = e.Current;
|
||||
* //body
|
||||
* }
|
||||
* }
|
||||
* finally
|
||||
* {
|
||||
* e.Dispose();
|
||||
* }
|
||||
* ```
|
||||
* More info about the desugaring process for `foreach` stmts:
|
||||
* https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs
|
||||
* A TODO is to not call `Dispose` no matter what, but desugar the `finally` as an `AsExpr` (cast to IDisposable),
|
||||
* the call to `Dispose` being made only if the result of the `AsExpr` is not null.
|
||||
* This is a rough approximation which will need further refining.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import semmle.code.csharp.ir.implementation.Opcode
|
||||
private import semmle.code.csharp.ir.implementation.internal.OperandTag
|
||||
private import semmle.code.csharp.ir.internal.TempVariableTag
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag
|
||||
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedStmt
|
||||
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedConditionBlueprint
|
||||
private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBlueprint
|
||||
|
||||
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
|
||||
private import Common
|
||||
private import internal.TranslatedCompilerGeneratedStmt
|
||||
private import internal.TranslatedCompilerGeneratedCall
|
||||
private import internal.TranslatedCompilerGeneratedDeclaration
|
||||
private import internal.TranslatedCompilerGeneratedCondition
|
||||
private import internal.TranslatedCompilerGeneratedElement
|
||||
|
||||
/**
|
||||
* Module that exposes the functions needed for the translation of the `foreach` stmt.
|
||||
*/
|
||||
module ForeachElements {
|
||||
TranslatedForeachTry getTry(ForeachStmt generatedBy) {
|
||||
exists(TranslatedForeachTry try |
|
||||
try.getAST() = generatedBy and
|
||||
result = try
|
||||
)
|
||||
}
|
||||
|
||||
TranslatedForeachEnum getEnumDecl(ForeachStmt generatedBy) {
|
||||
exists(TranslatedForeachEnum enum |
|
||||
enum.getAST() = generatedBy and
|
||||
result = enum
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class TranslatedForeachTry extends TranslatedCompilerGeneratedTry, TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachTry() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 0)
|
||||
}
|
||||
|
||||
override TranslatedElement getFinally() {
|
||||
exists(TranslatedForeachFinally ff |
|
||||
ff.getAST() = generatedBy and
|
||||
result = ff
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedElement getBody() {
|
||||
exists(TranslatedForeachWhile fw |
|
||||
fw.getAST() = generatedBy and
|
||||
result = fw
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the finally block.
|
||||
*/
|
||||
private class TranslatedForeachFinally extends TranslatedCompilerGeneratedBlock,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachFinally() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 1)
|
||||
}
|
||||
|
||||
override TranslatedElement getStmt(int index) {
|
||||
index = 0 and
|
||||
exists(TranslatedForeachDispose fd |
|
||||
fd.getAST() = generatedBy and
|
||||
result = fd
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The compiler generated while loop.
|
||||
*/
|
||||
private class TranslatedForeachWhile extends TranslatedCompilerGeneratedStmt, ConditionContext,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachWhile() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 2)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
result = getCondition().getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getInit() and result = getBody().getFirstInstruction() or
|
||||
child = getBody() and result = getCondition().getFirstInstruction()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getCondition() or
|
||||
id = 1 and result = getInit() or
|
||||
id = 2 and result = getBody()
|
||||
}
|
||||
|
||||
override final Instruction getChildTrueSuccessor(ConditionBlueprint child) {
|
||||
child = getCondition() and result = getInit().getFirstInstruction()
|
||||
}
|
||||
|
||||
override final Instruction getChildFalseSuccessor(ConditionBlueprint child) {
|
||||
child = getCondition() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
private TranslatedStmt getBody() {
|
||||
result = getTranslatedStmt(generatedBy.getBody())
|
||||
}
|
||||
|
||||
private TranslatedElement getInit() {
|
||||
exists(TranslatedForeachIterVar iv |
|
||||
iv.getAST() = generatedBy and
|
||||
result = iv
|
||||
)
|
||||
}
|
||||
|
||||
private ValueConditionBlueprint getCondition() {
|
||||
exists(TranslatedForeachWhileCondition cond |
|
||||
cond.getAST() = generatedBy and
|
||||
result = cond
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to the `MoveNext` method, used as a condition for the while.
|
||||
*/
|
||||
private class TranslatedForeachMoveNext extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachMoveNext() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 3)
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable internal |
|
||||
internal.getName() = "MoveNext" and
|
||||
internal.getReturnType() instanceof BoolType and result = internal
|
||||
)
|
||||
}
|
||||
|
||||
override Type getCallResultType() {
|
||||
result instanceof BoolType
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override predicate hasArguments() { none() }
|
||||
|
||||
override TranslatedExprBlueprint getQualifier() {
|
||||
exists(TranslatedMoveNextEnumAcc acc |
|
||||
acc.getAST() = generatedBy and
|
||||
result = acc
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to retrieve the enumerator.
|
||||
*/
|
||||
private class TranslatedForeachGetEnumerator extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachGetEnumerator() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 4)
|
||||
}
|
||||
|
||||
final override Type getCallResultType() {
|
||||
result = getInstructionFunction(CallTargetTag()).getReturnType()
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable internal |
|
||||
internal.getName() = "GetEnumerator" and
|
||||
// TODO: For now ignore the possibility that the
|
||||
// foreach variable can have a generic type.
|
||||
// The type of the callable will need to be fabricated,
|
||||
// since we might not find the correct callable in the DB.
|
||||
// Probably will have change the way the immediate
|
||||
// operand of `FunctionAddress` is calculated.
|
||||
internal.getReturnType().getName() = "IEnumerator" and
|
||||
result = internal
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override predicate hasArguments() { none() }
|
||||
|
||||
override TranslatedExprBlueprint getQualifier() {
|
||||
result = getTranslatedExpr(generatedBy.getIterableExpr())
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to the getter method of the `Current` property of the enumerator.
|
||||
*/
|
||||
private class TranslatedForeachCurrent extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachCurrent() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 5)
|
||||
}
|
||||
|
||||
override Type getCallResultType() {
|
||||
result = generatedBy.getAVariable().getType()
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override predicate hasArguments() { none() }
|
||||
|
||||
override TranslatedExprBlueprint getQualifier() {
|
||||
exists(TranslatedForeachCurrentEnumAcc acc |
|
||||
acc.getAST() = generatedBy and
|
||||
result = acc
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Property prop |
|
||||
prop.getName() = "Current" and
|
||||
result = prop.getGetter()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation of the call to dispose (inside the finally block)
|
||||
*/
|
||||
private class TranslatedForeachDispose extends TranslatedCompilerGeneratedCall,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachDispose() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 6)
|
||||
}
|
||||
|
||||
override Callable getInstructionFunction(InstructionTag tag) {
|
||||
tag = CallTargetTag() and
|
||||
exists(Callable dispose |
|
||||
dispose.getName() = "Dispose" and
|
||||
result = dispose
|
||||
)
|
||||
}
|
||||
|
||||
final override Type getCallResultType() {
|
||||
result instanceof VoidType
|
||||
}
|
||||
|
||||
override TranslatedExpr getArgument(int id) { none() }
|
||||
|
||||
override predicate hasArguments() { none() }
|
||||
|
||||
override TranslatedExprBlueprint getQualifier() {
|
||||
exists(TranslatedForeachDisposeEnumAcc acc |
|
||||
acc.getAST() = generatedBy and
|
||||
result = acc
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
result = getQualifier().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition for the while, ie. a call to MoveNext.
|
||||
*/
|
||||
private class TranslatedForeachWhileCondition extends TranslatedCompilerGeneratedValueCondition,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachWhileCondition() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 7)
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getValueExpr() {
|
||||
exists(TranslatedForeachMoveNext mn |
|
||||
mn.getAST() = generatedBy and
|
||||
result = mn
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction valueExprResult() {
|
||||
result = getValueExpr().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of the declaration that happens before the `try ... finally` block (the
|
||||
* declaration of the `temporary` enumerator variable)
|
||||
*/
|
||||
private class TranslatedForeachEnum extends TranslatedCompilerGeneratedDeclaration,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachEnum() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 8)
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getInitialization().getCallResultType()
|
||||
}
|
||||
|
||||
override IRTempVariable getIRVariable() {
|
||||
result = getIRTempVariable(generatedBy, ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getInitialization() {
|
||||
exists(TranslatedForeachGetEnumerator ge |
|
||||
ge.getAST() = generatedBy and
|
||||
result = ge
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInitializationResult() {
|
||||
result = getInitialization().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of the declaration that's happening inside the body of the while.
|
||||
*/
|
||||
private class TranslatedForeachIterVar extends TranslatedCompilerGeneratedDeclaration,
|
||||
TTranslatedCompilerGeneratedElement {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachIterVar() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 9)
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
result = getIRVariable()
|
||||
}
|
||||
|
||||
override IRVariable getIRVariable() {
|
||||
result = getIRUserVariable(getFunction(), generatedBy.getAVariable())
|
||||
}
|
||||
|
||||
override TranslatedCompilerGeneratedCall getInitialization() {
|
||||
exists(TranslatedForeachCurrent crtProp |
|
||||
crtProp.getAST() = generatedBy and
|
||||
result = crtProp
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInitializationResult() {
|
||||
result = getInitialization().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of access to the temporary enumerator variable. Used as the qualifier
|
||||
* for the call to `MoveNext`.
|
||||
*/
|
||||
private class TranslatedMoveNextEnumAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedMoveNextEnumAcc() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 10)
|
||||
}
|
||||
|
||||
override Type getResultType() {
|
||||
result instanceof BoolType
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getResultType()
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = getTempVariable(ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of access to the temporary enumerator variable. Used as the qualifier
|
||||
* for the call to the getter of the property `Current`.
|
||||
*/
|
||||
private class TranslatedForeachCurrentEnumAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachCurrentEnumAcc() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 11)
|
||||
}
|
||||
|
||||
override Type getResultType() {
|
||||
result instanceof BoolType
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getResultType()
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = getTempVariable(ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that represents that translation of access to the temporary enumerator variable. Used as the qualifier
|
||||
* for the call to `Dispose`.
|
||||
*/
|
||||
private class TranslatedForeachDisposeEnumAcc extends TTranslatedCompilerGeneratedElement,
|
||||
TranslatedCompilerGeneratedVariableAccess {
|
||||
override ForeachStmt generatedBy;
|
||||
|
||||
TranslatedForeachDisposeEnumAcc() {
|
||||
this = TTranslatedCompilerGeneratedElement(generatedBy, 12)
|
||||
}
|
||||
|
||||
override Type getResultType() {
|
||||
result instanceof BoolType
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
tag = ForeachEnumTempVar() and
|
||||
type = getResultType()
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = AddressTag() and
|
||||
result = getTempVariable(ForeachEnumTempVar())
|
||||
}
|
||||
|
||||
override predicate needsLoad() { any() }
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
import csharp
|
||||
|
||||
// TODO: ARE THOSE TAGS ENOUGH?
|
||||
newtype TTempVariableTag =
|
||||
ConditionValueTempVar() or
|
||||
ReturnValueTempVar() or
|
||||
ThrowTempVar() or
|
||||
LambdaTempVar()
|
||||
LambdaTempVar() or
|
||||
ForeachEnumTempVar()
|
||||
|
||||
|
||||
string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ConditionValueTempVar() and result = "CondVal"
|
||||
or
|
||||
tag = ReturnValueTempVar() and result = "Ret"
|
||||
or
|
||||
tag = ThrowTempVar() and result = "Throw"
|
||||
or
|
||||
tag = LambdaTempVar() and result = "Lambda"
|
||||
tag = ConditionValueTempVar() and result = "CondVal" or
|
||||
tag = ReturnValueTempVar() and result = "Ret" or
|
||||
tag = ThrowTempVar() and result = "Throw" or
|
||||
tag = LambdaTempVar() and result = "Lambda" or
|
||||
tag = ForeachEnumTempVar() and result = "ForeachEnum"
|
||||
}
|
||||
|
||||
12
csharp/ql/test/library-tests/ir/ir/foreach.cs
Normal file
12
csharp/ql/test/library-tests/ir/ir/foreach.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
class ForEach {
|
||||
public static void Main() {
|
||||
int[] a_array = new int[] { 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
foreach(int items in a_array)
|
||||
{
|
||||
int x = items;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -354,6 +354,85 @@ delegates.cs:
|
||||
# 11| v0_17(Void) = UnmodeledUse : mu*
|
||||
# 11| v0_18(Void) = ExitFunction :
|
||||
|
||||
foreach.cs:
|
||||
# 4| System.Void ForEach.Main()
|
||||
# 4| Block 0
|
||||
# 4| v0_0(Void) = EnterFunction :
|
||||
# 4| mu0_1(null) = AliasedDefinition :
|
||||
# 4| mu0_2(null) = UnmodeledDefinition :
|
||||
# 5| r0_3(glval<Int32[]>) = VariableAddress[a_array] :
|
||||
# 5| mu0_4(Int32[]) = Uninitialized[a_array] : &:r0_3
|
||||
# 5| r0_5(Int32) = Constant[0] :
|
||||
# 5| r0_6(glval<Int32>) = PointerAdd : r0_3, r0_5
|
||||
# 5| r0_7(Int32) = Constant[1] :
|
||||
# 5| mu0_8(Int32) = Store : &:r0_6, r0_7
|
||||
# 5| r0_9(Int32) = Constant[1] :
|
||||
# 5| r0_10(glval<Int32>) = PointerAdd : r0_3, r0_9
|
||||
# 5| r0_11(Int32) = Constant[2] :
|
||||
# 5| mu0_12(Int32) = Store : &:r0_10, r0_11
|
||||
# 5| r0_13(Int32) = Constant[2] :
|
||||
# 5| r0_14(glval<Int32>) = PointerAdd : r0_3, r0_13
|
||||
# 5| r0_15(Int32) = Constant[3] :
|
||||
# 5| mu0_16(Int32) = Store : &:r0_14, r0_15
|
||||
# 5| r0_17(Int32) = Constant[3] :
|
||||
# 5| r0_18(glval<Int32>) = PointerAdd : r0_3, r0_17
|
||||
# 5| r0_19(Int32) = Constant[4] :
|
||||
# 5| mu0_20(Int32) = Store : &:r0_18, r0_19
|
||||
# 5| r0_21(Int32) = Constant[4] :
|
||||
# 5| r0_22(glval<Int32>) = PointerAdd : r0_3, r0_21
|
||||
# 5| r0_23(Int32) = Constant[5] :
|
||||
# 5| mu0_24(Int32) = Store : &:r0_22, r0_23
|
||||
# 5| r0_25(Int32) = Constant[5] :
|
||||
# 5| r0_26(glval<Int32>) = PointerAdd : r0_3, r0_25
|
||||
# 5| r0_27(Int32) = Constant[6] :
|
||||
# 5| mu0_28(Int32) = Store : &:r0_26, r0_27
|
||||
# 5| r0_29(Int32) = Constant[6] :
|
||||
# 5| r0_30(glval<Int32>) = PointerAdd : r0_3, r0_29
|
||||
# 5| r0_31(Int32) = Constant[7] :
|
||||
# 5| mu0_32(Int32) = Store : &:r0_30, r0_31
|
||||
# 7| r0_33(glval<IEnumerator>) = VariableAddress[#temp7:9] :
|
||||
# 7| r0_34(glval<Int32[]>) = VariableAddress[a_array] :
|
||||
# 7| r0_35(Int32[]) = Load : &:r0_34, ~mu0_2
|
||||
# 7| r0_36(glval<null>) = FunctionAddress[GetEnumerator] :
|
||||
# 7| r0_37(IEnumerator) = Call : func:r0_36, this:r0_35
|
||||
# 7| mu0_38(null) = ^CallSideEffect : ~mu0_2
|
||||
# 7| mu0_39(IEnumerator) = Store : &:r0_33, r0_37
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 7| Block 1
|
||||
# 7| r1_0(glval<Boolean>) = VariableAddress[#temp7:9] :
|
||||
# 7| r1_1(Boolean) = Load : &:r1_0, ~mu0_2
|
||||
# 7| r1_2(glval<null>) = FunctionAddress[MoveNext] :
|
||||
# 7| r1_3(Boolean) = Call : func:r1_2, this:r1_1
|
||||
# 7| mu1_4(null) = ^CallSideEffect : ~mu0_2
|
||||
# 7| v1_5(Void) = ConditionalBranch : r1_3
|
||||
#-----| False (back edge) -> Block 3
|
||||
#-----| True (back edge) -> Block 2
|
||||
|
||||
# 7| Block 2
|
||||
# 7| r2_0(glval<Int32>) = VariableAddress[items] :
|
||||
# 7| r2_1(glval<Boolean>) = VariableAddress[#temp7:9] :
|
||||
# 7| r2_2(Boolean) = Load : &:r2_1, ~mu0_2
|
||||
# 7| r2_3(glval<null>) = FunctionAddress[get_Current] :
|
||||
# 7| r2_4(Int32) = Call : func:r2_3, this:r2_2
|
||||
# 7| mu2_5(null) = ^CallSideEffect : ~mu0_2
|
||||
# 7| mu2_6(Int32) = Store : &:r2_0, r2_4
|
||||
# 9| r2_7(glval<Int32>) = VariableAddress[x] :
|
||||
# 9| r2_8(glval<Int32>) = VariableAddress[items] :
|
||||
# 9| r2_9(Int32) = Load : &:r2_8, ~mu0_2
|
||||
# 9| mu2_10(Int32) = Store : &:r2_7, r2_9
|
||||
#-----| Goto (back edge) -> Block 1
|
||||
|
||||
# 7| Block 3
|
||||
# 7| r3_0(glval<Boolean>) = VariableAddress[#temp7:9] :
|
||||
# 7| r3_1(Boolean) = Load : &:r3_0, ~mu0_2
|
||||
# 7| r3_2(glval<null>) = FunctionAddress[Dispose] :
|
||||
# 7| v3_3(Void) = Call : func:r3_2, this:r3_1
|
||||
# 7| mu3_4(null) = ^CallSideEffect : ~mu0_2
|
||||
# 4| v3_5(Void) = ReturnVoid :
|
||||
# 4| v3_6(Void) = UnmodeledUse : mu*
|
||||
# 4| v3_7(Void) = ExitFunction :
|
||||
|
||||
func_with_param_call.cs:
|
||||
# 5| System.Int32 test_call_with_param.f(System.Int32,System.Int32)
|
||||
# 5| Block 0
|
||||
|
||||
Reference in New Issue
Block a user