Skip to content

Commit 241a40c

Browse files
author
AndreiDiaconu1
committed
C# IR: Initializers
Add support for collection initializers. Instead of using `AssignExpr` for the translation of object initializers, `MemberInitializer` is now used.
1 parent 0528d8e commit 241a40c

File tree

4 files changed

+118
-7
lines changed

4 files changed

+118
-7
lines changed

csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCall.qll

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import semmle.code.csharp.ir.implementation.internal.OperandTag
44
private import InstructionTag
55
private import TranslatedElement
66
private import TranslatedExpr
7+
private import TranslatedInitialization
78
private import semmle.code.csharp.ir.Util
89
private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedCallBase
910
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
@@ -50,7 +51,13 @@ class TranslatedFunctionCall extends TranslatedNonConstantExpr, TranslatedCall {
5051
result = getTranslatedExpr(expr.(QualifiableExpr).getQualifier())
5152
}
5253

53-
override Instruction getQualifierResult() { result = this.getQualifier().getResult() }
54+
override Instruction getQualifierResult() {
55+
// since `ElementInitializer`s do not have a qualifier, the qualifier's result is retrieved
56+
// from the enclosing initialization context
57+
if expr.getParent() instanceof CollectionInitializer
58+
then result = getTranslatedExpr(expr.getParent()).(InitializationContext).getTargetAddress()
59+
else result = this.getQualifier().getResult()
60+
}
5461

5562
override Type getCallResultType() { result = expr.getTarget().getReturnType() }
5663

csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -485,13 +485,48 @@ class TranslatedObjectInitializerExpr extends TranslatedNonConstantExpr, Initial
485485
}
486486

487487
override TranslatedElement getChild(int id) {
488-
exists(AssignExpr assign |
489-
result = getTranslatedExpr(expr.getChild(id)) and
490-
expr.getAChild() = assign and
491-
assign.getIndex() = id
488+
result = getTranslatedExpr(expr.getMemberInitializer(id))
489+
}
490+
491+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
492+
493+
override Instruction getChildSuccessor(TranslatedElement child) {
494+
exists(int index |
495+
child = this.getChild(index) and
496+
if exists(this.getChild(index + 1))
497+
then result = this.getChild(index + 1).getFirstInstruction()
498+
else result = this.getParent().getChildSuccessor(this)
492499
)
493500
}
494501

502+
override Instruction getTargetAddress() {
503+
// The target address is the address of the newly allocated object,
504+
// which can be retrieved from the parent `TranslatedObjectCreation`.
505+
result = this.getParent().getInstruction(NewObjTag())
506+
}
507+
508+
override Type getTargetType() {
509+
result = this.getParent().getInstruction(NewObjTag()).getResultType()
510+
}
511+
}
512+
513+
class TranslatedCollectionInitializer extends TranslatedNonConstantExpr, InitializationContext {
514+
override CollectionInitializer expr;
515+
516+
override Instruction getResult() { none() }
517+
518+
override Instruction getFirstInstruction() { result = this.getChild(0).getFirstInstruction() }
519+
520+
override predicate hasInstruction(
521+
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue
522+
) {
523+
none()
524+
}
525+
526+
override TranslatedElement getChild(int id) {
527+
result = getTranslatedExpr(expr.getElementInitializer(id))
528+
}
529+
495530
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
496531

497532
override Instruction getChildSuccessor(TranslatedElement child) {
@@ -503,9 +538,15 @@ class TranslatedObjectInitializerExpr extends TranslatedNonConstantExpr, Initial
503538
)
504539
}
505540

506-
override Instruction getTargetAddress() { result = this.getParent().getInstruction(NewObjTag()) }
541+
override Instruction getTargetAddress() {
542+
// The target address is the address of the newly allocated object,
543+
// which can be retrieved from the parent `TranslatedObjectCreation`.
544+
result = this.getParent().getInstruction(NewObjTag())
545+
}
507546

508-
override Type getTargetType() { none() }
547+
override Type getTargetType() {
548+
result = this.getParent().getInstruction(NewObjTag()).getResultType()
549+
}
509550
}
510551

