From bc65d6ba751bf9c6ca05d58215d2d7f027450821 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Tue, 16 Jun 2020 10:08:13 -0700 Subject: [PATCH] Fix generic delegate and MD Address method handling (#37937) - Fix delegates to generic methods defined in the same compilation unit - Fix usage of taking the address of a multidimensional array - Pull the generics test from CoreRT into the ready to run generics test Fixes issue #31654 --- .../ReadyToRun/LocalMethodImport.cs | 4 +- .../ReadyToRun/PrecodeMethodImport.cs | 4 +- .../ReadyToRun/SignatureBuilder.cs | 3 +- src/coreclr/tests/src/readytorun/tests/generics.cs | 2438 ++++++++++++++++++++ 4 files changed, 2446 insertions(+), 3 deletions(-) diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs index cfc5b30..55f6342 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/LocalMethodImport.cs @@ -14,6 +14,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public class LocalMethodImport : DelayLoadHelperImport, IMethodNode { private readonly MethodWithGCInfo _localMethod; + private readonly MethodWithToken _method; public LocalMethodImport( NodeFactory factory, @@ -33,9 +34,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun isInstantiatingStub)) { _localMethod = localMethod; + _method = method; } - public MethodDesc Method => _localMethod.Method; + public MethodDesc Method => _method.Method; public MethodWithGCInfo MethodCodeNode => _localMethod; public override int ClassCode => 459923351; diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs index 7b691c4..d426fae 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/PrecodeMethodImport.cs @@ -15,6 +15,7 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun public class PrecodeMethodImport : PrecodeHelperImport, IMethodNode { private readonly MethodWithGCInfo _localMethod; + private readonly MethodWithToken _method; public PrecodeMethodImport( NodeFactory factory, @@ -33,9 +34,10 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun ) { _localMethod = localMethod; + _method = method; } - public MethodDesc Method => _localMethod.Method; + public MethodDesc Method => _method.Method; public MethodWithGCInfo MethodCodeNode => _localMethod; diff --git a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs index fe45742..7362dea 100644 --- a/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs +++ b/src/coreclr/src/tools/crossgen2/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/SignatureBuilder.cs @@ -442,7 +442,8 @@ namespace ILCompiler.DependencyAnalysis.ReadyToRun // Owner type is needed for type specs to instantiating stubs or generics with signature variables still present if (!method.Method.OwningType.IsDefType && - ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0 || method.Method.OwningType.ContainsSignatureVariables())) + ((flags & (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_InstantiatingStub) != 0 || method.Method.OwningType.ContainsSignatureVariables()) + || method.Method.IsArrayAddressMethod()) { flags |= (uint)ReadyToRunMethodSigFlags.READYTORUN_METHOD_SIG_OwnerType; } diff --git a/src/coreclr/tests/src/readytorun/tests/generics.cs b/src/coreclr/tests/src/readytorun/tests/generics.cs index 918db9f..53e5e7a 100644 --- a/src/coreclr/tests/src/readytorun/tests/generics.cs +++ b/src/coreclr/tests/src/readytorun/tests/generics.cs @@ -4,6 +4,7 @@ // using System; +using System.Reflection; using System.Collections.Generic; using System.Globalization; using System.Runtime.CompilerServices; @@ -31,6 +32,33 @@ class Program RunTest5(); RunTest6(); RunTest7(); + TestDictionaryDependencyTracking.Run(); + TestStaticBaseLookups.Run(); + TestInitThisClass.Run(); + TestDelegateFatFunctionPointers.Run(); + TestDelegateToCanonMethods.Run(); + TestVirtualMethodUseTracking.Run(); + TestSlotsInHierarchy.Run(); + TestDelegateVirtualMethod.Run(); + TestDelegateInterfaceMethod.Run(); + TestThreadStaticFieldAccess.Run(); + TestConstrainedMethodCalls.Run(); + TestInstantiatingUnboxingStubs.Run(); + TestNameManglingCollisionRegression.Run(); + TestSimpleGVMScenarios.Run(); + TestGvmDelegates.Run(); + TestGvmDependencies.Run(); + TestInterfaceVTableTracking.Run(); + TestClassVTableTracking.Run(); + TestReflectionInvoke.Run(); + TestFieldAccess.Run(); + TestDevirtualization.Run(); + TestGenericInlining.Run(); + TestNullableCasting.Run(); + TestVariantCasting.Run(); + TestMDArrayAddressMethod.Run(); + TestNativeLayoutGeneration.Run(); + TestByRefLikeVTables.Run(); } static void RunTest1() @@ -267,6 +295,2416 @@ class Program { return n.GetEnumerable(o); } + + /// + /// Tests that we properly track dictionary dependencies of generic methods. + /// (Getting this wrong is a linker failure.) + /// + class TestDictionaryDependencyTracking + { + static object Gen1() + { + return MakeArray>(); + } + + static object MakeArray() + { + return new T[0]; + } + + class Gen + { + public object Frob() + { + return new ValueGen(); + } + + public object Futz() + { + return Gen1>(); + } + } + + struct ValueGen + { + } + + class ClassGen + { + } + + public static void Run() + { + new Gen().Frob(); + new Gen().Futz(); + } + } + + /// + /// Tests static base access. + /// + class TestStaticBaseLookups + { + class C1 { } + class C2 { } + class C3 { } + + class GenHolder + { + public static int IntField; + public static string StringField; + } + + class GenAccessor + { + public static string Read() + { + return GenHolder.IntField.ToString() + GenHolder.StringField; + } + + public static void SetSimple(int i, string s) + { + GenHolder.IntField = i; + GenHolder.StringField = s; + } + + public static void SetComplex(int i, string s) + { + GenHolder.IntField = i; + GenHolder.StringField = s; + GenHolder.IntField = i + 1; + GenHolder.StringField = s + "`"; + } + } + + public static void Run() + { + GenAccessor.SetComplex(42, "Hello"); + GenAccessor.SetSimple(85, "World"); + + if (GenAccessor.Read() != "42Hello") + throw new Exception(); + + if (GenHolder.IntField != 43 || GenHolder.StringField != "Hello`") + throw new Exception(); + + if (GenAccessor.Read() != "85World") + throw new Exception(); + } + } + + /// + /// Tests that we can use a delegate that points to a generic method. + /// + class TestDelegateFatFunctionPointers + { + struct SmallStruct + { + public int X; + } + + struct MediumStruct + { + public int X, Y, Z, W; + } + + unsafe struct BigStruct + { + public const int Length = 128; + public fixed byte Bytes[Length]; + } + + T Generic(object o) where T : class + { + Func f = OtherGeneric; + return f(o); + } + + T OtherGeneric(object o) where T : class + { + return o as T; + } + + delegate void VoidGenericDelegate(ref T x, T val); + void VoidGeneric(ref T x, T val) + { + x = val; + } + + SmallStruct SmallStructGeneric(SmallStruct x) + { + return x; + } + + MediumStruct MediumStructGeneric(MediumStruct x) + { + return x; + } + + BigStruct BigStructGeneric(BigStruct x) + { + return x; + } + + public static void Run() + { + var o = new TestDelegateFatFunctionPointers(); + + string hw = "Hello World"; + string roundtrip = o.Generic(hw); + if (roundtrip != hw) + throw new Exception(); + + { + VoidGenericDelegate f = o.VoidGeneric; + object obj = new object(); + object location = null; + f(ref location, obj); + if (location != obj) + throw new Exception(); + } + + { + Func f = o.SmallStructGeneric; + SmallStruct x = new SmallStruct { X = 12345 }; + SmallStruct result = f(x); + if (result.X != x.X) + throw new Exception(); + } + + { + Func f = o.MediumStructGeneric; + MediumStruct x = new MediumStruct { X = 12, Y = 34, Z = 56, W = 78 }; + MediumStruct result = f(x); + if (result.X != x.X || result.Y != x.Y || result.Z != x.Z || result.W != x.W) + throw new Exception(); + } + + unsafe + { + Func f = o.BigStructGeneric; + BigStruct x = new BigStruct(); + for (int i = 0; i < BigStruct.Length; i++) + x.Bytes[i] = (byte)(i * 2); + + BigStruct result = f(x); + + for (int i = 0; i < BigStruct.Length; i++) + if (x.Bytes[i] != result.Bytes[i]) + throw new Exception(); + } + } + } + + class TestDelegateToCanonMethods + { + class Foo + { + public readonly int Value; + public Foo(int value) + { + Value = value; + } + + public override string ToString() + { + return Value.ToString(); + } + } + + class Bar + { + public readonly int Value; + public Bar(int value) + { + Value = value; + } + + public override string ToString() + { + return Value.ToString(); + } + } + + class FooShared + { + public readonly int Value; + public FooShared(int value) + { + Value = value; + } + + public override string ToString() + { + return Value.ToString(); + } + } + + class BarShared + { + public readonly int Value; + public BarShared(int value) + { + Value = value; + } + + public override string ToString() + { + return Value.ToString(); + } + } + + class GenClass + { + public readonly T X; + + public GenClass(T x) + { + X = x; + } + + public string MakeString() + { + // Use a constructed type that is not used elsewhere + return typeof(T[,]).GetElementType().Name + ": " + X.ToString(); + } + + public string MakeGenString() + { + // Use a constructed type that is not used elsewhere + return typeof(T[,,]).GetElementType().Name + ", " + + typeof(U[,,,]).GetElementType().Name + ": " + X.ToString(); + } + } + + struct GenStruct + { + public readonly T X; + + public GenStruct(T x) + { + X = x; + } + + public string MakeString() + { + // Use a constructed type that is not used elsewhere + return typeof(T[,]).GetElementType().Name + ": " + X.ToString(); + } + + public string MakeGenString() + { + // Use a constructed type that is not used elsewhere + return typeof(T[,,]).GetElementType().Name + ", " + + typeof(U[,,,]).GetElementType().Name + ": " + X.ToString(); + } + } + + private static void RunReferenceTypeShared(T value) + { + // Delegate to a shared nongeneric reference type instance method + { + GenClass g = new GenClass(value); + Func f = g.MakeString; + if (f() != "FooShared: 42") + throw new Exception(); + } + + // Delegate to a shared generic reference type instance method + { + GenClass g = new GenClass(value); + Func f = g.MakeGenString; + if (f() != "FooShared, FooShared: 42") + throw new Exception(); + } + } + + private static void RunValueTypeShared(T value) + { + // Delegate to a shared nongeneric value type instance method + { + GenStruct g = new GenStruct(value); + Func f = g.MakeString; + if (f() != "BarShared: 42") + throw new Exception(); + } + + // Delegate to a shared generic value type instance method + { + GenStruct g = new GenStruct(value); + Func f = g.MakeGenString; + if (f() != "BarShared, BarShared: 42") + throw new Exception(); + } + } + + public static void Run() + { + // Delegate to a shared nongeneric reference type instance method + { + GenClass g = new GenClass(new Foo(42)); + Func f = g.MakeString; + if (f() != "Foo: 42") + throw new Exception(); + } + + // Delegate to a unshared nongeneric reference type instance method + { + GenClass g = new GenClass(85); + Func f = g.MakeString; + if (f() != "Int32: 85") + throw new Exception(); + } + + // Delegate to a shared generic reference type instance method + { + GenClass g = new GenClass(new Foo(42)); + Func f = g.MakeGenString; + if (f() != "Foo, Foo: 42") + throw new Exception(); + } + + // Delegate to a unshared generic reference type instance method + { + GenClass g = new GenClass(85); + Func f = g.MakeGenString; + if (f() != "Int32, Int32: 85") + throw new Exception(); + } + + // Delegate to a shared nongeneric value type instance method + { + GenStruct g = new GenStruct(new Bar(42)); + Func f = g.MakeString; + if (f() != "Bar: 42") + throw new Exception(); + } + + // Delegate to a unshared nongeneric value type instance method + { + GenStruct g = new GenStruct(85); + Func f = g.MakeString; + if (f() != "Int32: 85") + throw new Exception(); + } + + // Delegate to a shared generic value type instance method + { + GenStruct g = new GenStruct(new Bar(42)); + Func f = g.MakeGenString; + if (f() != "Bar, Bar: 42") + throw new Exception(); + } + + // Delegate to a unshared generic value type instance method + { + GenStruct g = new GenStruct(85); + Func f = g.MakeGenString; + if (f() != "Int32, Int32: 85") + throw new Exception(); + } + + // Now the same from shared code + RunReferenceTypeShared(new FooShared(42)); + RunValueTypeShared(new BarShared(42)); + } + } + + class TestDelegateVirtualMethod + { + static void Generic() + { + Base o = new Derived(); + Func f = o.Do; + if (f() != "Derived") + throw new Exception(); + + o = new Base(); + f = o.Do; + if (f() != "Base") + throw new Exception(); + } + + public static void Run() + { + Generic(); + } + + class Base + { + public virtual string Do() => "Base"; + } + + class Derived : Base + { + public override string Do() => "Derived"; + } + } + + class TestDelegateInterfaceMethod + { + static void Generic() + { + IFoo o = new Foo(); + Func f = o.Do; + if (f() != "Foo") + throw new Exception(); + } + + public static void Run() + { + Generic(); + } + + interface IFoo + { + string Do(); + } + + class Foo : IFoo + { + public string Do() => "Foo"; + } + } + + /// + /// Tests RyuJIT's initThisClass. + /// + class TestInitThisClass + { + class Gen1 where T : class + { + static string s_str1; + static string s_str2; + + static Gen1() + { + s_str1 = ("Hello" as T) as string; + s_str2 = ("World" as T) as string; + } + + public static string Get1() + { + return (s_str1 as T) as string; + } + + public static string Get2() + { + return (s_str2 as T) as string; + } + } + + class Gen2 where T : class + { + public static string GetFromClassParam() + { + return (Gen1.Get1() as T) as string; + } + + public static string GetFromMethodParam() + { + return (Gen1.Get2() as T) as string; + } + } + + class NonGeneric + { + public static readonly string Message; + + static NonGeneric() + { + Message = "Hi there"; + } + + public static string Get(object o) + { + if (o is T[]) + return Message; + return null; + } + } + + public static void Run() + { + if (Gen2.GetFromClassParam() != "Hello") + throw new Exception(); + + if (Gen2.GetFromMethodParam() != "World") + throw new Exception(); + + if (NonGeneric.Get(new object[0]) != "Hi there") + throw new Exception(); + } + } + + /// + /// Tests that lazily built vtables for canonically equivalent types have the same shape. + /// + class TestVirtualMethodUseTracking + { + class C1 { } + class C2 { } + + class Base1 where T : class + { + public virtual T As(object o) + { + return o as T; + } + } + + class Derived1 : Base1 where T : class + { + public T AsToo(object o) + { + return o as T; + } + } + + class Base2 + { + public virtual string Method1() => "Base2.Method1"; + public virtual string Method2() => "Base2.Method2"; + } + + class Derived2 : Base2 + { + public override string Method1() => "Derived2.Method1"; + public override string Method2() => "Derived2.Method2"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static string TestMethod1FromSharedCode(Base2 o) => o.Method1(); + + [MethodImpl(MethodImplOptions.NoInlining)] + private static string TestMethod2FromSharedCode(Base2 o) => o.Method2(); + + public static void Run() + { + C1 c1 = new C1(); + if (new Derived1().As(c1) != c1) + throw new Exception(); + + C2 c2 = new C2(); + if (new Derived1().AsToo(c2) != c2) + throw new Exception(); + + // Also test the stability of the vtables. + Base2 b1 = new Derived2(); + if (b1.Method1() != "Derived2.Method1") + throw new Exception(); + Base2 b2 = new Derived2(); + if (b2.Method2() != "Derived2.Method2") + throw new Exception(); + if (TestMethod1FromSharedCode(b2) != "Derived2.Method1") + throw new Exception(); + if (TestMethod1FromSharedCode(b1) != "Derived2.Method1") + throw new Exception(); + if (TestMethod2FromSharedCode(b2) != "Derived2.Method2") + throw new Exception(); + if (TestMethod2FromSharedCode(b1) != "Derived2.Method2") + throw new Exception(); + } + } + + /// + /// Makes sure that during the base slot computation for types such as + /// Derived<__Canon> (where the base type ends up being Base<__Canon, string>), + /// the lazy vtable slot computation works. + /// + class TestSlotsInHierarchy + { + class Base + { + public virtual int Do() + { + return 42; + } + } + + class Derived : Base where T : class + { + public T Cast(object v) + { + return v as T; + } + } + + public static void Run() + { + var derived = new Derived(); + var derivedAsBase = (Base)derived; + + if (derivedAsBase.Do() != 42) + throw new Exception(); + + if (derived.Cast("Hello") != "Hello") + throw new Exception(); + } + } + + class TestReflectionInvoke + { + static int s_NumErrors = 0; + + struct Foo + { + public int Value; + + [MethodImpl(MethodImplOptions.NoInlining)] + public bool SetAndCheck(int value, U check) + { + Value = value; + return check != null && typeof(T) == typeof(U); + } + } + + public interface IFace + { + string IFaceMethod1(T t); + string IFaceGVMethod1(T t, U u); + } + + public class BaseClass : IFace + { + public virtual string Method1(T t) { return "BaseClass.Method1"; } + public virtual string Method2(T t) { return "BaseClass.Method2"; } + public virtual string Method3(T t) { return "BaseClass.Method3"; } + public virtual string Method4(T t) { return "BaseClass.Method4"; } + public virtual string GVMethod1(T t, U u) { return "BaseClass.GVMethod1"; } + public virtual string GVMethod2(T t, U u) { return "BaseClass.GVMethod2"; } + public virtual string GVMethod3(T t, U u) { return "BaseClass.GVMethod3"; } + public virtual string GVMethod4(T t, U u) { return "BaseClass.GVMethod4"; } + + public virtual string IFaceMethod1(T t) { return "BaseClass.IFaceMethod1"; } + public virtual string IFaceGVMethod1(T t, U u) { return "BaseClass.IFaceGVMethod1"; } + + [MethodImpl(MethodImplOptions.NoInlining)] + public virtual string VirtualButNotUsedVirtuallyMethod(T t) { return "BaseClass.VirtualButNotUsedVirtuallyMethod"; } + } + + public class DerivedClass1 : BaseClass, IFace + { + public override sealed string Method1(T t) { return "DerivedClass1.Method1"; } + public override string Method2(T t) { return "DerivedClass1.Method2"; } + public new virtual string Method3(T t) { return "DerivedClass1.Method3"; } + public override sealed string GVMethod1(T t, U u) { return "DerivedClass1.GVMethod1"; } + public override string GVMethod2(T t, U u) { return "DerivedClass1.GVMethod2"; } + public new virtual string GVMethod3(T t, U u) { return "DerivedClass1.GVMethod3"; } + + public override string IFaceMethod1(T t) { return "DerivedClass1.IFaceMethod1"; } + + public string UseVirtualButNotUsedVirtuallyMethod(T t) + { + // Calling through base produces a `call` instead of `callvirt` instruction. + return base.VirtualButNotUsedVirtuallyMethod(t); + } + } + + public class DerivedClass2 : DerivedClass1, IFace + { + public override string Method3(T t) { return "DerivedClass2.Method3"; } + public override string Method4(T t) { return "DerivedClass2.Method4"; } + public override string GVMethod3(T t, U u) { return "DerivedClass2.GVMethod3"; } + public override string GVMethod4(T t, U u) { return "DerivedClass2.GVMethod4"; } + + string IFace.IFaceMethod1(T t) { return "DerivedClass2.IFaceMethod1"; } + public override string IFaceGVMethod1(T t, U u) { return "DerivedClass2.IFaceGVMethod1"; } + } + + private static void Verify(T expected, T actual) + { + if (!actual.Equals(expected)) + { + Console.WriteLine("ACTUAL : " + actual); + Console.WriteLine("EXPECTED : " + expected); + s_NumErrors++; + } + } + + public static void Run() + { + if (String.Empty.Length > 0) + { + // Make sure we compile this method body. + var tmp = new Foo(); + tmp.SetAndCheck(0, null); + } + + object o = new Foo(); + + { + MethodInfo mi = typeof(Foo).GetMethod("SetAndCheck").MakeGenericMethod(typeof(string)); + if (!(bool)mi.Invoke(o, new object[] { 123, "hello" })) + s_NumErrors++; + + var foo = (Foo)o; + if (foo.Value != 123) + s_NumErrors++; + + if ((bool)mi.Invoke(o, new object[] { 123, null })) + s_NumErrors++; + } + + // Uncomment when we have the type loader to buld invoke stub dictionaries. + { + MethodInfo mi = typeof(Foo).GetMethod("SetAndCheck").MakeGenericMethod(typeof(object)); + if ((bool)mi.Invoke(o, new object[] { 123, new object() })) + s_NumErrors++; + } + + // VirtualInvokeMap testing + { + // Rooting some methods to make them reflectable + new BaseClass().Method1("string"); + new BaseClass().Method2("string"); + new BaseClass().Method3("string"); + new BaseClass().Method4("string"); + new BaseClass().GVMethod1("string", "string2"); + new BaseClass().GVMethod2("string", "string2"); + new BaseClass().GVMethod3("string", "string2"); + new BaseClass().GVMethod4("string", "string2"); + new DerivedClass1().Method1("string"); + new DerivedClass1().Method2("string"); + new DerivedClass1().Method3("string"); + new DerivedClass1().Method4("string"); + new DerivedClass1().GVMethod1("string", "string2"); + new DerivedClass1().GVMethod2("string", "string2"); + new DerivedClass1().GVMethod3("string", "string2"); + new DerivedClass1().GVMethod4("string", "string2"); + new DerivedClass1().UseVirtualButNotUsedVirtuallyMethod("string"); + new DerivedClass2().Method1("string"); + new DerivedClass2().Method2("string"); + new DerivedClass2().Method3("string"); + new DerivedClass2().Method4("string"); + new DerivedClass2().GVMethod1("string", "string2"); + new DerivedClass2().GVMethod2("string", "string2"); + new DerivedClass2().GVMethod3("string", "string2"); + new DerivedClass2().GVMethod4("string", "string2"); + Func> f = () => new BaseClass(); // Hack to prevent devirtualization + f().IFaceMethod1("string"); + ((IFace)new BaseClass()).IFaceGVMethod1("string1", "string2"); + + MethodInfo m1 = typeof(BaseClass).GetMethod("Method1"); + MethodInfo m2 = typeof(BaseClass).GetMethod("Method2"); + MethodInfo m3 = typeof(BaseClass).GetMethod("Method3"); + MethodInfo m4 = typeof(BaseClass).GetMethod("Method4"); + MethodInfo unusedMethod = typeof(BaseClass).GetMethod("VirtualButNotUsedVirtuallyMethod"); + MethodInfo gvm1 = typeof(BaseClass).GetMethod("GVMethod1").MakeGenericMethod(typeof(string)); + MethodInfo gvm2 = typeof(BaseClass).GetMethod("GVMethod2").MakeGenericMethod(typeof(string)); + MethodInfo gvm3 = typeof(BaseClass).GetMethod("GVMethod3").MakeGenericMethod(typeof(string)); + MethodInfo gvm4 = typeof(BaseClass).GetMethod("GVMethod4").MakeGenericMethod(typeof(string)); + Verify("BaseClass.Method1", m1.Invoke(new BaseClass(), new[] { "" })); + Verify("BaseClass.Method2", m2.Invoke(new BaseClass(), new[] { "" })); + Verify("BaseClass.Method3", m3.Invoke(new BaseClass(), new[] { "" })); + Verify("BaseClass.Method4", m4.Invoke(new BaseClass(), new[] { "" })); + Verify("BaseClass.VirtualButNotUsedVirtuallyMethod", unusedMethod.Invoke(new BaseClass(), new[] { "" })); + Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass1(), new[] { "" })); + Verify("DerivedClass1.Method2", m2.Invoke(new DerivedClass1(), new[] { "" })); + Verify("BaseClass.Method3", m3.Invoke(new DerivedClass1(), new[] { "" })); + Verify("BaseClass.Method4", m4.Invoke(new DerivedClass1(), new[] { "" })); + Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass1.Method2", m2.Invoke(new DerivedClass2(), new[] { "" })); + Verify("BaseClass.Method3", m3.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass2.Method4", m4.Invoke(new DerivedClass2(), new[] { "" })); + Verify("BaseClass.GVMethod1", gvm1.Invoke(new BaseClass(), new[] { "", "" })); + Verify("BaseClass.GVMethod2", gvm2.Invoke(new BaseClass(), new[] { "", "" })); + Verify("BaseClass.GVMethod3", gvm3.Invoke(new BaseClass(), new[] { "", "" })); + Verify("BaseClass.GVMethod4", gvm4.Invoke(new BaseClass(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod1", gvm1.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod2", gvm2.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("BaseClass.GVMethod3", gvm3.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("BaseClass.GVMethod4", gvm4.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod1", gvm1.Invoke(new DerivedClass2(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod2", gvm2.Invoke(new DerivedClass2(), new[] { "", "" })); + Verify("BaseClass.GVMethod3", gvm3.Invoke(new DerivedClass2(), new[] { "", "" })); + Verify("DerivedClass2.GVMethod4", gvm4.Invoke(new DerivedClass2(), new[] { "", "" })); + + m1 = typeof(DerivedClass1).GetMethod("Method1"); + m2 = typeof(DerivedClass1).GetMethod("Method2"); + m3 = typeof(DerivedClass1).GetMethod("Method3"); + gvm1 = typeof(DerivedClass1).GetMethod("GVMethod1").MakeGenericMethod(typeof(string)); + gvm2 = typeof(DerivedClass1).GetMethod("GVMethod2").MakeGenericMethod(typeof(string)); + gvm3 = typeof(DerivedClass1).GetMethod("GVMethod3", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly).MakeGenericMethod(typeof(string)); + Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass1(), new[] { "" })); + Verify("DerivedClass1.Method2", m2.Invoke(new DerivedClass1(), new[] { "" })); + Verify("DerivedClass1.Method3", m3.Invoke(new DerivedClass1(), new[] { "" })); + Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass1.Method2", m2.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass2.Method3", m3.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass1.GVMethod1", gvm1.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod2", gvm2.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod3", gvm3.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod1", gvm1.Invoke(new DerivedClass2(), new[] { "", "" })); + Verify("DerivedClass1.GVMethod2", gvm2.Invoke(new DerivedClass2(), new[] { "", "" })); + Verify("DerivedClass2.GVMethod3", gvm3.Invoke(new DerivedClass2(), new[] { "", "" })); + + m3 = typeof(DerivedClass2).GetMethod("Method3"); + m4 = typeof(DerivedClass2).GetMethod("Method4"); + gvm3 = typeof(DerivedClass2).GetMethod("GVMethod3", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly).MakeGenericMethod(typeof(string)); + gvm4 = typeof(DerivedClass2).GetMethod("GVMethod4", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly).MakeGenericMethod(typeof(string)); + Verify("DerivedClass2.Method3", m3.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass2.Method4", m4.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass2.GVMethod3", gvm3.Invoke(new DerivedClass2(), new[] { "", "" })); + Verify("DerivedClass2.GVMethod4", gvm4.Invoke(new DerivedClass2(), new[] { "", "" })); + + // BaseClass.Method1 has the same slot as BaseClass.Method3 on CoreRT, because vtable entries + // get populated on demand (the first type won't get a Method3 entry, and the latter won't get a Method1 entry) + // On ProjectN, both types will get vtable entries for both methods. + new BaseClass().Method1(1); + m1 = typeof(BaseClass).GetMethod("Method1"); + Verify("BaseClass.Method1", m1.Invoke(new BaseClass(), new object[] { (int)1 })); + Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass1(), new object[] { (int)1 })); + Verify("DerivedClass1.Method1", m1.Invoke(new DerivedClass2(), new object[] { (int)1 })); + + new BaseClass().Method3(1); + m3 = typeof(BaseClass).GetMethod("Method3"); + Verify("BaseClass.Method3", m3.Invoke(new BaseClass(), new object[] { 1.1f })); + Verify("BaseClass.Method3", m3.Invoke(new DerivedClass1(), new object[] { 1.1f })); + Verify("BaseClass.Method3", m3.Invoke(new DerivedClass2(), new object[] { 1.1f })); + + m1 = typeof(IFace).GetMethod("IFaceMethod1"); + gvm1 = typeof(IFace).GetMethod("IFaceGVMethod1").MakeGenericMethod(typeof(string)); + Verify("BaseClass.IFaceMethod1", m1.Invoke(new BaseClass(), new[] { "" })); + Verify("BaseClass.IFaceGVMethod1", gvm1.Invoke(new BaseClass(), new[] { "", "" })); + Verify("DerivedClass1.IFaceMethod1", m1.Invoke(new DerivedClass1(), new[] { "" })); + Verify("BaseClass.IFaceGVMethod1", gvm1.Invoke(new DerivedClass1(), new[] { "", "" })); + Verify("DerivedClass2.IFaceMethod1", m1.Invoke(new DerivedClass2(), new[] { "" })); + Verify("DerivedClass2.IFaceGVMethod1", gvm1.Invoke(new DerivedClass2(), new[] { "", "" })); + } + + if (s_NumErrors != 0) + throw new Exception(); + } + } + + class TestThreadStaticFieldAccess + { + class TypeWithThreadStaticField + { + [ThreadStatic] + public static int X; + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Read() + { + return X; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static void Write(int x) + { + X = x; + } + } + + class BeforeFieldInitType + { + [ThreadStatic] + public static int X = 1985; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static int ReadFromBeforeFieldInitType() + { + return BeforeFieldInitType.X; + } + + public static void Run() + { + // This will set the field to a value from non-shared code + TypeWithThreadStaticField.X = 42; + + // Now read the value from shared code + if (TypeWithThreadStaticField.Read() != 42) + throw new Exception(); + + // Set the value from shared code + TypeWithThreadStaticField.Write(112); + + // Now read the value from non-shared code + if (TypeWithThreadStaticField.X != 112) + throw new Exception(); + + // Check that the storage locations for string and object instantiations differ + if (TypeWithThreadStaticField.Read() != 42) + throw new Exception(); + + // Make sure we run the cctor + if (ReadFromBeforeFieldInitType() != 1985) + throw new Exception(); + } + } + + class TestConstrainedMethodCalls + { + class Atom1 { } + class Atom2 { } + + interface IFoo + { + bool Frob(object o); + } + + struct Foo : IFoo + { + public int FrobbedValue; + + public bool Frob(object o) + { + FrobbedValue = 12345; + return o is T[,,]; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool DoFrob(ref T t, object o) where T : IFoo + { + // Perform a constrained interface call from shared code. + // This should have been resolved to a direct call at compile time. + return t.Frob(o); + } + + public static void Run() + { + var foo1 = new Foo(); + bool result = DoFrob, Atom1>(ref foo1, new Atom1[0, 0, 0]); + + // If the FrobbedValue doesn't change when we frob, we must have done box+interface call. + if (foo1.FrobbedValue != 12345) + throw new Exception(); + + // Also check we passed the right generic context to Foo.Frob + if (!result) + throw new Exception(); + + // Also check dependency analysis: + // EEType for Atom2[,,] that we'll check for was never allocated. + var foo2 = new Foo(); + if (DoFrob, Atom2>(ref foo2, new object())) + throw new Exception(); + } + } + + class TestInstantiatingUnboxingStubs + { + static volatile IFoo s_foo; + + interface IFoo + { + bool IsInst(object o); + + void Set(int value); + } + + struct Foo : IFoo + { + public int Value; + + public bool IsInst(object o) + { + return o is T; + } + + public void Set(int value) + { + Value = value; + } + } + + public static void Run() + { + s_foo = new Foo(); + + // Make sure the instantiation argument is properly passed + if (!s_foo.IsInst("ab")) + throw new Exception(); + + if (s_foo.IsInst(new object())) + throw new Exception(); + + // Make sure the byref to 'this' is properly passed + s_foo.Set(42); + + var foo = (Foo)s_foo; + if (foo.Value != 42) + throw new Exception(); + } + } + + class TestMDArrayAddressMethod + { + [MethodImpl(MethodImplOptions.NoInlining)] + private static void PassByRef(ref object x) + { + x = new Object(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DoGen(object[,] arr) + { + // Here, the array type is known statically at the time of compilation + PassByRef(ref arr[0, 0]); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void PassByRef2(ref T x) + { + x = default(T); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DoGen2(T[,] arr) + { + // Here, the array type needs to be looked up from the dictionary + PassByRef2(ref arr[0, 0]); + } + + public static void Run() + { + int exceptionsSeen = 0; + + try + { + DoGen(new string[1, 1]); + } + catch (ArrayTypeMismatchException) + { + exceptionsSeen++; + } + + DoGen(new object[1, 1]); + + try + { + DoGen2(new string[1, 1]); + } + catch (ArrayTypeMismatchException) + { + exceptionsSeen++; + } + + DoGen2(new object[1, 1]); + + if (exceptionsSeen != 2) + throw new Exception(); + } + } + + // + // Regression test for issue https://github.com/dotnet/corert/issues/1964 + // + class TestNameManglingCollisionRegression + { + class Gen1 + { + public Gen1(T t) { } + } + + public static void Run() + { + Gen1[] g1 = new Gen1[1]; + g1[0] = new Gen1(new object[] { new object[1] }); + + Gen1 g2 = new Gen1(new object[1][]); + } + } + + class TestSimpleGVMScenarios + { + interface IFoo + { + string IMethod1(T t1, T t2); + } + + interface ICovariant + { + string ICovariantGVM(); + } + + public interface IBar + { + U IBarGVMethod(Func arg); + } + + public interface IFace + { + string IFaceGVMethod1(T t, U u); + } + + class Base : IFoo, IFoo + { + public virtual string GMethod1(T t1, T t2) { return "Base.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + public virtual string IMethod1(T t1, T t2) { return "Base.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + class Derived : Base, IFoo, IFoo + { + public override string GMethod1(T t1, T t2) { return "Derived.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + string IFoo.IMethod1(T t1, T t2) { return "Derived.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + class SuperDerived : Derived, IFoo, IFoo + { + string IFoo.IMethod1(T t1, T t2) { return "SuperDerived.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + + + class GenBase : IFoo, IFoo + { + public virtual string GMethod1(T t1, T t2) { return "GenBase<" + typeof(A) + ">.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + public virtual string IMethod1(T t1, T t2) { return "GenBase<" + typeof(A) + ">.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + class GenDerived : GenBase, IFoo, IFoo + { + public override string GMethod1(T t1, T t2) { return "GenDerived<" + typeof(A) + ">.GMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + string IFoo.IMethod1(T t1, T t2) { return "GenDerived<" + typeof(A) + ">.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + class GenSuperDerived : GenDerived, IFoo, IFoo + { + string IFoo.IMethod1(T t1, T t2) { return "GenSuperDerived<" + typeof(A) + ">.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + + struct MyStruct1 : IFoo, IFoo + { + string IFoo.IMethod1(T t1, T t2) { return "MyStruct1.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + string IFoo.IMethod1(T t1, T t2) { return "MyStruct1.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + struct MyStruct2 : IFoo, IFoo + { + string IFoo.IMethod1(T t1, T t2) { return "MyStruct2.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + public string IMethod1(T t1, T t2) { return "MyStruct2.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + struct MyStruct3 : IFoo, IFoo + { + string IFoo.IMethod1(T t1, T t2) { return "MyStruct3.IFoo.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + public string IMethod1(T t1, T t2) { return "MyStruct3.IMethod1<" + typeof(T) + ">(" + t1 + "," + t2 + ")"; } + } + + public class AnotherBaseClass + { + public virtual string IFaceMethod1(T t) { return "AnotherBaseClass.IFaceMethod1"; } + public virtual string IFaceGVMethod1(T t, U u) { return "AnotherBaseClass.IFaceGVMethod1"; } + } + + public class AnotherDerivedClass : AnotherBaseClass, IFace + { + } + + public class BarImplementor : IBar + { + public virtual U IBarGVMethod(Func arg) { return arg(123); } + } + + public class Yahoo + { + public virtual U YahooGVM(Func arg) { return default(U); } + } + + public class YahooDerived : Yahoo + { + public override U YahooGVM(Func arg) { return arg(456); } + } + + public class Covariant : ICovariant + { + public string ICovariantGVM() { return String.Format("Covariant<{0}>.ICovariantGVM<{1}>", typeof(T).Name, typeof(U).Name); } + } + + static string s_GMethod1; + static string s_IFooString; + static string s_IFooObject; + static string s_IFooInt; + + static int s_NumErrors = 0; + + private static void TestWithStruct(IFoo ifooStr, IFoo ifooObj, IFoo ifooInt) + { + var res = ifooStr.IMethod1(1, 2); + WriteLineWithVerification(res, s_IFooString); + + res = ifooObj.IMethod1(3, 4); + WriteLineWithVerification(res, s_IFooObject); + + res = ifooInt.IMethod1(5, 6); + WriteLineWithVerification(res, s_IFooInt); + } + + private static void TestWithClass(object o) + { + Base b = o as Base; + var res = b.GMethod1(1, 2); + WriteLineWithVerification(res, s_GMethod1); + + IFoo ifoo1 = o as IFoo; + res = ifoo1.IMethod1(3, 4); + WriteLineWithVerification(res, s_IFooString); + + IFoo ifoo2 = o as IFoo; + res = ifoo2.IMethod1(5, 6); + WriteLineWithVerification(res, s_IFooObject); + + IFoo ifoo3 = o as IFoo; + res = ifoo3.IMethod1(7, 8); + WriteLineWithVerification(res, s_IFooInt); + } + + private static void TestWithGenClass(object o) + { + GenBase b = o as GenBase; + var res = b.GMethod1(1, 2); + WriteLineWithVerification(res, s_GMethod1); + + IFoo ifoo1 = o as IFoo; + res = ifoo1.IMethod1(3, 4); + WriteLineWithVerification(res, s_IFooString); + + IFoo ifoo2 = o as IFoo; + res = ifoo2.IMethod1(5, 6); + WriteLineWithVerification(res, s_IFooObject); + + IFoo ifoo3 = o as IFoo; + res = ifoo3.IMethod1(7, 8); + WriteLineWithVerification(res, s_IFooInt); + } + + private static void WriteLineWithVerification(string actual, string expected) + { + if (actual != expected) + { + Console.WriteLine("ACTUAL : " + actual); + Console.WriteLine("EXPECTED : " + expected); + s_NumErrors++; + } + else + { + Console.WriteLine(actual); + } + } + + public static void Run() + { + { + s_GMethod1 = "Base.GMethod1(1,2)"; + s_IFooString = "Base.IMethod1(3,4)"; + s_IFooObject = "Base.IMethod1(5,6)"; + s_IFooInt = "Base.IMethod1(7,8)"; + TestWithClass(new Base()); + Console.WriteLine("===================="); + + + s_GMethod1 = "Derived.GMethod1(1,2)"; + s_IFooString = "Derived.IFoo.IMethod1(3,4)"; + s_IFooObject = "Derived.IFoo.IMethod1(5,6)"; + s_IFooInt = "Base.IMethod1(7,8)"; + TestWithClass(new Derived()); + Console.WriteLine("===================="); + + + s_GMethod1 = "Derived.GMethod1(1,2)"; + s_IFooString = "Derived.IFoo.IMethod1(3,4)"; + s_IFooObject = "Derived.IFoo.IMethod1(5,6)"; + s_IFooInt = "SuperDerived.IFoo.IMethod1(7,8)"; + TestWithClass(new SuperDerived()); + Console.WriteLine("===================="); + } + + { + s_GMethod1 = "GenBase.GMethod1(1,2)"; + s_IFooString = "GenBase.IMethod1(3,4)"; + s_IFooObject = "GenBase.IMethod1(5,6)"; + s_IFooInt = "GenBase.IMethod1(7,8)"; + TestWithGenClass(new GenBase()); + Console.WriteLine("===================="); + + + s_GMethod1 = "GenDerived.GMethod1(1,2)"; + s_IFooString = "GenDerived.IFoo.IMethod1(3,4)"; + s_IFooObject = "GenDerived.IFoo.IMethod1(5,6)"; + s_IFooInt = "GenBase.IMethod1(7,8)"; + TestWithGenClass(new GenDerived()); + Console.WriteLine("===================="); + + + s_GMethod1 = "GenDerived.GMethod1(1,2)"; + s_IFooString = "GenDerived.IFoo.IMethod1(3,4)"; + s_IFooObject = "GenDerived.IFoo.IMethod1(5,6)"; + s_IFooInt = "GenBase.IMethod1(7,8)"; + TestWithGenClass(new GenDerived()); + Console.WriteLine("===================="); + + + s_GMethod1 = "GenDerived.GMethod1(1,2)"; + s_IFooString = "GenDerived.IFoo.IMethod1(3,4)"; + s_IFooObject = "GenDerived.IFoo.IMethod1(5,6)"; + s_IFooInt = "GenSuperDerived.IFoo.IMethod1(7,8)"; + TestWithGenClass(new GenSuperDerived()); + Console.WriteLine("===================="); + } + + { + s_IFooString = "MyStruct1.IFoo.IMethod1(1,2)"; + s_IFooObject = "MyStruct1.IFoo.IMethod1(3,4)"; + s_IFooInt = "MyStruct1.IFoo.IMethod1(5,6)"; + TestWithStruct(new MyStruct1(), new MyStruct1(), new MyStruct1()); + Console.WriteLine("===================="); + + + s_IFooString = "MyStruct2.IFoo.IMethod1(1,2)"; + s_IFooObject = "MyStruct2.IFoo.IMethod1(3,4)"; + s_IFooInt = "MyStruct2.IMethod1(5,6)"; + TestWithStruct(new MyStruct2(), new MyStruct2(), new MyStruct2()); + Console.WriteLine("===================="); + + + s_IFooString = "MyStruct3.IMethod1(1,2)"; + s_IFooObject = "MyStruct3.IMethod1(3,4)"; + s_IFooInt = "MyStruct3.IFoo.IMethod1(5,6)"; + TestWithStruct(new MyStruct3(), new MyStruct3(), new MyStruct3()); + Console.WriteLine("===================="); + } + + { + string res = ((IFace)new AnotherDerivedClass()).IFaceGVMethod1("string1", "string2"); + WriteLineWithVerification("AnotherBaseClass.IFaceGVMethod1", res); + + res = ((IBar)new BarImplementor()).IBarGVMethod((i) => "BarImplementor:" + i.ToString()); + WriteLineWithVerification("BarImplementor:123", res); + + Yahoo y = new YahooDerived(); + WriteLineWithVerification("YahooDerived:456", y.YahooGVM((i) => "YahooDerived:" + i.ToString())); + + ICovariant cov = new Covariant(); + WriteLineWithVerification("Covariant.ICovariantGVM", cov.ICovariantGVM()); + } + + if (s_NumErrors != 0) + throw new Exception(); + } + } + + class TestGvmDelegates + { + class Atom { } + + interface IFoo + { + string Frob(int arg); + } + + class FooUnshared : IFoo + { + public string Frob(int arg) + { + return typeof(T[,]).GetElementType().Name + arg.ToString(); + } + } + + class FooShared : IFoo + { + public string Frob(int arg) + { + return typeof(T[,,]).GetElementType().Name + arg.ToString(); + } + } + + class Base + { + public virtual string Frob(string s) + { + return typeof(T).Name + ": Base: " + s; + } + } + + class Derived : Base + { + public override string Frob(string s) + { + return typeof(T).Name + ": Derived: " + s; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void ValidateShared(string s) + { + Func f = Frob; + if (f(s) != typeof(T).Name + ": Derived: " + s) + throw new Exception(); + + f = base.Frob; + if (f(s) != typeof(T).Name + ": Base: " + s) + throw new Exception(); + } + + public void Validate(string s) + { + Func f = Frob; + if (f(s) != typeof(string).Name + ": Derived: " + s) + throw new Exception(); + + f = base.Frob; + if (f(s) != typeof(string).Name + ": Base: " + s) + throw new Exception(); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void RunShared(IFoo foo) + { + Func a = foo.Frob; + if (a(456) != "Atom456") + throw new Exception(); + } + + public static void Run() + { + IFoo foo = new FooUnshared(); + Func a = foo.Frob; + if (a(123) != "Atom123") + throw new Exception(); + + RunShared(new FooShared()); + + new Derived().Validate("hello"); + new Derived().ValidateShared("ola"); + } + } + + class TestGvmDependencies + { + class Atom { } + + class Foo + { + public virtual object Frob() + { + return new T[0, 0]; + } + } + + class Bar : Foo + { + public override object Frob() + { + return new T[0, 0, 0]; + } + } + + public static void Run() + { + { + Foo x = new Foo(); + x.Frob(); + } + + { + Foo x = new Bar(); + x.Frob(); + } + } + } + + class TestFieldAccess + { + class ClassType { } + class ClassType2 { } + struct StructType { } + + class Foo + { + static Foo() + { + Console.WriteLine("Foo<" + typeof(T).Name + "> cctor"); + + if (typeof(T) == typeof(ClassType)) + TestFieldAccess.s_FooClassTypeCctorCount++; + else if (typeof(T) == typeof(StructType)) + TestFieldAccess.s_FooStructTypeCctorCount++; + } + + public static int s_intField; + public static float s_floatField; + public static string s_stringField; + public static object s_objectField; + public static long s_longField1; + public static long s_longField2; + public static long s_longField3; + public static KeyValuePair s_kvp; + + public int m_intField; + public float m_floatField; + public string m_stringField; + public object m_objectField; + } + + class Bar + { + static Bar() + { + Console.WriteLine("Bar cctor"); + TestFieldAccess.s_BarCctorCount++; + } + + public static int s_intField; + public static float s_floatField; + public static string s_stringField; + public static object s_objectField; + public static long s_longField1; + public static long s_longField2; + public static long s_longField3; + public static KeyValuePair s_kvp; + + public int m_intField; + public float m_floatField; + public string m_stringField; + public object m_objectField; + } + + public class DynamicBase + { + public T _t; + + [MethodImpl(MethodImplOptions.NoInlining)] + public DynamicBase() { } + + [MethodImpl(MethodImplOptions.NoInlining)] + public int SimpleMethod() + { + return 123; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public int MethodWithTInSig(T t) + { + _t = t; + return 234; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public virtual string VirtualMethod(T t) + { + _t = t; + return "DynamicBase.VirtualMethod"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public string GenericMethod(T t, U u) + { + _t = t; + return typeof(U).ToString() + u.ToString(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public virtual string GenericVirtualMethod(T t, U u) + { + _t = t; + return "DynamicBase" + typeof(U).ToString() + u.ToString(); + } + } + + public class DynamicDerived : DynamicBase + { + [MethodImpl(MethodImplOptions.NoInlining)] + public DynamicDerived() { } + + [MethodImpl(MethodImplOptions.NoInlining)] + public override string VirtualMethod(T t) + { + _t = t; + return "DynamicDerived.VirtualMethod"; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public override string GenericVirtualMethod(T t, U u) + { + _t = t; + return "DynamicDerived" + typeof(U).ToString() + u.ToString(); + } + } + + class UnconstructedTypeWithGCStatics + { +#pragma warning disable 169 + static string s_gcField; +#pragma warning restore + } + + class UnconstructedTypeWithNonGCStatics + { +#pragma warning disable 169 + static float s_nonGcField; +#pragma warning restore + } + + class UnconstructedTypeInstantiator { } + + public static int s_FooClassTypeCctorCount = 0; + public static int s_FooStructTypeCctorCount = 0; + public static int s_BarCctorCount = 0; + public static int s_NumErrors = 0; + + private static void Verify(T expected, T actual) + { + if (!actual.Equals(expected)) + { + Console.WriteLine("ACTUAL : " + actual); + Console.WriteLine("EXPECTED : " + expected); + s_NumErrors++; + } + } + + private static void TestDynamicStaticFields() + { + Foo.s_intField = 1234; + Foo.s_floatField = 12.34f; + Foo.s_longField1 = 0x1111; + + var fooDynamicOfClassType = typeof(Foo<>).MakeGenericType(typeof(ClassType)); + var fooDynamicOfClassType2 = typeof(Foo<>).MakeGenericType(typeof(ClassType2)); + + FieldInfo fi = fooDynamicOfClassType.GetField("s_intField"); + FieldInfo fi2 = fooDynamicOfClassType2.GetField("s_intField"); + fi.SetValue(null, 1111); + fi2.SetValue(null, 2222); + Verify(1111, (int)fi.GetValue(null)); + Verify(2222, (int)fi2.GetValue(null)); + + fi = fooDynamicOfClassType.GetField("s_floatField"); + fi2 = fooDynamicOfClassType2.GetField("s_floatField"); + fi.SetValue(null, 1.1f); + fi2.SetValue(null, 2.2f); + Verify(1.1f, (float)fi.GetValue(null)); + Verify(2.2f, (float)fi2.GetValue(null)); + + fi = fooDynamicOfClassType.GetField("s_longField1"); + fi2 = fooDynamicOfClassType2.GetField("s_longField1"); + fi.SetValue(null, 0x11111111); + fi2.SetValue(null, 0x22222222); + Verify(0x11111111, (long)fi.GetValue(null)); + Verify(0x22222222, (long)fi2.GetValue(null)); + + fi = fooDynamicOfClassType.GetField("s_stringField"); + fi2 = fooDynamicOfClassType2.GetField("s_stringField"); + fi.SetValue(null, "abc123"); + fi2.SetValue(null, "omgroflpwn"); + Verify("abc123", (string)fi.GetValue(null)); + Verify("omgroflpwn", (string)fi2.GetValue(null)); + + fi = fooDynamicOfClassType.GetField("s_objectField"); + fi2 = fooDynamicOfClassType2.GetField("s_objectField"); + fi.SetValue(null, "qwerty"); + fi2.SetValue(null, "ytrewq"); + Verify("qwerty", (string)fi.GetValue(null)); + Verify("ytrewq", (string)fi2.GetValue(null)); + } + + private static void TestDynamicInvokeStubs() + { + Console.WriteLine("Testing dynamic invoke stubs..."); + // Root required methods / types statically instantiated over some unrelated type + DynamicBase heh = new DynamicBase(); + heh.MethodWithTInSig(new Program()); + heh.SimpleMethod(); + heh.VirtualMethod(new Program()); + heh.GenericMethod(new Program(), "hello"); + heh.GenericVirtualMethod(new Program(), "hello"); + + DynamicDerived heh2 = new DynamicDerived(); + heh2.VirtualMethod(new Program()); + heh2.GenericVirtualMethod(new Program(), "ayy"); + + // Simple method invocation + var dynamicBaseOfString = typeof(DynamicBase<>).MakeGenericType(typeof(string)); + object obj = Activator.CreateInstance(dynamicBaseOfString); + { + var simpleMethod = dynamicBaseOfString.GetMethod("SimpleMethod"); + int result = (int)simpleMethod.Invoke(obj, null); + Verify((int)123, result); + } + + // Method with T in the signature + { + var methodWithTInSig = dynamicBaseOfString.GetMethod("MethodWithTInSig"); + int result = (int)methodWithTInSig.Invoke(obj, new[] { "fad" }); + Verify((int)234, result); + } + + // Test virtual method invocation + { + var virtualMethodDynamicBase = dynamicBaseOfString.GetMethod("VirtualMethod"); + string result = (string)virtualMethodDynamicBase.Invoke(obj, new[] { "fad" }); + Verify("DynamicBase.VirtualMethod", result); + } + + { + var dynamicDerivedOfString = typeof(DynamicDerived<>).MakeGenericType(typeof(string)); + object dynamicDerivedObj = Activator.CreateInstance(dynamicDerivedOfString); + var virtualMethodDynamicDerived = dynamicDerivedOfString.GetMethod("VirtualMethod"); + string result = (string)virtualMethodDynamicDerived.Invoke(dynamicDerivedObj, new[] { "fad" }); + Verify("DynamicDerived.VirtualMethod", result); + } + + // Test generic method invocation + { + var genericMethod = dynamicBaseOfString.GetMethod("GenericMethod").MakeGenericMethod(new[] { typeof(string) }); + string result = (string)genericMethod.Invoke(obj, new[] { "hey", "hello" }); + + Verify("System.Stringhello", result); + } + + // Test GVM invocation + { + var genericMethod = dynamicBaseOfString.GetMethod("GenericVirtualMethod"); + genericMethod = genericMethod.MakeGenericMethod(new[] { typeof(string) }); + string result = (string)genericMethod.Invoke(obj, new[] { "hey", "hello" }); + Verify("DynamicBaseSystem.Stringhello", result); + } + + { + var dynamicDerivedOfString = typeof(DynamicDerived<>).MakeGenericType(typeof(string)); + object dynamicDerivedObj = Activator.CreateInstance(dynamicDerivedOfString); + var virtualMethodDynamicDerived = dynamicDerivedOfString.GetMethod("GenericVirtualMethod").MakeGenericMethod(new[] { typeof(string) }); + string result = (string)virtualMethodDynamicDerived.Invoke(dynamicDerivedObj, new[] { "hey", "fad" }); + Verify("DynamicDerivedSystem.Stringfad", result); + } + } + + private static void TestStaticFields() + { + Foo.s_intField = 11223344; + Foo.s_stringField = "abcd"; + Foo.s_floatField = 12.34f; + Foo.s_objectField = "123"; + Foo.s_kvp = new KeyValuePair("1122", "3344"); + + Foo.s_intField = 44332211; + Foo.s_stringField = "dcba"; + Foo.s_floatField = 43.21f; + Foo.s_objectField = "321"; + Foo.s_kvp = new KeyValuePair("4433", "2211"); + + + Bar.s_intField = 778899; + Bar.s_stringField = "xxyyzz"; + Bar.s_floatField = 88.99f; + Bar.s_objectField = "890"; + Bar.s_kvp = new KeyValuePair("7788", "8899"); + + // Testing correctness of cctor context + { + Foo.s_longField1 = 0xff00; + Foo.s_longField2 = 0xff00; + Foo.s_longField3 = 0xff00; + if (TestFieldAccess.s_FooClassTypeCctorCount != 1) + s_NumErrors++; + + Foo.s_longField1 = 0xff00; + Foo.s_longField2 = 0xff00; + Foo.s_longField3 = 0xff00; + if (TestFieldAccess.s_FooStructTypeCctorCount != 1) + s_NumErrors++; + + Bar.s_longField1 = 0xff00; + Bar.s_longField2 = 0xff00; + Bar.s_longField3 = 0xff00; + if (TestFieldAccess.s_BarCctorCount != 1) + s_NumErrors++; + } + + Console.WriteLine("Testing static fields on type Foo ..."); + { + FieldInfo fi = typeof(Foo).GetField("s_intField"); + Verify((int)11223344, (int)fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_stringField"); + Verify("abcd", (string)fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_floatField"); + Verify(12.34f, (float)fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_objectField"); + Verify("123", fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_kvp"); + var result = (KeyValuePair)fi.GetValue(null); + Verify("1122", result.Key); + Verify("3344", result.Value); + + typeof(Foo).GetField("s_stringField").SetValue(null, "ThisIsAString1"); + typeof(Foo).GetField("s_objectField").SetValue(null, "ThisIsAString2"); + typeof(Foo).GetField("s_kvp").SetValue(null, new KeyValuePair("ThisIs", "AString")); + Verify("ThisIsAString1", (string)Foo.s_stringField); + Verify("ThisIsAString2", (string)Foo.s_objectField); + Verify("ThisIs", (string)Foo.s_kvp.Key); + Verify("AString", (string)Foo.s_kvp.Value); + } + + Console.WriteLine("Testing static fields on type Foo ..."); + { + FieldInfo fi = typeof(Foo).GetField("s_intField"); + Verify(44332211, (int)fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_stringField"); + Verify("dcba", (string)fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_floatField"); + Verify(43.21f, (float)fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_objectField"); + Verify("321", fi.GetValue(null)); + + fi = typeof(Foo).GetField("s_kvp"); + var result = (KeyValuePair)fi.GetValue(null); + Verify("4433", result.Key); + Verify("2211", result.Value); + + typeof(Foo).GetField("s_stringField").SetValue(null, "ThisIsAString3"); + typeof(Foo).GetField("s_objectField").SetValue(null, "ThisIsAString4"); + typeof(Foo).GetField("s_kvp").SetValue(null, new KeyValuePair("ThisIs1", "AString1")); + Verify("ThisIsAString3", (string)Foo.s_stringField); + Verify("ThisIsAString4", (string)Foo.s_objectField); + Verify("ThisIs1", (string)Foo.s_kvp.Key); + Verify("AString1", (string)Foo.s_kvp.Value); + } + + Console.WriteLine("Testing static fields on type Bar ..."); + { + FieldInfo fi = typeof(Bar).GetField("s_intField"); + Verify(778899, (int)fi.GetValue(null)); + + fi = typeof(Bar).GetField("s_stringField"); + Verify("xxyyzz", (string)fi.GetValue(null)); + + fi = typeof(Bar).GetField("s_floatField"); + Verify(88.99f, (float)fi.GetValue(null)); + + fi = typeof(Bar).GetField("s_objectField"); + Verify("890", fi.GetValue(null)); + + fi = typeof(Bar).GetField("s_kvp"); + var result = (KeyValuePair)fi.GetValue(null); + Verify("7788", result.Key); + Verify("8899", result.Value); + + typeof(Bar).GetField("s_stringField").SetValue(null, "ThisIsAString5"); + typeof(Bar).GetField("s_objectField").SetValue(null, "ThisIsAString6"); + typeof(Bar).GetField("s_kvp").SetValue(null, new KeyValuePair("ThisIs2", "AString2")); + Verify("ThisIsAString5", (string)Bar.s_stringField); + Verify("ThisIsAString6", (string)Bar.s_objectField); + Verify("ThisIs2", (string)Bar.s_kvp.Key); + Verify("AString2", (string)Bar.s_kvp.Value); + } + } + + private static void TestInstanceFields() + { + Foo fooClassType = new Foo + { + m_intField = 1212, + m_stringField = "2323", + m_floatField = 34.34f, + m_objectField = "4545", + }; + + Foo fooStructType = new Foo + { + m_intField = 2323, + m_stringField = "3434", + m_floatField = 45.45f, + m_objectField = "5656", + }; + + Bar bar = new Bar + { + m_intField = 3434, + m_stringField = "4545", + m_floatField = 56.56f, + m_objectField = "6767", + }; + + Console.WriteLine("Testing instance fields on type Foo ..."); + { + FieldInfo fi = typeof(Foo).GetField("m_intField"); + Verify(1212, (int)fi.GetValue(fooClassType)); + + fi = typeof(Foo).GetField("m_stringField"); + Verify("2323", (string)fi.GetValue(fooClassType)); + + fi = typeof(Foo).GetField("m_floatField"); + Verify(34.34f, (float)fi.GetValue(fooClassType)); + + fi = typeof(Foo).GetField("m_objectField"); + Verify("4545", fi.GetValue(fooClassType)); + + typeof(Foo).GetField("m_stringField").SetValue(fooClassType, "ThisIsAString7"); + typeof(Foo).GetField("m_objectField").SetValue(fooClassType, "ThisIsAString8"); + Verify("ThisIsAString7", (string)fooClassType.m_stringField); + Verify("ThisIsAString8", (string)fooClassType.m_objectField); + } + + Console.WriteLine("Testing instance fields on type Foo ..."); + { + FieldInfo fi = typeof(Foo).GetField("m_intField"); + Verify(2323, (int)fi.GetValue(fooStructType)); + + fi = typeof(Foo).GetField("m_stringField"); + Verify("3434", (string)fi.GetValue(fooStructType)); + + fi = typeof(Foo).GetField("m_floatField"); + Verify(45.45f, (float)fi.GetValue(fooStructType)); + + fi = typeof(Foo).GetField("m_objectField"); + Verify("5656", fi.GetValue(fooStructType)); + + typeof(Foo).GetField("m_stringField").SetValue(fooStructType, "ThisIsAString9"); + typeof(Foo).GetField("m_objectField").SetValue(fooStructType, "ThisIsAString10"); + Verify("ThisIsAString9", (string)fooStructType.m_stringField); + Verify("ThisIsAString10", (string)fooStructType.m_objectField); + } + + Console.WriteLine("Testing instance fields on type Bar ..."); + { + FieldInfo fi = typeof(Bar).GetField("m_intField"); + Verify(3434, (int)fi.GetValue(bar)); + + fi = typeof(Bar).GetField("m_stringField"); + Verify("4545", (string)fi.GetValue(bar)); + + fi = typeof(Bar).GetField("m_floatField"); + Verify(56.56f, (float)fi.GetValue(bar)); + + fi = typeof(Bar).GetField("m_objectField"); + Verify("6767", fi.GetValue(bar)); + + typeof(Bar).GetField("m_stringField").SetValue(bar, "ThisIsAString11"); + typeof(Bar).GetField("m_objectField").SetValue(bar, "ThisIsAString12"); + Verify("ThisIsAString11", (string)bar.m_stringField); + Verify("ThisIsAString12", (string)bar.m_objectField); + } + } + + private static void TestUnconstructedTypes() + { + // Testing for compilation failures due to references to unused static bases + // See: https://github.com/dotnet/corert/issues/3211 + var a = typeof(UnconstructedTypeInstantiator).ToString(); + var b = typeof(UnconstructedTypeInstantiator).ToString(); + } + + public static void Run() + { + TestStaticFields(); + TestInstanceFields(); + TestDynamicStaticFields(); + TestDynamicInvokeStubs(); + TestUnconstructedTypes(); + + if (s_NumErrors != 0) + throw new Exception(s_NumErrors + " errors!"); + } + } + + // Regression test for https://github.com/dotnet/corert/issues/3659 + class TestNativeLayoutGeneration + { +#pragma warning disable 649 // s_ref was never assigned + private static object s_ref; +#pragma warning restore 649 + + class Used + { + [MethodImpl(MethodImplOptions.NoInlining)] + public virtual string DoStuff() + { + return "Used"; + } + } + + class Unused : Used + { + [MethodImpl(MethodImplOptions.NoInlining)] + public override string DoStuff() + { + return "Unused " + typeof(T).ToString(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public void Blagh() + { + } + } + + public static void Run() + { + new Used().DoStuff(); + + try + { + // Call an instance method on something we never allocated, but overrides a used virtual. + // This asserted the compiler when trying to build a template for Unused<__Canon>. + ((Unused)s_ref).Blagh(); + } + catch (NullReferenceException) + { + return; + } + + throw new Exception(); + } + } + + class TestInterfaceVTableTracking + { + class Gen { } + + interface IFoo + { + Array Frob(); + } + + class GenericBase : IFoo + { + public Array Frob() + { + return new Gen[1, 1]; + } + } + + class Derived : GenericBase> + { + } + + static volatile IFoo> s_foo; + + public static void Run() + { + // This only really tests whether we can compile this. + s_foo = new Derived(); + Array arr = s_foo.Frob(); + arr.SetValue(new Gen>(), new int[] { 0, 0 }); + } + } + + class TestClassVTableTracking + { + class Unit { } + + class Gen + { + public virtual int Test() + { + return 42; + } + } + + static int Call() + { + return new Gen().Test(); + } + + public static void Run() + { + // This only really tests whether we can compile this. + Call(); + } + } + + class TestNullableCasting + { + struct Mine { } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CallWithNullable(object m) + { + return m is T; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool CallWithReferenceType(object m) + { + return m is Nullable>; + } + + public static void Run() + { + if (!CallWithNullable>>(new Mine())) + throw new Exception(); + + if (CallWithNullable>>(new Mine())) + throw new Exception(); + + if (!CallWithReferenceType(new Mine())) + throw new Exception(); + + if (CallWithReferenceType(new Mine())) + throw new Exception(); + + if (!(((object)new Mine()) is Nullable>)) + throw new Exception(); + } + } + + class TestVariantCasting + { + private delegate T GenericDelegate(); + + class Base { } + class Derived : Base { } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsInstanceOfGenericDelegateOf(object o) + { + return o is GenericDelegate; + } + + public static void Run() + { + GenericDelegate del = () => null; + if (!IsInstanceOfGenericDelegateOf(del)) + throw new Exception(); + } + } + + class TestByRefLikeVTables + { + class Atom { } + + ref struct RefStruct + { + public override bool Equals(object o) => o is Atom; + public override int GetHashCode() => 0; + + public override string ToString() + { + return typeof(T).ToString(); + } + } + + public static void Run() + { + // This is a regression test making sure we can build a vtable for the byref-like type. + // The vtable is necessary for a generic dictionary lookup in the ToString method. + // Method bodies of Equals and GetHashCode become reachable through the magical + // "unboxing" thunks we generate for byref-like types, and only through them. + RefStruct r = default; + if (r.ToString() != "System.String") + throw new Exception(); + } + } + + class TestDevirtualization + { + interface IDevirt + { + int GetAndSet(int x); + } + + struct Devirt : IDevirt + { + public int X; + + public int GetAndSet(int x) + { + int result = X; + X = x; + return result; + } + } + + interface IGenericDevirt + { + int GetAndSet(int x); + Type GetTheType(); + } + + struct GenericDevirt : IGenericDevirt + { + public int X; + + public int GetAndSet(int x) + { + int result = X; + X = x; + return result; + } + + public Type GetTheType() + { + return typeof(T); + } + } + + static void DoSimpleDevirt() + { + // This will potentially transform to a direct call + int result = ((IDevirt)new Devirt { X = 123 }).GetAndSet(456); + if (result != 123) + throw new Exception(); + } + + static void DoSimpleDevirtBoxed() + { + object o = new Devirt { X = 123 }; + + // Force o to be boxed no matter what + o.ToString(); + + // This will potentially transform to a direct call + int result = ((IDevirt)o).GetAndSet(456); + if (result != 123) + throw new Exception(); + + if (((Devirt)o).X != 456) + throw new Exception(); + } + + static void DoGenericDevirt() + { + // This will potentially transform to a direct call + int result1 = ((IGenericDevirt)new GenericDevirt { X = 123 }).GetAndSet(456); + if (result1 != 123) + throw new Exception(); + + // This will potentially transform to a direct call + Type result2 = ((IGenericDevirt)new GenericDevirt()).GetTheType(); + if (result2 != typeof(string)) + throw new Exception(); + } + + static void DoGenericDevirtBoxed() + { + object o1 = new GenericDevirt { X = 123 }; + + // Force o1 to be boxed no matter what + o1.ToString(); + + // This will potentially transform to a direct call + int result1 = ((IGenericDevirt)o1).GetAndSet(456); + if (result1 != 123) + throw new Exception(); + + if (((GenericDevirt)o1).X != 456) + throw new Exception(); + + object o2 = new GenericDevirt { X = 123 }; + + // Force o2 to be boxed no matter what + o2.ToString(); + + // This will potentially transform to a direct call + Type result2 = ((IGenericDevirt)o2).GetTheType(); + if (result2 != typeof(string)) + throw new Exception(); + } + + static void DoGenericDevirtShared() + { + // This will potentially transform to a direct call + int result1 = ((IGenericDevirt)new GenericDevirt { X = 123 }).GetAndSet(456); + if (result1 != 123) + throw new Exception(); + + // This will potentially transform to a direct call + Type result2 = ((IGenericDevirt)new GenericDevirt()).GetTheType(); + if (result2 != typeof(T[])) + throw new Exception(); + } + + static void DoGenericDevirtBoxedShared() + { + object o1 = new GenericDevirt { X = 123 }; + + // Force o1 to be boxed no matter what + o1.ToString(); + + // This will potentially transform to a direct call + int result1 = ((IGenericDevirt)o1).GetAndSet(456); + if (result1 != 123) + throw new Exception(); + + if (((GenericDevirt)o1).X != 456) + throw new Exception(); + + object o2 = new GenericDevirt { X = 123 }; + + // Force o2 to be boxed no matter what + o2.ToString(); + + // This will potentially transform to a direct call + Type result2 = ((IGenericDevirt)o2).GetTheType(); + if (result2 != typeof(T[])) + throw new Exception(); + } + + public static void Run() + { + DoSimpleDevirt(); + DoSimpleDevirtBoxed(); + DoGenericDevirt(); + DoGenericDevirtBoxed(); + DoGenericDevirtShared(); + DoGenericDevirtBoxedShared(); + } + } + + class TestGenericInlining + { + class NeverSeenInstantiated { } + + class AnotherNeverSeenInstantiated { } + + class NeverAllocatedIndirection + { + public string GetString() => new AnotherNeverSeenInstantiated().ToString(); + } + + class NeverAllocated + { + static NeverAllocatedIndirection s_indirection = null; + + public string GetString() => new NeverSeenInstantiated().ToString(); + public string GetStringIndirect() => s_indirection.GetString(); + } + + class Dummy { } + + static NeverAllocated s_neverAllocated = null; + + class GenericInline + { + public GenericInline() + { + _arr = (T)(object)new string[1] { "ohai" }; + } + T _arr; + public T GetArr() => _arr; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static object InnerTest(object o, object dummy) => o; + + static object OtherTest() => null; + + [MethodImpl(MethodImplOptions.NoInlining)] + static object Test(GenericInline t) + { + return InnerTest(t.GetArr()[0], OtherTest()); + } + + public static void Run() + { + // We're just making sure the compiler doesn't crash. + // Both of the calls below are expected to get inlined by an optimized codegen, + // triggering interesting behaviors in the dependency analysis of the scanner + // that runs before compilation. + if (s_neverAllocated != null) + { + Console.WriteLine(s_neverAllocated.GetString()); + Console.WriteLine(s_neverAllocated.GetStringIndirect()); + } + + // Regression test for https://github.com/dotnet/corert/issues/7625 + if ((string)Test(new GenericInline()) != "ohai") + throw new Exception(); + } + } } interface IGenInterface -- 2.7.4