Skip to content

Commit a5ec763

Browse files
author
AndreiDiaconu1
committed
Delegate creation and call
Added support for delegate creation and call. Added a test case and updated the expected output.
1 parent 331707f commit a5ec763

File tree

6 files changed

+317
-2
lines changed

6 files changed

+317
-2
lines changed

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ private predicate ignoreExprAndDescendants(Expr expr) {
6161
// constant value.
6262
isIRConstant(getRealParent(expr))
6363
or
64+
// Ignore the local declaration done by a `ForeachStmt`
65+
// since we desugar it
66+
(
67+
expr instanceof LocalVariableDeclExpr and
68+
expr.getParent().getParent() instanceof ForeachStmt
69+
)
70+
or
6471
ignoreExprAndDescendants(getRealParent(expr)) // recursive case
6572
}
6673

@@ -208,12 +215,15 @@ newtype TTranslatedElement =
208215
exists(LocalVariableDeclAndInitExpr lvInit |
209216
lvInit.getInitializer() = expr and
210217
not expr instanceof ArrayCreation and
211-
not expr instanceof ObjectCreation
218+
not expr instanceof ObjectCreation and
219+
not expr instanceof DelegateCreation
212220
)
213221
or
214222
// Then treat more complex ones
215223
expr instanceof ObjectCreation
216224
or
225+
expr instanceof DelegateCreation
226+
or
217227
expr instanceof ArrayInitializer
218228
or
219229
expr instanceof ObjectInitializer

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ private import TranslatedInitialization
1212
private import common.TranslatedConditionBlueprint
1313
private import common.TranslatedCallBlueprint
1414
private import common.TranslatedExprBlueprint
15+
private import desugar.Delegate
16+
private import desugar.internal.TranslatedCompilerGeneratedCall
1517
import TranslatedCall
1618
private import semmle.code.csharp.ir.Util
1719
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
@@ -1879,3 +1881,47 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont
18791881
result = getTranslatedInitialization(expr.getChild(0))
18801882
}
18811883
}
1884+
1885+
/**
1886+
* The translation of a `DelegateCall`. Since this type of call needs
1887+
* desugaring, we treat it as a special case. The AST node of the
1888+
* call expression will be the parent to a compiler generated call.
1889+
*/
1890+
class TranslatedDelegateCall extends TranslatedNonConstantExpr {
1891+
override DelegateCall expr;
1892+
1893+
override final Instruction getFirstInstruction() {
1894+
result = getInovkeCall().getFirstInstruction()
1895+
}
1896+
1897+
override final TranslatedElement getChild(int id) {
1898+
id = 0 and result = getInovkeCall()
1899+
}
1900+
1901+
override Instruction getResult() {
1902+
result = getInovkeCall().getResult()
1903+
}
1904+
1905+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
1906+
none()
1907+
}
1908+
1909+
override Instruction getChildSuccessor(TranslatedElement child) {
1910+
child = getInovkeCall() and
1911+
result = getParent().getChildSuccessor(this)
1912+
}
1913+
1914+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType,
1915+
boolean isLValue) {
1916+
none()
1917+
}
1918+
1919+
override Instruction getInstructionOperand(InstructionTag tag,
1920+
OperandTag operandTag) {
1921+
none()
1922+
}
1923+
1924+
private TranslatedCompilerGeneratedCall getInovkeCall() {
1925+
result = DelegateElements::getInvoke(expr)
1926+
}
1927+
}

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

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import TranslatedExpr
1212
private import TranslatedFunction
1313
private import semmle.code.csharp.ir.Util
1414
private import IRInternal
15+
private import desugar.Delegate
1516

