From 1c1f542107a67aa75c8bf72894bd5fa43dae35d0 Mon Sep 17 00:00:00 2001 From: Robert Xiao Date: Fri, 19 Jun 2020 05:52:09 -0700 Subject: [PATCH] Tests: Add new test assemblies for C# features (#36) * Add new test assemblies for C# features Three of these test assemblies go over several important features of C#, organized by C# language version. PartialGenericTypes stresses closed and partially closed generics. Finally, VTablesAndLayout tests the layout of classes when translated to C++, and also contains code which calls vtable and interface functions to test reverse engineering. * Tests: Update .csproj * Tests: Update .csproj Co-authored-by: Katy Coe --- Il2CppTests/Il2CppTests.csproj | 30 +-- Il2CppTests/TestSources/CSharp1.cs | 57 +++++ Il2CppTests/TestSources/CSharp2.cs | 100 ++++++++ Il2CppTests/TestSources/CSharp3.cs | 25 ++ .../TestSources/PartialGenericTypes.cs | 50 ++++ Il2CppTests/TestSources/VTablesAndLayout.cs | 218 ++++++++++++++++++ 6 files changed, 466 insertions(+), 14 deletions(-) create mode 100644 Il2CppTests/TestSources/CSharp1.cs create mode 100644 Il2CppTests/TestSources/CSharp2.cs create mode 100644 Il2CppTests/TestSources/CSharp3.cs create mode 100644 Il2CppTests/TestSources/PartialGenericTypes.cs create mode 100644 Il2CppTests/TestSources/VTablesAndLayout.cs diff --git a/Il2CppTests/Il2CppTests.csproj b/Il2CppTests/Il2CppTests.csproj index a416127..b2a1cd0 100644 --- a/Il2CppTests/Il2CppTests.csproj +++ b/Il2CppTests/Il2CppTests.csproj @@ -31,15 +31,9 @@ - - - - - - @@ -58,17 +52,25 @@ - - + + + + + + + + + + + + + + + - - - - - - + diff --git a/Il2CppTests/TestSources/CSharp1.cs b/Il2CppTests/TestSources/CSharp1.cs new file mode 100644 index 0000000..61abc38 --- /dev/null +++ b/Il2CppTests/TestSources/CSharp1.cs @@ -0,0 +1,57 @@ +using System; + +/* C# 1.0 feature test */ +namespace Il2CppTests.TestSources +{ + public class SimpleClass : SimpleInterface + { + public SimpleStruct ss; + public int i; + + public static SimpleStruct StaticFunc(SimpleStruct ss) { + Console.WriteLine(ss); + return new SimpleStruct(); + } + + public SimpleStruct InstanceFunc(SimpleStruct ss) { + Console.WriteLine(ss); + return this.ss; + } + + public int func(int val) { + return val + 42; + } + + public delegate SimpleStruct SimpleDelegate(SimpleStruct ss); + public event SimpleDelegate SimpleEvent; + public int SimpleProperty { + get { return 0; } + set { SimpleEvent(ss); } + } + } + + public struct SimpleStruct : SimpleInterface + { + public SimpleClass sc; + public int i; + + public static SimpleStruct StaticFunc(SimpleStruct ss) { + Console.WriteLine(ss); + return new SimpleStruct(); + } + + public SimpleClass InstanceFunc(SimpleStruct ss) { + Console.WriteLine(ss); + return this.sc; + } + + public int func(int val) { + return ((SimpleInterface)sc).func(val) + 13; + } + } + + public interface SimpleInterface + { + int func(int val); + } +} diff --git a/Il2CppTests/TestSources/CSharp2.cs b/Il2CppTests/TestSources/CSharp2.cs new file mode 100644 index 0000000..5662844 --- /dev/null +++ b/Il2CppTests/TestSources/CSharp2.cs @@ -0,0 +1,100 @@ +using System; + +/* C# 2.0 feature test */ +namespace Il2CppTests.TestSources +{ + public interface GenericInterface + { + T func(T v); + T genericMethod(T v, T2 w); + } + + public class GenericClass : GenericInterface + { + public T x; + + public T func(T v) { + return v; + } + + public T genericMethod(T v, T2 w) { + return x; + } + + public static T myMethod(T x) { + return x; + } + + public virtual bool returnBool() { + return true; + } + } + + public class DerivedGenericClass : GenericClass, GenericInterface + { + public T2 t2; + public T2 func(T2 v) { + return t2; + } + + public T2 genericMethod(T2 v, T3 w) { + return v; + } + + public T2 newGenericMethod() { + return t2; + } + + public static int Return42() { + return 42; + } + } + + public struct GenericStruct : GenericInterface where T : struct + { + public T a; + public T func(T v) { + throw new NotImplementedException(); + } + + public T genericMethod(T v, T2 w) { + throw new NotImplementedException(); + } + + public T? genericMethod2(T x) { + return (new GenericClass()).returnBool() ? null : (T?)x; + } + + public static GenericStruct ReturnStruct(T y) { + var res = new GenericStruct(); + res.a = y; + return res; + } + } + + public interface IVariance + { + T2 func(T1 v); + } + + public class UseGenerics + { + public static void Main(string[] args) { + GenericStruct[] arr = new GenericStruct[3]; + arr[0] = GenericStruct.ReturnStruct(3); + DerivedGenericClass gc = new DerivedGenericClass(); + string c = gc.func("oops"); + Console.WriteLine(c); + gc.genericMethod("hello", arr[0].genericMethod2(3) ?? 0); + GenericInterface s = gc; + s.genericMethod("goodbye", c); + GenericClass.myMethod(42); + IVariance q = null; + q.func("nope"); + + foreach (var v in arr) { + v.genericMethod(64, "nope"); + } + } + } +} diff --git a/Il2CppTests/TestSources/CSharp3.cs b/Il2CppTests/TestSources/CSharp3.cs new file mode 100644 index 0000000..950e3ed --- /dev/null +++ b/Il2CppTests/TestSources/CSharp3.cs @@ -0,0 +1,25 @@ +using System; +using System.Linq; + +/* C# 3.0 feature test */ +namespace Il2CppTests.TestSources +{ + public class FeatureTest + { + public string AutoProp { get; set; } + public void AnonType() { + var c = new { Value = 3, Message = "Nobody" }; + Console.WriteLine(c); + } + + public int Linq() { + var scores = new int[] { 1, 2, 3, 4 }; + var highScoresQuery = + from score in scores + where score > 80 + orderby score descending + select score; + return highScoresQuery.Count(); + } + } +} diff --git a/Il2CppTests/TestSources/PartialGenericTypes.cs b/Il2CppTests/TestSources/PartialGenericTypes.cs new file mode 100644 index 0000000..8d45193 --- /dev/null +++ b/Il2CppTests/TestSources/PartialGenericTypes.cs @@ -0,0 +1,50 @@ +using System; + +/* Test for concretized and partially concretized generics */ +namespace Il2CppTests.TestSources +{ + public class FunkyA + { + public AT at; + public FunkyA() { + } + public FunkyA(AT at) { + this.at = at; + } + public AT rt(AT x) { + return x; + } + } + + public class FunkyB : FunkyA + { + public BT1 bt1; + public BT2 bt2; + public BT2 rt1(BT1 x) { + return bt2; + } + public BT1 rt2(BT2 y) { + return bt1; + } + public FunkyA> rtx(FunkyA> z) { + return new FunkyA>(new FunkyA(bt1)); + } + } + + public class FunkyC : FunkyB + { + } + + public class FunkyTest + { + public FunkyA x; + public FunkyB y; + public FunkyC z; + void test() { + x.rt(0); + y.rt1(""); + y.rt2(0); + z.rtx(null); + } + } +} diff --git a/Il2CppTests/TestSources/VTablesAndLayout.cs b/Il2CppTests/TestSources/VTablesAndLayout.cs new file mode 100644 index 0000000..0af0601 --- /dev/null +++ b/Il2CppTests/TestSources/VTablesAndLayout.cs @@ -0,0 +1,218 @@ +using System; + +/* Test virtual method calls (VTables) and class layout */ +namespace Il2CppTests.TestSources +{ + public struct Vector3 + { + public float x, y, z; + } + + public class TestVTable + { + public interface TestInterface + { + int overrideme(); + } + + public interface TestInterface2 + { + int overrideme2(); + } + + public interface TestInterface3 + { + int overrideme3(); + } + + public interface IT1 + { + void f1(); + void f2(); + } + + public interface IT2 + { + void f1(); + void f3(); + } + + public interface IT3 : IT2, IT1 + { + new void f2(); + new void f1(); + } + + public class WeirdLayout1 + { + public ulong x; + public char y; + } + + public class WeirdLayout2 : WeirdLayout1 + { + public char z; + public short f; + } + + public struct TestStruct : TestInterface, TestInterface2, IT1, IT2 + { + public int x; + public int overrideme() { + return 42 + x; + } + + public int overrideme2() { + return 42000 * x; + } + + public void f1() { + + } + + public void f2() { + + } + + public void f3() { + + } + } + + public class TestClass : TestInterface, TestInterface3, IT3 + { + public int x; + public virtual int overrideme() { + return 64 - x; + } + public void normal1() { + + } + public virtual int overrideme3() { + return -1 + x; + } + public void normal2() { + + } + public void f1() { } + public void f2() { } + public void f3() { } + } + + public class TestClass2 : TestClass + { + public int y; + public override int overrideme() { + return 1 + y; + } + public new void normal2() { + + } + } + + public interface ITestGeneric + { + void genericFunc(T t); + } + + public class TestGeneric : ITestGeneric + { + public T m_t; + public T[] arr; + public int x; + public TestGeneric(T[] arr) { + this.arr = arr; + this.x = 0; + for (int i = 0; i < arr.Length; i++) { + x += arr[i].GetHashCode(); + } + } + public void genericFunc(T t) { + x += arr[0].Equals(t) ? 1 : 0; + } + } + + public class TestGeneric2 : TestGeneric + { + public T1 m_t1; + public T2 m_t2; + public TestGeneric2(T2[] t2) : base(t2) { + + } + public void secondGenericFunc(T1 t1, T2 t2) { + genericFunc(t2); + TestGeneric tg1 = new TestGeneric(null); + tg1.genericFunc(t1); + } + } + + public void overrideme(int x) { + Console.WriteLine(x); + } + public float takestruct(Vector3 a, Vector3 b, Vector3 c) { + return a.x + b.y + c.z; + } + public delegate void callit(int x); + public callit monkey; + + public int doit(TestInterface ti, TestInterface2 ti2, TestInterface3 ti3) { + return ti.overrideme() * 2 + ti2.overrideme2() + ti3.overrideme3(); + } + + public void calltypes(ref Vector3 vin, ref Vector3 vout, ref Vector3 vref, params Vector3[] vparams) { + vout = vin; + } + + public void callgeneric(ITestGeneric it, T t) { + it.genericFunc(t); + } + + public void callints(IT1 it1, IT2 it2, IT3 it3) { + it1.f1(); + it1.f2(); + it2.f1(); + it2.f3(); + it3.f1(); + it3.f2(); + it3.f3(); + } + + public int[] intProperty { get; set; } + public int[] intProp2 { get { return intArray; } set { intArray = value; } } + private int[] intArray; + public object[] objArray; + + void Start() { + if (monkey != null) + monkey(0); + TestClass i1 = new TestClass2(); + i1.overrideme(); + i1.overrideme3(); + i1.normal2(); + TestStruct i2 = new TestStruct(); + i2.overrideme(); + i2.overrideme2(); + doit(i1, i2, i1); + doit(i2, i2, i1); + callints(i2, i2, i1); + int res = 0; + for (int i = 0; i < intArray.Length; i++) { + res += intArray[i]; + } + for (int i = 0; i < objArray.Length; i++) { + res += (int)objArray[i]; + } + ITestGeneric x1 = new TestGeneric(new int[] { 1, 2, 3 }); + ITestGeneric x2 = new TestGeneric(new object[] { new object() }); + callgeneric(x1, 0); + callgeneric(x2, "foo"); + ITestGeneric x3 = new TestGeneric2(new string[] { "as", "de" }); + new WeirdLayout2().f = 3; + x3.genericFunc("bar"); + } + + void Update() { + overrideme(0); + } + } +}