xamarin-macios/tests/test-libraries/testgenerator.cs

539 строки
18 KiB
C#

using System;
using System.IO;
using System.Linq;
using System.Text;
static class C {
[Flags]
enum Architecture
{
None = 0,
Sim32 = 1,
Sim64 = 2,
Arm32 = 4,
Armv7k = 8,
// Arm64 is never stret
}
// X86: structs > 8 + structs with 3 members.
// X64: structs > 16
// ARM32: all structs, except those matching an integral platform type (i.e. a struct with a single int, but not a struct with a single float).
// ARM64: never
// armv7k: > 16, except homogeneous types with no more than 4 elements (i.e. structs with 3 or 4 doubles).
// the numbers below are bitmasks of Architecture values.
static string [] structs_and_stret = {
/* integral types */
"c:0", "cc:4", "ccc:5", "cccc:4",
"s:0", "ss:4", "sss:5", "ssss:4",
"i:0", "ii:4", "iii:5", "iiii:5", "iiiii:15",
"l:4", "ll:5", "lll:15", "llll:15", "lllll:15",
/* floating point types */
"f:4", "ff:4", "fff:5", "ffff:5", "fffff:15",
"d:4", "dd:5", "ddd:7", "dddd:7", "ddddd:15",
/* mixed types */
"if:4", "fi:4", // 8 bytes
"iff:5", // 12 bytes
"iiff:5", // 16 bytes
"id:5", "di:5", // 16 bytes
"iid:5", // 16 bytes
"idi:15", // 16 bytes on i386 and 24 bytes on x86_64 (due to alignment)
"ddi:15", // 24 bytes
"didi:15", // 24 bytes on 32-bit arch, 32 bytes on 64-bit arch
"idid:15", // 24 bytes on 32-bit arch, 32 bytes on 64-bit arch
"dldl:15",
"ldld:15",
"fifi:5",
"ifif:5",
};
static string [] structs = structs_and_stret.Select ((v) => v.IndexOf (':') >= 0 ? v.Substring (0, v.IndexOf (':')) : v).ToArray ();
static Architecture [] strets = structs_and_stret.Select ((v) => v.IndexOf (':') >= 0 ? (Architecture) int.Parse (v.Substring (v.IndexOf (':') + 1)) : Architecture.None).ToArray ();
static string GetNativeName (char t)
{
switch (t) {
case 'f': return "float";
case 'd': return "double";
case 'c': return "char";
case 's': return "short";
case 'i': return "int";
case 'l': return "long long";
default:
throw new NotImplementedException ();
}
}
static string GetManagedName (char t)
{
switch (t) {
case 'f': return "float";
case 'd': return "double";
case 'c': return "byte";
case 's': return "short";
case 'i': return "int";
case 'l': return "long";
default:
throw new NotImplementedException ();
}
}
static string GetValue (char t, int i, int multiplier = 1)
{
switch (t) {
case 'c':
case 's':
case 'i':
case 'l': return ((i + 1) * multiplier).ToString ();
case 'f': return (3.14f * (i + 1) * multiplier) + "f";
case 'd': return (1.23f * (i + 1) * multiplier).ToString ();
default:
throw new NotImplementedException ();
}
}
static void WriteLibTestStructH ()
{
var w = new StringBuilder ();
foreach (var s in structs) {
w.Append ($"struct S{s} {{ ");
for (int i = 0; i < s.Length; i++) {
w.Append (GetNativeName (s [i])).Append (" x").Append (i).Append ("; ");
}
w.AppendLine ($"}} S{s};");
}
File.WriteAllText ("libtest.structs.h", w.ToString ());
}
static void WriteLibTestDecompileM ()
{
var w = new StringBuilder ();
// This is code to be disassembled to see how it's compiled by clang
// to see if a particular structure is using objc_msgSend_stret or not.
//
// To disassemble:
// otool -vVt .libs/ios/libtest.armv7.o
//
// Then in the _decompile_me output, look for the _____* function call,
// matching the structure you want to check, and then backtrack until
// you see either an objc_msgSend or objc_msgSend_stret call, and you
// have your answer.
#if false
w.AppendLine ("extern \"C\" {");
foreach (var s in structs)
w.AppendLine ($"void _____________________________________{s} (struct S{s} x) __attribute__ ((optnone)) {{ }}");
w.AppendLine ("void decompile_me () __attribute__ ((optnone))");
w.AppendLine ("{");
w.AppendLine ("\tObjCRegistrarTest *obj = NULL;");
foreach (var s in structs) {
w.AppendLine ($"\t_____________________________________{s} ([obj PS{s}]);");
}
w.AppendLine ("}");
w.AppendLine ("}");
#endif
File.WriteAllText ("libtest.decompile.m", w.ToString ());
}
static void WriteLibTestPropertiesH ()
{
var w = new StringBuilder ();
foreach (var s in structs)
w.AppendLine ($"\t@property struct S{s} PS{s};");
File.WriteAllText ("libtest.properties.h", w.ToString ());
}
static void WriteApiDefinition ()
{
var w = new StringBuilder ();
w.AppendLine (@"using System;
#if !__WATCHOS__
using System.Drawing;
#endif
#if __UNIFIED__
using ObjCRuntime;
using Foundation;
using UIKit;
#else
using MonoTouch.ObjCRuntime;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
#endif
namespace Bindings.Test {
partial interface ObjCRegistrarTest {
");
foreach (var s in structs) {
w.AppendLine ($"\t\t[Export (\"PS{s}\")]");
w.AppendLine ($"\t\tS{s} PS{s} {{ get; set; }}");
w.AppendLine ();
}
w.AppendLine (@" }
}");
File.WriteAllText ("../bindings-test/ApiDefinition.generated.cs", w.ToString ());
}
static void WriteStructsAndEnums ()
{
var w = new StringBuilder ();
w.AppendLine (@"using System;
using System.Runtime.InteropServices;
#if !__UNIFIED__
using nint=System.Int32;
#endif
namespace Bindings.Test
{
");
foreach (var s in structs) {
w.AppendLine ($"\tpublic struct S{s} {{ ");
w.Append ("\t\t");
for (int i = 0; i < s.Length; i++) {
w.Append ("public ").Append (GetManagedName (s [i])).Append (" x").Append (i).Append ("; ");
}
w.AppendLine ();
w.Append ($"\t\tpublic override string ToString () {{ return $\"S{s} [");
for (int i = 0; i < s.Length; i++) {
w.Append ("{x").Append (i).Append ("};");
}
w.Length--;
w.AppendLine ("]\"; } ");
w.AppendLine ("\t}");
w.AppendLine ();
}
w.AppendLine (@"}");
File.WriteAllText ("../bindings-test/StructsAndEnums.generated.cs", w.ToString ());
}
static void WriteRegistrarTests ()
{
var w = new StringBuilder ();
w.AppendLine (@"
#if XAMCORE_2_0
using Foundation;
using ObjCRuntime;
using MonoTouchException=ObjCRuntime.RuntimeException;
using NativeException=Foundation.MonoTouchException;
#else
using MonoTouch;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
using MonoTouchException=MonoTouch.RuntimeException;
using NativeException=MonoTouch.Foundation.MonoTouchException;
#endif
using NUnit.Framework;
using Bindings.Test;
using XamarinTests.ObjCRuntime;
namespace MonoTouchFixtures.ObjCRuntime {
[TestFixture]
[Preserve (AllMembers = true)]
public class RegistrarTestGenerated {");
foreach (var s in structs) {
w.AppendLine ("\t\t[Test]");
w.AppendLine ($"\t\tpublic void Test_{s} ()");
w.AppendLine ("\t\t{");
w.AppendLine ("\t\t\tusing (var tc = new ObjCRegistrarTest ()) {");
w.AppendLine ($"\t\t\t\tvar s = tc.PS{s};");
for (int i = 0; i < s.Length; i++)
w.AppendLine ($"\t\t\t\tAssert.AreEqual (0, s.x{i}, \"pre-#{i}\");");
w.Append ($"\t\t\t\tvar k = new S{s} () {{ ");
for (int i = 0; i < s.Length; i++)
w.Append ($"x{i} = ").Append (GetValue (s [i], i)).Append (", ");
w.Length -= 2;
w.AppendLine ("};");
w.AppendLine ($"\t\t\t\ttc.PS{s} = k;");
w.AppendLine ($"\t\t\t\ts = tc.PS{s};");
for (int i = 0; i < s.Length; i++)
w.AppendLine ($"\t\t\t\tAssert.AreEqual (k.x{i}, s.x{i}, \"post-#{i}\");");
w.AppendLine ("\t\t\t}");
w.AppendLine ("\t\t}");
w.AppendLine ();
}
w.AppendLine (@" }
}");
File.WriteAllText ("../monotouch-test/ObjCRuntime/RegistrarTest.generated.cs", w.ToString ());
}
static void WriteTrampolineTests ()
{
var w = new StringBuilder ();
w.AppendLine (@"
using System;
using System.Runtime.InteropServices;
#if XAMCORE_2_0
using Foundation;
using ObjCRuntime;
#else
using MonoTouch;
using MonoTouch.Foundation;
using MonoTouch.ObjCRuntime;
#endif
using NUnit.Framework;
using Bindings.Test;
using XamarinTests.ObjCRuntime;
namespace MonoTouchFixtures.ObjCRuntime {
[TestFixture]
[Preserve (AllMembers = true)]
public class TrampolineTestGenerated {");
w.AppendLine ("\t\tconst string LIBOBJC_DYLIB = \"/usr/lib/libobjc.dylib\";");
w.AppendLine ();
w.AppendLine ("\t\t[Register (\"GeneratedStretTrampolines\")]");
w.AppendLine ("\t\t[Preserve (AllMembers = true)]");
w.AppendLine ("\t\tpublic class GeneratedStretTrampolines : NSObject {");
foreach (var s in structs) {
w.AppendLine ();
w.AppendLine ($"\t\t\t// {s}");
w.AppendLine ();
w.AppendLine ($"\t\t\t[Export (\"Test_{s}Struct\")]");
w.AppendLine ($"\t\t\tS{s} Test_{s}Struct ()");
w.AppendLine ($"\t\t\t{{");
w.AppendLine ($"\t\t\t\treturn {GenerateNewExpression (s, 1)};");
w.AppendLine ($"\t\t\t}}");
w.AppendLine ();
w.AppendLine ($"\t\t\t[Export (\"Test_Static{s}Struct\")]");
w.AppendLine ($"\t\t\tstatic S{s} Test_Static{s}Struct ()");
w.AppendLine ($"\t\t\t{{");
w.AppendLine ($"\t\t\t\treturn {GenerateNewExpression (s, 2)};");
w.AppendLine ($"\t\t\t}}");
w.AppendLine ();
w.AppendLine ($"\t\t\tS{s} Test_{s}StructProperty {{");
w.AppendLine ($"\t\t\t\t[Export (\"Test_{s}StructProperty\")]");
w.AppendLine ($"\t\t\t\tget {{ return {GenerateNewExpression (s, 3)}; }}");
w.AppendLine ($"\t\t\t}}");
w.AppendLine ();
w.AppendLine ($"\t\t\tstatic S{s} Test_Static{s}StructProperty {{");
w.AppendLine ($"\t\t\t\t[Export (\"Test_Static{s}StructProperty\")]");
w.AppendLine ($"\t\t\t\tget {{ return {GenerateNewExpression (s, 4)}; }}");
w.AppendLine ($"\t\t\t}}");
w.AppendLine ();
w.AppendLine ($"\t\t\t[Export (\"Test_{s}Struct_out_double:\")]");
w.AppendLine ($"\t\t\tS{s} Test_{s}Struct (out double x0)");
w.AppendLine ($"\t\t\t{{");
w.AppendLine ($"\t\t\t\tx0 = 3.14;");
w.AppendLine ($"\t\t\t\treturn {GenerateNewExpression (s, 5)};");
w.AppendLine ($"\t\t\t}}");
w.AppendLine ();
w.AppendLine ($"\t\t\t[Export (\"Test_Static{s}Struct_out_float:\")]");
w.AppendLine ($"\t\t\tstatic S{s} Test_Static{s}Struct (out float x0)");
w.AppendLine ($"\t\t\t{{");
w.AppendLine ($"\t\t\t\tx0 = 3.15f;");
w.AppendLine ($"\t\t\t\treturn {GenerateNewExpression (s, 6)};");
w.AppendLine ($"\t\t\t}}");
}
w.AppendLine ("\t\t}");
foreach (var s in structs) {
if (s.Length == 1 || s.Contains ('c'))
continue; // our trampolines don't currently like structs with a single member, nor char members
bool never;
w.AppendLine ();
w.AppendLine ($"\t\t[Test]");
w.AppendLine ($"\t\tpublic void Test_{s} ()");
w.AppendLine ($"\t\t{{");
w.AppendLine ($"\t\t\tIntPtr class_ptr = Class.GetHandle (typeof (GeneratedStretTrampolines));");
w.AppendLine ($"\t\t\tS{s} rv = new S{s} ();");
w.AppendLine ($"\t\t\tdouble rvd;");
w.AppendLine ($"\t\t\tfloat rvf;");
w.AppendLine ($"\t\t\tusing (var obj = new GeneratedStretTrampolines ()) {{");
WriteStretConditions (w, s, out never);
if (never) {
w.AppendLine ($"\t\t\t\trv = S{s}_objc_msgSend (obj.Handle, new Selector (\"Test_{s}Struct\").Handle);");
} else {
w.AppendLine ($"\t\t\t\t\tS{s}_objc_msgSend_stret (out rv, obj.Handle, new Selector (\"Test_{s}Struct\").Handle);");
w.AppendLine ($"\t\t\t\t}} else {{");
w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (obj.Handle, new Selector (\"Test_{s}Struct\").Handle);");
w.AppendLine ($"\t\t\t\t}}");
}
w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 1)}).ToString (), rv.ToString (), \"a\");");
w.AppendLine ();
WriteStretConditions (w, s, out never);
if (never) {
w.AppendLine ($"\t\t\t\trv = S{s}_objc_msgSend (class_ptr, new Selector (\"Test_Static{s}Struct\").Handle);");
} else {
w.AppendLine ($"\t\t\t\t\tS{s}_objc_msgSend_stret (out rv, class_ptr, new Selector (\"Test_Static{s}Struct\").Handle);");
w.AppendLine ($"\t\t\t\t}} else {{");
w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (class_ptr, new Selector (\"Test_Static{s}Struct\").Handle);");
w.AppendLine ($"\t\t\t\t}}");
}
w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 2)}).ToString (), rv.ToString (), \"a\");");
w.AppendLine ();
WriteStretConditions (w, s, out never);
if (never) {
w.AppendLine ($"\t\t\t\trv = S{s}_objc_msgSend (obj.Handle, new Selector (\"Test_{s}StructProperty\").Handle);");
} else {
w.AppendLine ($"\t\t\t\t\tS{s}_objc_msgSend_stret (out rv, obj.Handle, new Selector (\"Test_{s}StructProperty\").Handle);");
w.AppendLine ($"\t\t\t\t}} else {{");
w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (obj.Handle, new Selector (\"Test_{s}StructProperty\").Handle);");
w.AppendLine ($"\t\t\t\t}}");
}
w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 3)}).ToString (), rv.ToString (), \"a\");");
w.AppendLine ();
WriteStretConditions (w, s, out never);
if (never) {
w.AppendLine ($"\t\t\t\trv = S{s}_objc_msgSend (class_ptr, new Selector (\"Test_Static{s}StructProperty\").Handle);");
} else {
w.AppendLine ($"\t\t\t\t\tS{s}_objc_msgSend_stret (out rv, class_ptr, new Selector (\"Test_Static{s}StructProperty\").Handle);");
w.AppendLine ($"\t\t\t\t}} else {{");
w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend (class_ptr, new Selector (\"Test_Static{s}StructProperty\").Handle);");
w.AppendLine ($"\t\t\t\t}}");
}
w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 4)}).ToString (), rv.ToString (), \"a\");");
w.AppendLine ();
w.AppendLine ($"\t\t\t\trvd = 0;");
WriteStretConditions (w, s, out never);
if (never) {
w.AppendLine ($"\t\t\t\trv = S{s}_objc_msgSend_out_double (obj.Handle, new Selector (\"Test_{s}Struct_out_double:\").Handle, out rvd);");
} else {
w.AppendLine ($"\t\t\t\t\tS{s}_objc_msgSend_stret_out_double (out rv, obj.Handle, new Selector (\"Test_{s}Struct_out_double:\").Handle, out rvd);");
w.AppendLine ($"\t\t\t\t}} else {{");
w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend_out_double (obj.Handle, new Selector (\"Test_{s}Struct_out_double:\").Handle, out rvd);");
w.AppendLine ($"\t\t\t\t}}");
}
w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 5)}).ToString (), rv.ToString (), \"a\");");
w.AppendLine ($"\t\t\t\tAssert.AreEqual (3.14, rvd, \"double out\");");
w.AppendLine ();
w.AppendLine ($"\t\t\t\trvf = 0;");
WriteStretConditions (w, s, out never);
if (never) {
w.AppendLine ($"\t\t\t\trv = S{s}_objc_msgSend_out_float (class_ptr, new Selector (\"Test_Static{s}Struct_out_float:\").Handle, out rvf);");
} else {
w.AppendLine ($"\t\t\t\t\tS{s}_objc_msgSend_stret_out_float (out rv, class_ptr, new Selector (\"Test_Static{s}Struct_out_float:\").Handle, out rvf);");
w.AppendLine ($"\t\t\t\t}} else {{");
w.AppendLine ($"\t\t\t\t\trv = S{s}_objc_msgSend_out_float (class_ptr, new Selector (\"Test_Static{s}Struct_out_float:\").Handle, out rvf);");
w.AppendLine ($"\t\t\t\t}}");
}
w.AppendLine ($"\t\t\t\tAssert.AreEqual (({GenerateNewExpression (s, 6)}).ToString (), rv.ToString (), \"a\");");
w.AppendLine ($"\t\t\t\tAssert.AreEqual (3.15f, rvf, \"float out\");");
w.AppendLine ();
w.AppendLine ($"\t\t\t}}");
w.AppendLine ($"\t\t}}");
// objc_msgSend variants
w.AppendLine ();
w.AppendLine ($"\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend\")]");
w.AppendLine ($"\t\textern static S{s} S{s}_objc_msgSend (IntPtr received, IntPtr selector);");
w.AppendLine ();
w.AppendLine ($"\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend\")]");
w.AppendLine ($"\t\textern static S{s} S{s}_objc_msgSend_out_float (IntPtr received, IntPtr selector, out float x1);");
w.AppendLine ();
w.AppendLine ($"\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend\")]");
w.AppendLine ($"\t\textern static S{s} S{s}_objc_msgSend_out_double (IntPtr received, IntPtr selector, out double x1);");
w.AppendLine ();
w.AppendLine ($"\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend_stret\")]");
w.AppendLine ($"\t\textern static void S{s}_objc_msgSend_stret (out S{s} rv, IntPtr received, IntPtr selector);");
w.AppendLine ();
w.AppendLine ($"\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend_stret\")]");
w.AppendLine ($"\t\textern static void S{s}_objc_msgSend_stret_out_float (out S{s} rv, IntPtr received, IntPtr selector, out float x1);");
w.AppendLine ();
w.AppendLine ($"\t\t[DllImport (LIBOBJC_DYLIB, EntryPoint=\"objc_msgSend_stret\")]");
w.AppendLine ($"\t\textern static void S{s}_objc_msgSend_stret_out_double (out S{s} rv, IntPtr received, IntPtr selector, out double x1);");
}
w.AppendLine (@" }
}");
File.WriteAllText ("../monotouch-test/ObjCRuntime/TrampolineTest.generated.cs", w.ToString ());
}
static void WriteStretConditions (StringBuilder w, string s, out bool never)
{
var stret = strets [Array.IndexOf (structs, s)];
if (stret == Architecture.None) {
never = true;
} else {
never = false;
w.Append ("\t\t\t\tif (");
if ((stret & Architecture.Arm32) == Architecture.Arm32)
w.Append ("TrampolineTest.IsArm32 || ");
if ((stret & Architecture.Armv7k) == Architecture.Armv7k)
w.Append ("TrampolineTest.IsArmv7k || ");
if ((stret & Architecture.Sim32) == Architecture.Sim32)
w.Append ("TrampolineTest.IsSim32 || ");
if ((stret & Architecture.Sim64) == Architecture.Sim64)
w.Append ("TrampolineTest.IsSim64 || ");
w.Length -= 4;
w.AppendLine (") {");
}
}
static string GenerateNewExpression (string s, int multiplier = 1)
{
var sb = new StringBuilder ();
sb.Append ($"new S{s} () {{ ");
for (int i = 0; i < s.Length; i++)
sb.Append ("x").Append (i).Append (" = ").Append (GetValue (s [i], i, multiplier)).Append (", ");
sb.Length -= 2;
sb.Append (" }");
return sb.ToString ();
}
static void Main ()
{
while (Path.GetFileName (Environment.CurrentDirectory) != "test-libraries")
Environment.CurrentDirectory = Path.GetDirectoryName (Environment.CurrentDirectory);
/* native code */
WriteLibTestStructH ();
WriteLibTestDecompileM ();
WriteLibTestPropertiesH ();
/* binding code */
WriteApiDefinition ();
WriteStructsAndEnums ();
/* tests */
WriteRegistrarTests ();
WriteTrampolineTests ();
Console.WriteLine ("Generated test files");
}
}