1617
/**
1718
* Gets the `TranslatedInitialization` for the expression `expr`.
@@ -125,9 +126,10 @@ class TranslatedDirectInitialization extends TranslatedInitialization {
125126
// TODO: Make sure this is complete and correct
126127
not expr instanceof ArrayInitializer and
127128
not expr instanceof ObjectInitializer and
129+
not expr instanceof DelegateCreation and
128130
not expr instanceof CollectionInitializer and
129131
not expr instanceof ObjectCreation and
130-
not expr instanceof StringLiteral
132+
not expr instanceof StringLiteral
131133
}
132134

133135
override TranslatedElement getChild(int id) { id = 0 and result = this.getInitializer() }
@@ -469,3 +471,75 @@ class TranslatedConstructorInitializer extends TranslatedConstructorCallFromCons
469471
derivedClass = this.getFunction().getDeclaringType()
470472
}
471473
}
474+
475+
/**
476+
* Represents the IR translation of a delegate creation.
477+
*/
478+
class TranslatedDelegateCreation extends TranslatedInitialization, ConstructorCallContext {
479+
override DelegateCreation expr;
480+
481+
override TranslatedElement getChild(int id) {
482+
id = 0 and result = getConstructorCall()
483+
}
484+
485+
override predicate hasInstruction(
486+
Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue) {
487+
(
488+
tag = NewObjTag() and
489+
opcode instanceof Opcode::NewObj and
490+
resultType = expr.getType() and
491+
isLValue = false
492+
)
493+
or
494+
(
495+
tag = InitializerStoreTag() and
496+
opcode instanceof Opcode::Store and
497+
resultType = expr.getType() and
498+
isLValue = false
499+
)
500+
}
501+
502+
override final Instruction getFirstInstruction() {
503+
result = getInstruction(NewObjTag())
504+
}
505+
506+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
507+
(
508+
tag = NewObjTag() and
509+
result = getConstructorCall().getFirstInstruction() and
510+
kind instanceof GotoEdge
511+
) or
512+
(
513+
tag = InitializerStoreTag() and
514+
result = getParent().getChildSuccessor(this) and
515+
kind instanceof GotoEdge
516+
)
517+
}
518+
519+
override Instruction getChildSuccessor(TranslatedElement child) {
520+
child = getConstructorCall() and
521+
result = getInstruction(InitializerStoreTag())
522+
}
523+
524+
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
525+
tag = InitializerStoreTag() and
526+
(
527+
(
528+
operandTag instanceof AddressOperandTag and
529+
result = getParent().(InitializationContext).getTargetAddress()
530+
) or
531+
(
532+
operandTag instanceof StoreValueOperandTag and
533+
result = getInstruction(NewObjTag())
534+
)
535+
)
536+
}
537+
538+
TranslatedElement getConstructorCall() {
539+
result = DelegateElements::getConstructor(expr)
540+
}
541+
542+
override Instruction getReceiver() {
543+
result = getInstruction(NewObjTag())
544+
}
545+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* File that translates the desugaring of delegate creation and call expressions.
3+
* In particular, in the IR we explicitly allocate a new object and call the delegate's constructor when
4+
* creating a new one.
5+
* For the delegate call, we explicitly call the `Invoke` method.
6+
* More information about the internals:
7+
* https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_DelegateCreationExpression.cs
8+
* This is a rough approximation which will need further refining.
9+
*/
10+
11+
import csharp
12+
private import semmle.code.csharp.ir.implementation.Opcode
13+
private import semmle.code.csharp.ir.implementation.internal.OperandTag
14+
private import semmle.code.csharp.ir.internal.TempVariableTag
15+
private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag
16+
17+
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr
18+
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement
19+
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedStmt
20+
private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedCondition
21+
22+
private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language
23+
private import Common
24+
private import internal.TranslatedCompilerGeneratedCall
25+
private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedExprBlueprint
26+
27+
/**
28+
* Module that exposes the functions needed for the translation of the delegate creation and call expressions.
29+
*/
30+
module DelegateElements {
31+
TranslatedDelegateConstructorCall getConstructor(DelegateCreation generatedBy) {
32+
exists(TranslatedDelegateConstructorCall cons |
33+
cons.getAST() = generatedBy and
34+
result = cons
35+
)
36+
}
37+
38+
TranslatedDelegateInvokeCall getInvoke(DelegateCall generatedBy) {
39+
exists(TranslatedDelegateInvokeCall invoke |
40+
invoke.getAST() = generatedBy and
41+
result = invoke
42+
)
43+
}
44+
}
45+
46+
/**
47+
* The translation of the constructor call that happens as part of the delegate creation.
48+
*/
49+
private class TranslatedDelegateConstructorCall extends TranslatedCompilerGeneratedCall,
50+
TTranslatedCompilerGeneratedElement {
51+
override DelegateCreation generatedBy;
52+
53+
TranslatedDelegateConstructorCall() {
54+
this = TTranslatedCompilerGeneratedElement(generatedBy, 0)
55+
}
56+
57+
final override Type getCallResultType() {
58+
result instanceof VoidType
59+
}
60+
61+
override predicate hasArguments() {
62+
any()
63+
}
64+
65+
override TranslatedExpr getArgument(int index) {
66+
index = 0 and result = getTranslatedExpr(generatedBy.getArgument())
67+
}
68+
69+
override TranslatedExprBlueprint getQualifier() {
70+
none()
71+
}
72+
73+
override Instruction getQualifierResult() {
74+
exists(ConstructorCallContext context |
75+
context = getParent() and
76+
result = context.getReceiver()
77+
)
78+
}
79+
80+
override Callable getInstructionFunction(InstructionTag tag) {
81+
tag = CallTargetTag() and
82+
exists(Callable internal |
83+
internal.getName() = generatedBy.getDelegateType().getName() and
84+
internal.isCompilerGenerated() and
85+
internal.getFile() = generatedBy.getFile() and
86+
result = internal
87+
)
88+
}
89+
}
90+
91+
/**
92+
* The translation of the invoke call that happens as part of the desugaring of the delegate call.
93+
*/
94+
private class TranslatedDelegateInvokeCall extends TranslatedCompilerGeneratedCall,
95+
TTranslatedCompilerGeneratedElement{
96+
override DelegateCall generatedBy;
97+
98+
TranslatedDelegateInvokeCall() {
99+
this = TTranslatedCompilerGeneratedElement(generatedBy, 1)
100+
}
101+
102+
final override Type getCallResultType() {
103+
result instanceof VoidType
104+
}
105+
106+
override Callable getInstructionFunction(InstructionTag tag) {
107+
tag = CallTargetTag() and
108+
exists(Callable internal |
109+
internal.getName() = "Invoke" and
110+
internal.isCompilerGenerated() and
111+
internal.getFile() = generatedBy.getFile() and
112+
result = internal
113+
)
114+
}
115+
116+
override TranslatedExprBlueprint getQualifier() {
117+
result = getTranslatedExpr(generatedBy.getDelegateExpr())
118+
}
119+
120+
override Instruction getQualifierResult() {
121+
result = getQualifier().getResult()
122+
}
123+
124+
override predicate hasArguments() {
125+
any()
126+
}
127+
128+
override TranslatedExpr getArgument(int index) {
129+
result = getTranslatedExpr(generatedBy.getArgument(index))
130+
}
131+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System.Collections.Generic;
2+
3+
public class Delegates {
4+
delegate int Del(int num);
5+
6+
static int returns(int ret)
7+
{
8+
return ret;
9+
}
10+
11+
public static void Main() {
12+
Del del1 = new Del(returns);
13+
del1(5);
14+
}
15+
}

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,45 @@ crement.cs:
315315
# 3| v0_35(Void) = UnmodeledUse : mu*
316316
# 3| v0_36(Void) = ExitFunction :
317317

318+
delegates.cs:
319+
# 6| System.Int32 Delegates.returns(System.Int32)
320+
# 6| Block 0
321+
# 6| v0_0(Void) = EnterFunction :
322+
# 6| mu0_1(null) = AliasedDefinition :
323+
# 6| mu0_2(null) = UnmodeledDefinition :
324+
# 6| r0_3(glval<Int32>) = VariableAddress[ret] :
325+
# 6| mu0_4(Int32) = InitializeParameter[ret] : &:r0_3
326+
# 8| r0_5(glval<Int32>) = VariableAddress[#return] :
327+
# 8| r0_6(glval<Int32>) = VariableAddress[ret] :
328+
# 8| r0_7(Int32) = Load : &:r0_6, ~mu0_2
329+
# 8| mu0_8(Int32) = Store : &:r0_5, r0_7
330+
# 6| r0_9(glval<Int32>) = VariableAddress[#return] :
331+
# 6| v0_10(Void) = ReturnValue : &:r0_9, ~mu0_2
332+
# 6| v0_11(Void) = UnmodeledUse : mu*
333+
# 6| v0_12(Void) = ExitFunction :
334+
335+
# 11| System.Void Delegates.Main()
336+
# 11| Block 0
337+
# 11| v0_0(Void) = EnterFunction :
338+
# 11| mu0_1(null) = AliasedDefinition :
339+
# 11| mu0_2(null) = UnmodeledDefinition :
340+
# 12| r0_3(glval<Del>) = VariableAddress[del1] :
341+
# 12| r0_4(Del) = NewObj :
342+
# 12| r0_5(glval<null>) = FunctionAddress[Del] :
343+
# 12| r0_6(glval<Del>) = FunctionAddress[returns] :
344+
# 12| v0_7(Void) = Call : func:r0_5, this:r0_4, 0:r0_6
345+
# 12| mu0_8(null) = ^CallSideEffect : ~mu0_2
346+
# 12| mu0_9(Del) = Store : &:r0_3, r0_4
347+
# 13| r0_10(glval<Del>) = VariableAddress[del1] :
348+
# 13| r0_11(Del) = Load : &:r0_10, ~mu0_2
349+
# 13| r0_12(glval<null>) = FunctionAddress[Invoke] :
350+
# 13| r0_13(Int32) = Constant[5] :
351+
# 13| v0_14(Void) = Call : func:r0_12, this:r0_11, 0:r0_13
352+
# 13| mu0_15(null) = ^CallSideEffect : ~mu0_2
353+
# 11| v0_16(Void) = ReturnVoid :
354+
# 11| v0_17(Void) = UnmodeledUse : mu*
355+
# 11| v0_18(Void) = ExitFunction :
356+
318357
func_with_param_call.cs:
319358
# 5| System.Int32 test_call_with_param.f(System.Int32,System.Int32)
320359
# 5| Block 0

0 commit comments

Comments
 (0)