|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
public class CompoundAssignmentForDelegate : EmitMetadataTestBase
{
// The method to removal or concatenation with 'optional' parameter
[Fact]
public void OptionalParaInCompAssignOperator()
{
var text =
@"
delegate void MyDelegate1(int x, float y);
class C
{
public void DelegatedMethod(int x, float y = 3.0f) { System.Console.WriteLine(y); }
static void Main(string[] args)
{
C mc = new C();
MyDelegate1 md1 = null;
md1 += mc.DelegatedMethod;
md1(1, 5);
md1 -= mc.DelegatedMethod;
}
}
";
string expectedIL = @"{
// Code size 65 (0x41)
.maxstack 4
.locals init (C V_0) //mc
IL_0000: newobj ""C..ctor()""
IL_0005: stloc.0
IL_0006: ldnull
IL_0007: ldloc.0
IL_0008: ldftn ""void C.DelegatedMethod(int, float)""
IL_000e: newobj ""MyDelegate1..ctor(object, System.IntPtr)""
IL_0013: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
IL_0018: castclass ""MyDelegate1""
IL_001d: dup
IL_001e: ldc.i4.1
IL_001f: ldc.r4 5
IL_0024: callvirt ""void MyDelegate1.Invoke(int, float)""
IL_0029: ldloc.0
IL_002a: ldftn ""void C.DelegatedMethod(int, float)""
IL_0030: newobj ""MyDelegate1..ctor(object, System.IntPtr)""
IL_0035: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
IL_003a: castclass ""MyDelegate1""
IL_003f: pop
IL_0040: ret
}
";
//var tree = SyntaxTree.ParseCompilationUnit(text);
//var type = from item in ((CompilationUnitSyntax)tree.Root).Members where item as TypeDeclarationSyntax != null select item as TypeDeclarationSyntax;
//var cla = type.First() as TypeDeclarationSyntax;
//var method = from item in cla.Members where (MethodDeclarationSyntax)item != null select item as MethodDeclarationSyntax ;
//var block = method.Last().Body;
//var statement = block.Statements;
CompileAndVerify(text, expectedOutput: "5").VerifyIL("C.Main", expectedIL);
}
// The object to removal or concatenation could be create a new instance of a method or a method name
[Fact]
public void ObjectOfCompAssignOperator()
{
var text =
@"
delegate void boo();
public class abc
{
public void bar() { System.Console.WriteLine(""bar""); }
}
class C
{
static void Main(string[] args)
{
abc p = new abc();
boo goo = null;
goo += p.bar;
goo += new boo(p.bar);
goo();
goo -= p.bar;
goo -= new boo(p.bar);
}
}
";
var expectedOutput = @"bar
bar";
CompileAndVerify(text, expectedOutput: expectedOutput);
}
// The object to removal or concatenation could be null
[Fact]
public void ObjectOfCompAssignOperatorIsNull()
{
var text =
@"
delegate void boo();
class C
{
static void Main(string[] args)
{
boo goo = null;
goo += (boo)null;
goo -= (boo)null;
goo += null;
goo -= null;
}
}
";
var expectedIL = @"{
// Code size 47 (0x2f)
.maxstack 2
IL_0000: ldnull
IL_0001: ldnull
IL_0002: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
IL_0007: castclass ""boo""
IL_000c: ldnull
IL_000d: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
IL_0012: castclass ""boo""
IL_0017: ldnull
IL_0018: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
IL_001d: castclass ""boo""
IL_0022: ldnull
IL_0023: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
IL_0028: castclass ""boo""
IL_002d: pop
IL_002e: ret
}";
CompileAndVerify(text).VerifyIL("C.Main", expectedIL);
}
// The object to removal or concatenation could be an object of delegate
[Fact]
public void ObjectOfCompAssignOperatorIsObjectOfDelegate()
{
var text =
@"
using System;
delegate void boo();
public class abc
{
public void bar() { System.Console.WriteLine(""bar""); }
static public void far() { System.Console.WriteLine(""far""); }
}
class C
{
static void Main(string[] args)
{
abc p = new abc();
boo goo = null;
boo goo1 = new boo(abc.far);
goo += goo1; // Same type
goo();
goo -= goo1; // Same type
boo[] arrfoo = { p.bar, abc.far };
goo += (boo)Delegate.Combine(arrfoo); // OK
goo += (boo)Delegate.Combine(goo, goo1); // OK
goo();
}
}
";
var expectedOutput = @"
far
bar
far
bar
far
far";
CompileAndVerify(text, expectedOutput: expectedOutput);
}
[Fact]
public void AnonymousMethodToRemovalOrConcatenation()
{
var text = @"
using System;
delegate void boo(int x);
class C
{
static void Main()
{
boo goo = null;
goo += delegate (int x)
{
System.Console.WriteLine(x);
};
goo(10);
Delegate[] del = goo.GetInvocationList();
goo -= (boo)del[0];
}
}
";
CompileAndVerify(text, expectedOutput: "10").VerifyIL("C.Main", @"
{
// Code size 77 (0x4d)
.maxstack 3
.locals init (System.Delegate[] V_0) //del
IL_0000: ldnull
IL_0001: ldsfld ""boo C.<>c.<>9__0_0""
IL_0006: dup
IL_0007: brtrue.s IL_0020
IL_0009: pop
IL_000a: ldsfld ""C.<>c C.<>c.<>9""
IL_000f: ldftn ""void C.<>c.<Main>b__0_0(int)""
IL_0015: newobj ""boo..ctor(object, System.IntPtr)""
IL_001a: dup
IL_001b: stsfld ""boo C.<>c.<>9__0_0""
IL_0020: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
IL_0025: castclass ""boo""
IL_002a: dup
IL_002b: ldc.i4.s 10
IL_002d: callvirt ""void boo.Invoke(int)""
IL_0032: dup
IL_0033: callvirt ""System.Delegate[] System.Delegate.GetInvocationList()""
IL_0038: stloc.0
IL_0039: ldloc.0
IL_003a: ldc.i4.0
IL_003b: ldelem.ref
IL_003c: castclass ""boo""
IL_0041: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
IL_0046: castclass ""boo""
IL_004b: pop
IL_004c: ret
}
");
}
[Fact]
public void LambdaMethodToRemovalOrConcatenation()
{
var text = @"
using System;
delegate void boo(string x);
class C
{
static void Main()
{
boo goo = null;
goo += (x) =>
{
System.Console.WriteLine(x);
};
goo(""Hello"");
Delegate[] del = goo.GetInvocationList();
goo -= (boo)del[0];
}
}
";
CompileAndVerify(text, expectedOutput: "Hello").VerifyIL("C.Main()", @"
{
// Code size 80 (0x50)
.maxstack 3
.locals init (System.Delegate[] V_0) //del
IL_0000: ldnull
IL_0001: ldsfld ""boo C.<>c.<>9__0_0""
IL_0006: dup
IL_0007: brtrue.s IL_0020
IL_0009: pop
IL_000a: ldsfld ""C.<>c C.<>c.<>9""
IL_000f: ldftn ""void C.<>c.<Main>b__0_0(string)""
IL_0015: newobj ""boo..ctor(object, System.IntPtr)""
IL_001a: dup
IL_001b: stsfld ""boo C.<>c.<>9__0_0""
IL_0020: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
IL_0025: castclass ""boo""
IL_002a: dup
IL_002b: ldstr ""Hello""
IL_0030: callvirt ""void boo.Invoke(string)""
IL_0035: dup
IL_0036: callvirt ""System.Delegate[] System.Delegate.GetInvocationList()""
IL_003b: stloc.0
IL_003c: ldloc.0
IL_003d: ldc.i4.0
IL_003e: ldelem.ref
IL_003f: castclass ""boo""
IL_0044: call ""System.Delegate System.Delegate.Remove(System.Delegate, System.Delegate)""
IL_0049: castclass ""boo""
IL_004e: pop
IL_004f: ret
}
");
}
// Mixed named method and Lambda expression to removal or concatenation
[Fact]
public void MixedNamedMethodAndLambdaToRemovalOrConcatenation()
{
var text =
@"
using System;
delegate void boo(int x);
class C
{
static public void far(int x) { Console.WriteLine(""far:{0}"", x); }
static void Main(string[] args)
{
boo goo = far;
goo += (x) =>
System.Console.WriteLine(""lambda:{0}"", x);
goo(10);
Delegate[] del = goo.GetInvocationList();
goo -= (boo)del[0];
goo(20);
}
}
";
var expectedOutPut = @"far:10
lambda:10
lambda:20";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Mixed named method and Anonymous method to removal or concatenation
[Fact]
public void MixedNamedMethodAndAnonymousToRemovalOrConcatenation()
{
var text =
@"
using System;
delegate void boo(int x);
class C
{
static public void far(int x) { Console.WriteLine(""far:{0}"", x); }
static void Main(string[] args)
{
boo goo = far;
goo += delegate(int x)
{
System.Console.WriteLine(""Anonymous:{0}"", x);
};
goo(10);
Delegate[] del = goo.GetInvocationList();
goo -= (boo)del[0];
goo(20);
}
}
";
var expectedOutPut = @"far:10
Anonymous:10
Anonymous:20";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Mixed Lambda expression and Anonymous method to removal or concatenation
[Fact]
public void MixedAnonymousAndLambdaToRemovalOrConcatenation()
{
var text =
@"
using System;
delegate void boo(int x);
class C
{
static public void far(int x) { Console.WriteLine(""far:{0}"", x); }
static void Main(string[] args)
{
boo goo = far;
goo += x =>
{
System.Console.WriteLine(""Lambda:{0}"", x);
};
goo += delegate(int x)
{
System.Console.WriteLine(""Anonymous:{0}"", x);
};
goo(10);
Delegate[] del = goo.GetInvocationList();
goo -= (boo)del[0];
goo(20);
}
}
";
var expectedOutPut = @"far:10
Lambda:10
Anonymous:10
Lambda:20
Anonymous:20";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// To removal or concatenation same method multi- times
[Fact]
public void RemoveSameMethodMultiTime()
{
var text =
@"
using System;
delegate void D(ref int x);
class C
{
public static void M1(ref int i)
{
Console.WriteLine(""M1: "" + i);
i = 1;
}
public static void M2(ref int i)
{
Console.WriteLine(""M2: "" + i);
i = 2;
}
static void Main(string[] args)
{
int i = 0;
D cd1 = new D(M1); // M1
D cd2 = cd1;
cd1 += M2; // M1,M2
cd1 += M1; // M1,M2,M1
cd1(ref i);
cd1 -= cd2;// remove last M1
cd1(ref i);
cd1 -= M1; // remove first M1
cd1(ref i);
}
}
";
var expectedOutPut = @"M1: 0
M2: 1
M1: 2
M1: 1
M2: 1
M2: 2
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Remove Non existed method
[Fact]
public void RemoveNotExistMethod()
{
var text =
@"
using System;
delegate void D(ref int x);
class C
{
public static void M1(ref int i)
{
Console.WriteLine(""M1: "" + i);
i = 1;
}
public static void M2(ref int i)
{
Console.WriteLine(""M2: "" + i);
i = 2;
}
static void Main(string[] args)
{
int i = 0;
D cd1 = new D(M1); // M1
cd1 -= M2; // M1
cd1 -= M2; // M1
cd1(ref i);
}
}
";
var expectedOutPut = @"M1: 0";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Removal and concatenation works on both static and instance methods
[Fact]
public void RemoveBothStaticAndInstanceMethod()
{
var text =
@"
delegate void boo();
public class abc
{
public void bar() { System.Console.WriteLine(""bar""); }
static public void far() { System.Console.WriteLine(""far""); }
}
class C
{
static void Main(string[] args)
{
abc p = new abc();
boo goo = new boo(p.bar);
goo();
goo -= p.bar;
goo = new boo(abc.far);
goo();
goo -= abc.far;
goo += p.bar;
goo += abc.far;
goo();
}
}
";
var expectedOutPut = @"bar
far
bar
far";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Removal or concatenation for the delegate that is member of classes
[Fact]
public void RemoveDelegateIsMemberOfClass()
{
var text =
@"
public delegate void boo();
public class abc
{
public void bar() { System.Console.WriteLine(""bar""); }
static public void far() { System.Console.WriteLine(""far""); }
public boo goo = null;
}
class C
{
static void Main(string[] args)
{
abc p = new abc();
p.goo = null;
p.goo += abc.far;
p.goo += p.bar;
p.goo();
p.goo -= abc.far;
p.goo -= p.bar;
}
}
";
var expectedOutPut = @"far
bar
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Removal or concatenation for the delegate works on ternary operator
[Fact]
public void CompAssignWorksOnTernaryOperator()
{
var text =
@"
delegate void boo();
public class abc
{
public void bar() { System.Console.WriteLine(""bar""); }
static public void far() { System.Console.WriteLine(""far""); }
}
class C
{
static void Main(string[] args)
{
abc p = new abc();
boo goo = null;
goo += loo() ? new boo(p.bar) : new boo(abc.far);
goo();
goo -= loo() ? new boo(p.bar) : new boo(abc.far);
boo left = null;
boo right = null;
goo = !loo() ? left += new boo(abc.far) : right += new boo(p.bar);
goo();
goo = !loo() ? left -= new boo(abc.far) : right -= new boo(p.bar);
}
private static bool loo()
{
return true;
}
}
";
var expectedOutPut = @"bar
bar
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Removal or concatenation for the delegate that with 9 args
[Fact]
public void DelegateWithNineArgs()
{
var text =
@"
delegate void boo(out int i, double d, ref float f, string s, char c, decimal dc, C client, byte b, short sh);
class C
{
public static void Hello(out int i, double d, ref float f, string s, char c, decimal dc, C client, byte b, short sh)
{
i = 1;
System.Console.WriteLine(""Hello"");
}
static void Main(string[] args)
{
boo goo = null;
goo += new boo(C.Hello);
int i = 1;
float ff = 0;
goo(out i, 5.5, ref ff, ""a string"", 'C', 0.555m, new C(), 3, 16);
}
}
";
var expectedOutPut = @"Hello
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Removal or concatenation for the delegate that is virtual struct methods
[WorkItem(539908, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539908")]
[Fact]
public void DelegateWithStructMethods()
{
var text =
@"
delegate int boo();
interface I
{
int bar();
}
public struct abc : I
{
public int bar() { System.Console.WriteLine(""bar""); return 0x01; }
}
class C
{
static void Main(string[] args)
{
abc p = new abc();
boo goo = null;
goo += new boo(p.bar);
goo();
}
}
";
var expectedOutPut = @"bar
";
var expectedIL = @"
{
// Code size 44 (0x2c)
.maxstack 3
.locals init (abc V_0) //p
IL_0000: ldloca.s V_0
IL_0002: initobj ""abc""
IL_0008: ldnull
IL_0009: ldloc.0
IL_000a: box ""abc""
IL_000f: dup
IL_0010: ldvirtftn ""int abc.bar()""
IL_0016: newobj ""boo..ctor(object, System.IntPtr)""
IL_001b: call ""System.Delegate System.Delegate.Combine(System.Delegate, System.Delegate)""
IL_0020: castclass ""boo""
IL_0025: callvirt ""int boo.Invoke()""
IL_002a: pop
IL_002b: ret
}
";
CompileAndVerify(text, expectedOutput: expectedOutPut).VerifyIL("C.Main", expectedIL);
}
// Removal or concatenation for the delegate that the method is in base class
[Fact]
public void AddMethodThatInBaseClass()
{
var text =
@"
delegate double MyDelegate(int integerPortion, float fraction);
public class BaseClass
{
public delegate void MyDelegate();
public void DelegatedMethod()
{
System.Console.WriteLine(""Base"");
}
}
public class DerivedClass : BaseClass
{
new public delegate void MyDelegate();
public new void DelegatedMethod()
{
System.Console.WriteLine(""Derived"");
}
static void Main(string[] args)
{
DerivedClass derived = new DerivedClass();
BaseClass baseCls = new BaseClass();
MyDelegate derivedDel = new MyDelegate(derived.DelegatedMethod);
derivedDel += baseCls.DelegatedMethod;
derivedDel();
BaseClass.MyDelegate BaseDel = new BaseClass.MyDelegate(((BaseClass)derived).DelegatedMethod);
BaseDel += derived.DelegatedMethod;
BaseDel();
}
}
";
var expectedOutPut = @"Derived
Base
Base
Derived
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// delegate-in-a-generic-class (C<t>.goo(…)) += methodgroup-in-a-generic-class (C<T>.bar(…))
[Fact]
public void CompAssignOperatorForGenericClass()
{
var text =
@"
delegate void boo(short x);
class C<T>
{
public void bar(short x) { System.Console.WriteLine(""bar""); }
public static void far(T x) { System.Console.WriteLine(""far""); }
public static void par<U>(U x) { System.Console.WriteLine(""par""); }
public static boo goo = null;
}
class D
{
static void Main(string[] args)
{
C<long> p = new C<long>();
C<long>.goo += p.bar;
C<short>.goo += C<short>.far;
C<long>.goo += C<long>.par<short>;
C<long>.goo(short.MaxValue);
C<short>.goo(short.MaxValue);
}
}
";
var expectedOutPut = @"bar
par
far
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Compound assignment for the method with derived return type
[Fact]
public void CompAssignOperatorForInherit01()
{
var text =
@"
delegate BaseClass MyBaseDelegate(BaseClass x);
delegate DerivedClass MyDerivedDelegate(DerivedClass x);
public class BaseClass
{
public static BaseClass DelegatedMethod(BaseClass x)
{
System.Console.WriteLine(""Base"");
return x;
}
}
public class DerivedClass : BaseClass
{
public static DerivedClass DelegatedMethod(DerivedClass x)
{
System.Console.WriteLine(""Derived"");
return x;
}
static void Main(string[] args)
{
MyBaseDelegate goo = null;
goo += BaseClass.DelegatedMethod;
goo += DerivedClass.DelegatedMethod;
goo(new BaseClass());
goo(new DerivedClass());
MyDerivedDelegate goo1 = null;
//goo1 += BaseClass.DelegatedMethod;
goo1 += DerivedClass.DelegatedMethod;
goo1(new DerivedClass());
}
}
";
var expectedOutPut = @"Base
Base
Base
Base
Derived
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Compound assignment for the method with derived return type
[Fact]
public void CompAssignOperatorForInherit02()
{
var text =
@"
delegate T MyDelegate<T>(T x);
public class BaseClass
{
public static BaseClass DelegatedMethod(BaseClass x)
{
System.Console.WriteLine(""Base1"");
return x;
}
public static DerivedClass DelegatedMethod(DerivedClass x)
{
System.Console.WriteLine(""Base2"");
return x;
}
}
public class DerivedClass : BaseClass
{
public static new DerivedClass DelegatedMethod(DerivedClass x)
{
System.Console.WriteLine(""Derived1"");
return x;
}
public static new BaseClass DelegatedMethod(BaseClass x)
{
System.Console.WriteLine(""Derived2"");
return x;
}
static void Main(string[] args)
{
MyDelegate<BaseClass> goo = null;
goo += BaseClass.DelegatedMethod;
goo += DerivedClass.DelegatedMethod;
goo(new BaseClass());
goo(new DerivedClass());
MyDelegate<DerivedClass> goo1 = null;
goo1 += BaseClass.DelegatedMethod;
goo1 += DerivedClass.DelegatedMethod;
//goo1(new BaseClass());
goo1(new DerivedClass());
}
}
";
var expectedOutPut = @"Base1
Derived2
Base1
Derived2
Base2
Derived1
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Compound assignment for the method with derived return type
[WorkItem(539927, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539927")]
[Fact]
public void CompAssignOperatorForInherit03()
{
var text =
@"
delegate T MyDelegate<T>(T x);
public class BaseClass
{
public static T DelegatedMethod<T>(T x)
{
System.Console.WriteLine(""Base"");
return x;
}
public static double DelegatedMethod(double x)
{
System.Console.WriteLine(""double"");
return x;
}
}
public class DerivedClass : BaseClass
{
public static new T DelegatedMethod<T>(T x)
{
System.Console.WriteLine(""Derived"");
return x;
}
static void Main(string[] args)
{
MyDelegate<BaseClass> goo = null;
goo += BaseClass.DelegatedMethod;
goo += DerivedClass.DelegatedMethod;
goo(new BaseClass());
goo(new DerivedClass());
MyDelegate<DerivedClass> goo1 = null;
goo1 += BaseClass.DelegatedMethod;
goo1 += DerivedClass.DelegatedMethod;
//goo1(new BaseClass());
goo1(new DerivedClass());
MyDelegate<double> goo2 = null;
goo2 += BaseClass.DelegatedMethod<double>;
goo2 += BaseClass.DelegatedMethod;
goo2 += DerivedClass.DelegatedMethod;
goo2(2);
}
}
";
var expectedOutPut = @"Base
Derived
Base
Derived
Base
Derived
Base
double
Derived
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
// Compound assignment for the method with derived return type
[WorkItem(539927, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539927")]
[Fact]
public void CompAssignOperatorForInherit04()
{
var text =
@"
delegate double MyDelegate(double x);
public class BaseClass
{
public static T DelegatedMethod<T>(T x)
{
System.Console.WriteLine(""Base"");
return x;
}
public static double DelegatedMethod(double x)
{
System.Console.WriteLine(""double"");
return x;
}
}
public class DerivedClass : BaseClass
{
public static new T DelegatedMethod<T>(T x)
{
System.Console.WriteLine(""Derived"");
return x;
}
static void Main(string[] args)
{
MyDelegate goo = null;
goo += BaseClass.DelegatedMethod<double>;
goo += BaseClass.DelegatedMethod;
goo += DerivedClass.DelegatedMethod;
MyDelegate goo1 = null;
goo1 += goo;
goo += goo1;
goo(1);
goo1(1);
}
}
";
var expectedOutPut = @"Base
double
Derived
Base
double
Derived
Base
double
Derived
";
CompileAndVerify(text, expectedOutput: expectedOutPut);
}
}
}
|