511552
/**
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.Collections.Generic;
2+
3+
public class Collections
4+
{
5+
class MyClass
6+
{
7+
public string a;
8+
public string b;
9+
}
10+
11+
public static void Main()
12+
{
13+
var dict = new Dictionary<int, MyClass>()
14+
{
15+
{ 0, new MyClass { a="Hello", b="World" } },
16+
{ 1, new MyClass { a="Foo", b="Bar" } }
17+
};
18+
}
19+
}

csharp/ql/test/library-tests/ir/ir/raw_ir.expected

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,50 @@ casts.cs:
161161
# 11| v0_20(Void) = UnmodeledUse : mu*
162162
# 11| v0_21(Void) = ExitFunction :
163163

164+
collections.cs:
165+
# 11| System.Void Collections.Main()
166+
# 11| Block 0
167+
# 11| v0_0(Void) = EnterFunction :
168+
# 11| mu0_1(null) = AliasedDefinition :
169+
# 11| mu0_2(null) = UnmodeledDefinition :
170+
# 13| r0_3(glval<Dictionary<Int32,MyClass>>) = VariableAddress[dict] :
171+
# 13| r0_4(Dictionary<Int32,MyClass>) = NewObj :
172+
# 13| r0_5(glval<null>) = FunctionAddress[Dictionary] :
173+
# 13| v0_6(Void) = Call : func:r0_5, this:r0_4
174+
# 13| mu0_7(null) = ^CallSideEffect : ~mu0_2
175+
# 15| r0_8(glval<null>) = FunctionAddress[Add] :
176+
# 15| r0_9(Int32) = Constant[0] :
177+
# 15| r0_10(MyClass) = NewObj :
178+
# 15| r0_11(glval<null>) = FunctionAddress[MyClass] :
179+
# 15| v0_12(Void) = Call : func:r0_11, this:r0_10
180+
# 15| mu0_13(null) = ^CallSideEffect : ~mu0_2
181+
# 15| r0_14(String) = StringConstant["Hello"] :
182+
# 15| r0_15(glval<String>) = FieldAddress[a] : r0_10
183+
# 15| mu0_16(String) = Store : &:r0_15, r0_14
184+
# 15| r0_17(String) = StringConstant["World"] :
185+
# 15| r0_18(glval<String>) = FieldAddress[b] : r0_10
186+
# 15| mu0_19(String) = Store : &:r0_18, r0_17
187+
# 15| v0_20(Void) = Call : func:r0_8, this:r0_4, 0:r0_9, 1:r0_10
188+
# 15| mu0_21(null) = ^CallSideEffect : ~mu0_2
189+
# 16| r0_22(glval<null>) = FunctionAddress[Add] :
190+
# 16| r0_23(Int32) = Constant[1] :
191+
# 16| r0_24(MyClass) = NewObj :
192+
# 16| r0_25(glval<null>) = FunctionAddress[MyClass] :
193+
# 16| v0_26(Void) = Call : func:r0_25, this:r0_24
194+
# 16| mu0_27(null) = ^CallSideEffect : ~mu0_2
195+
# 16| r0_28(String) = StringConstant["Foo"] :
196+
# 16| r0_29(glval<String>) = FieldAddress[a] : r0_24
197+
# 16| mu0_30(String) = Store : &:r0_29, r0_28
198+
# 16| r0_31(String) = StringConstant["Bar"] :
199+
# 16| r0_32(glval<String>) = FieldAddress[b] : r0_24
200+
# 16| mu0_33(String) = Store : &:r0_32, r0_31
201+
# 16| v0_34(Void) = Call : func:r0_22, this:r0_4, 0:r0_23, 1:r0_24
202+
# 16| mu0_35(null) = ^CallSideEffect : ~mu0_2
203+
# 13| mu0_36(Dictionary<Int32,MyClass>) = Store : &:r0_3, r0_4
204+
# 11| v0_37(Void) = ReturnVoid :
205+
# 11| v0_38(Void) = UnmodeledUse : mu*
206+
# 11| v0_39(Void) = ExitFunction :
207+
164208
constructor_init.cs:
165209
# 5| System.Void BaseClass..ctor()
166210
# 5| Block 0

0 commit comments

Comments
 (0)