From 014a08403950ea606d32f2a16b5fa18ce241ffe3 Mon Sep 17 00:00:00 2001 From: triton Date: Wed, 20 Feb 2013 14:44:08 +0000 Subject: [PATCH] Added the new C# backend (still a work-in-progress). --- .../Generators/CSharp/CSharpGenerator.cs | 39 +++ .../Generators/CSharp/CSharpTextTemplate.cs | 314 ++++++++++++++++++ .../Generators/CSharp/CSharpTypePrinter.cs | 238 +++++++++++++ 3 files changed, 591 insertions(+) create mode 100644 src/Generator/Generators/CSharp/CSharpGenerator.cs create mode 100644 src/Generator/Generators/CSharp/CSharpTextTemplate.cs create mode 100644 src/Generator/Generators/CSharp/CSharpTypePrinter.cs diff --git a/src/Generator/Generators/CSharp/CSharpGenerator.cs b/src/Generator/Generators/CSharp/CSharpGenerator.cs new file mode 100644 index 00000000..8ba50b14 --- /dev/null +++ b/src/Generator/Generators/CSharp/CSharpGenerator.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using Cxxi.Types; + +namespace Cxxi.Generators.CSharp +{ + public class CSharpGenerator : Generator + { + private readonly ITypePrinter typePrinter; + + public CSharpGenerator(Driver driver) : base(driver) + { + typePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Library); + Type.TypePrinter = typePrinter; + } + + void WriteTemplate(TextTemplate template) + { + var file = Path.GetFileNameWithoutExtension(template.unit.FileName) + + Driver.Options.WrapperSuffix + "." + + template.FileExtension; + + var path = Path.Combine(Driver.Options.OutputDir, file); + + template.Generate(); + + Console.WriteLine(" Generated '" + file + "'."); + File.WriteAllText(Path.GetFullPath(path), template.ToString()); + } + + public override bool Generate(TranslationUnit unit) + { + var template = new CSharpTextTemplate(Driver, unit); + WriteTemplate(template); + + return true; + } + } +} diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs new file mode 100644 index 00000000..3865b121 --- /dev/null +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -0,0 +1,314 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Cxxi.Types; + +namespace Cxxi.Generators.CSharp +{ + public class CSharpTextTemplate : TextTemplate + { + public ITypePrinter TypePrinter { get; set; } + + public CSharpTextTemplate(Driver driver, TranslationUnit unit) + : base(driver, unit) + { + TypePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Library); + } + + // from https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs + private static string[] keywords = new string[] + { + "abstract", "event", "new", "struct", "as", "explicit", "null", "switch", "base", "extern", + "this", "false", "operator", "throw", "break", "finally", "out", "true", + "fixed", "override", "try", "case", "params", "typeof", "catch", "for", + "private", "foreach", "protected", "checked", "goto", "public", + "unchecked", "class", "if", "readonly", "unsafe", "const", "implicit", "ref", + "continue", "in", "return", "using", "virtual", "default", + "interface", "sealed", "volatile", "delegate", "internal", "do", "is", + "sizeof", "while", "lock", "stackalloc", "else", "static", "enum", + "namespace", + "object", "bool", "byte", "float", "uint", "char", "ulong", "ushort", + "decimal", "int", "sbyte", "short", "double", "long", "string", "void", + "partial", "yield", "where" + }; + + public static string SafeIdentifier(string proposedName) + { + proposedName = + new string(((IEnumerable) proposedName).Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()); + return keywords.Contains(proposedName) ? "@" + proposedName : proposedName; + } + + public override string FileExtension + { + get { return "cs"; } + } + + public override void Generate() + { + GenerateStart(); + + WriteLine("using System;"); + WriteLine("using System.Runtime.InteropServices;"); + NewLine(); + + WriteLine("namespace {0}", SafeIdentifier(Driver.Options.LibraryName)); + WriteStartBraceIndent(); + + GenerateDeclarations(); + + WriteCloseBraceIndent(); + } + + public void GenerateStart() + { + if (Transform != null) + { + Transform.GenerateStart(this); + return; + } + + WriteLine("//----------------------------------------------------------------------------"); + WriteLine("// This is autogenerated code by Cxxi."); + WriteLine("// Do not edit this file or all your changes will be lost after re-generation."); + WriteLine("//----------------------------------------------------------------------------"); + } + + public void GenerateDeclarations() + { + // Generate all the enum declarations for the module. + foreach (var @enum in unit.Enums) + { + if (@enum.Ignore || @enum.IsIncomplete) + continue; + + GenerateEnum(@enum); + NewLine(); + } + + // Generate all the typedef declarations for the module. + foreach (var typedef in unit.Typedefs) + { + if (typedef.Ignore) continue; + + if (!GenerateTypedef(typedef)) + continue; + + NewLine(); + } + + // Generate all the struct/class declarations for the module. + foreach (var @class in unit.Classes) + { + if (@class.Ignore) continue; + + GenerateClass(@class); + NewLine(); + } + + if (unit.HasFunctions) + { + WriteLine("public partial class " + SafeIdentifier(Options.LibraryName)); + WriteStartBraceIndent(); + + // Generate all the function declarations for the module. + foreach (var function in unit.Functions) + GenerateFunction(function); + + WriteCloseBraceIndent(); + } + } + + public void GenerateDeclarationCommon(Declaration T) + { + GenerateSummary(T.BriefComment); + GenerateDebug(T); + } + + #region Classes + + public void GenerateClass(Class @class) + { + if (@class.Ignore) return; + GenerateDeclarationCommon(@class); + + GenerateClassProlog(@class); + + NewLine(); + WriteStartBraceIndent(); + + if (!@class.IsOpaque) + GenerateClassFields(@class); + + WriteCloseBraceIndent(); + } + + public void GenerateClassProlog(Class @class) + { + if (@class.IsUnion) + WriteLine("[StructLayout(LayoutKind.Explicit)]"); + + Write("public unsafe "); + + if (@class.IsAbstract) + Write("abstract "); + + Write("class {0}", SafeIdentifier(@class.Name)); + + if (@class.HasBase) + Write(" : {0}", SafeIdentifier(@class.Bases[0].Class.Name)); + } + + public void GenerateClassFields(Class @class) + { + foreach (var field in @class.Fields) + { + if (field.Ignore) continue; + + GenerateDeclarationCommon(field); + if (@class.IsUnion) + WriteLine("[FieldOffset({0})]", field.Offset); + WriteLine("public {0} {1};", field.Type, SafeIdentifier(field.Name)); + } + } + + #endregion + + public bool GenerateTypedef(TypedefDecl typedef) + { + if (typedef.Ignore) + return false; + + GenerateDeclarationCommon(typedef); + + FunctionType func; + TagType tag; + + if (typedef.Type.IsPointerToPrimitiveType(PrimitiveType.Void) + || typedef.Type.IsPointerTo(out tag)) + { + WriteLine("public class " + SafeIdentifier(typedef.Name) + @" { }"); + } + else if (typedef.Type.IsPointerTo(out func)) + { + //WriteLine("public {0};", + // string.Format(func.ToDelegateString(), SafeIdentifier(T.Name))); + } + else if (typedef.Type.IsEnumType()) + { + // Already handled in the parser. + return false; + } + else + { + Console.WriteLine("Unhandled typedef type: {0}", typedef); + return false; + } + + return true; + } + + public void GenerateEnum(Enumeration @enum) + { + if (@enum.Ignore) return; + GenerateDeclarationCommon(@enum); + + if (@enum.IsFlags) + WriteLine("[Flags]"); + + Write("public enum {0}", SafeIdentifier(@enum.Name)); + + var typeName = TypePrinter.VisitPrimitiveType(@enum.BuiltinType.Type, + new TypeQualifiers()); + + if (@enum.BuiltinType.Type != PrimitiveType.Int32) + Write(" : {0}", typeName); + + NewLine(); + + WriteStartBraceIndent(); + for (var i = 0; i < @enum.Items.Count; ++i) + { + var item = @enum.Items[i]; + GenerateInlineSummary(item.Comment); + + Write(item.ExplicitValue + ? string.Format("{0} = {1}", SafeIdentifier(item.Name), item.Value) + : string.Format("{0}", SafeIdentifier(item.Name))); + + if (i < @enum.Items.Count - 1) + Write(","); + + NewLine(); + } + WriteCloseBraceIndent(); + } + + public void GenerateFunction(Function function) + { + if(function.Ignore) return; + GenerateDeclarationCommon(function); + + Write("[DllImport(\"{0}.dll\", ", SafeIdentifier(Library.SharedLibrary)); + Write("CallingConvention = CallingConvention.{0}, ", ToCSharpCallConv(function.CallingConvention)); + WriteLine("EntryPoint=\"{0}\")]", function.Mangled); + + Write("public unsafe static extern {0} {1}(", function.ReturnType, + SafeIdentifier(function.Name)); + + for(var i = 0; i < function.Parameters.Count; ++i) + { + var param = function.Parameters[i]; + Write("{0} {1}", param.Type, SafeIdentifier(param.Name)); + + if (i < function.Parameters.Count - 1) + Write(", "); + } + + WriteLine(");"); + } + + public static string ToCSharpCallConv(CallingConvention convention) + { + switch (convention) + { + case CallingConvention.Default: + return "Winapi"; + case CallingConvention.C: + return "Cdecl"; + case CallingConvention.StdCall: + return "StdCall"; + case CallingConvention.ThisCall: + return "ThisCall"; + case CallingConvention.FastCall: + return "FastCall"; + } + + return "Winapi"; + } + + public void GenerateDebug(Declaration decl) + { + if(Options.OutputDebug && !String.IsNullOrWhiteSpace(decl.DebugText)) + WriteLine("// DEBUG: " + decl.DebugText); + } + + public void GenerateSummary(string comment) + { + if(String.IsNullOrWhiteSpace(comment)) + return; + + WriteLine("/// "); + WriteLine("/// {0}", comment); + WriteLine("/// "); + } + + public void GenerateInlineSummary(string comment) + { + if(String.IsNullOrWhiteSpace(comment)) + return; + + WriteLine("/// {0}", comment); + } + } +} \ No newline at end of file diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs new file mode 100644 index 00000000..bc6e60b6 --- /dev/null +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -0,0 +1,238 @@ +using System; +using Cxxi.Types; + +namespace Cxxi.Generators.CSharp +{ + public class CSharpTypePrinter : ITypePrinter, IDeclVisitor + { + public Library Library { get; set; } + private readonly ITypeMapDatabase TypeMapDatabase; + + public CSharpTypePrinter(ITypeMapDatabase database, Library library) + { + TypeMapDatabase = database; + Library = library; + } + + public string VisitTagType(TagType tag, TypeQualifiers quals) + { + if (tag.Declaration == null) + return string.Empty; + + return VisitDeclaration(tag.Declaration, quals); + } + + public string VisitArrayType(ArrayType array, TypeQualifiers quals) + { + return string.Format("{0}[]", array.Type.Visit(this)); + + // C# only supports fixed arrays in unsafe sections + // and they are constrained to a set of built-in types. + } + + public string VisitFunctionType(FunctionType function, TypeQualifiers quals) + { + var arguments = function.Arguments; + var returnType = function.ReturnType; + var args = string.Empty; + + if (arguments.Count > 0) + args = GetArgumentsString(function, hasNames: false); + + if (returnType.IsPrimitiveType(PrimitiveType.Void)) + { + if (!string.IsNullOrEmpty(args)) + args = string.Format("<{0}>", args); + return string.Format("Action{0}", args); + } + + if (!string.IsNullOrEmpty(args)) + args = string.Format(", {0}", args); + + return string.Format("Func<{0}{1}>", returnType.Visit(this), args); + } + + public string VisitPointerType(PointerType pointer, TypeQualifiers quals) + { + var pointee = pointer.Pointee; + + if (pointee is FunctionType) + { + var function = pointee as FunctionType; + return string.Format("{0}", function.Visit(this, quals)); + } + + if (pointee.IsPrimitiveType(PrimitiveType.Void, walkTypedefs: true) || + pointee.IsPrimitiveType(PrimitiveType.UInt8, walkTypedefs: true)) + { + return "IntPtr"; + } + + if (pointee.IsPrimitiveType(PrimitiveType.Char) && quals.IsConst) + { + return "string"; + } + + return pointee.Visit(this, quals); + } + + public string VisitMemberPointerType(MemberPointerType member, + TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public string VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) + { + return VisitPrimitiveType(builtin.Type, quals); + } + + public string VisitTypedefType(TypedefType typedef, TypeQualifiers quals) + { + var decl = typedef.Declaration; + + TypeMap typeMap = null; + if (TypeMapDatabase.FindTypeMap(decl, out typeMap)) + { + return typeMap.Signature(); + } + + FunctionType func; + if (decl.Type.IsPointerTo(out func)) + { + // TODO: Use SafeIdentifier() + return string.Format("{0}", VisitDeclaration(decl)); + } + + return decl.Type.Visit(this); + } + + public string VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public string VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals) + { + switch (primitive) + { + case PrimitiveType.Bool: return "bool"; + case PrimitiveType.Void: return "void"; + case PrimitiveType.WideChar: return "char"; + case PrimitiveType.Int8: return "sbyte"; + case PrimitiveType.UInt8: return "byte"; + case PrimitiveType.Int16: return "short"; + case PrimitiveType.UInt16: return "ushort"; + case PrimitiveType.Int32: return "int"; + case PrimitiveType.UInt32: return "uint"; + case PrimitiveType.Int64: return "long"; + case PrimitiveType.UInt64: return "ulong"; + case PrimitiveType.Float: return "float"; + case PrimitiveType.Double: return "double"; + } + + throw new NotSupportedException(); + } + + public string VisitDeclaration(Declaration decl, TypeQualifiers quals) + { + return VisitDeclaration(decl); + } + + public string VisitDeclaration(Declaration decl) + { + var name = decl.Visit(this); + return string.Format("{0}", name); + } + + public string VisitClassDecl(Class @class) + { + return @class.Name; + } + + public string VisitFieldDecl(Field field) + { + throw new NotImplementedException(); + } + + public string VisitFunctionDecl(Function function) + { + throw new NotImplementedException(); + } + + public string VisitMethodDecl(Method method) + { + throw new NotImplementedException(); + } + + public string VisitParameterDecl(Parameter parameter) + { + throw new NotImplementedException(); + } + + public string VisitTypedefDecl(TypedefDecl typedef) + { + return typedef.Name; + } + + public string VisitEnumDecl(Enumeration @enum) + { + return @enum.Name; + } + + public string VisitClassTemplateDecl(ClassTemplate template) + { + throw new NotImplementedException(); + } + + public string VisitFunctionTemplateDecl(FunctionTemplate template) + { + throw new NotImplementedException(); + } + + public string VisitMacroDefinition(MacroDefinition macro) + { + throw new NotImplementedException(); + } + + public string VisitNamespace(Namespace @namespace) + { + throw new NotImplementedException(); + } + + public string GetArgumentsString(FunctionType function, bool hasNames) + { + var arguments = function.Arguments; + var s = string.Empty; + + for (var i = 0; i < arguments.Count; ++i) + { + s += GetArgumentString(arguments[i], hasNames); + if (i < arguments.Count - 1) + s += ", "; + } + + return s; + } + + public string GetArgumentString(Parameter arg, bool hasName) + { + var quals = new TypeQualifiers { IsConst = arg.IsConst }; + + var type = arg.Type.Visit(this, quals); + var name = arg.Name; + + if (hasName && !string.IsNullOrEmpty(name)) + return string.Format("{0} {1}", type, name); + + return type; + } + + public string ToDelegateString(FunctionType function) + { + return string.Format("delegate {0} {{0}}({1})", + function.ReturnType.Visit(this), + GetArgumentsString(function, hasNames: true)); + } + } +} \ No newline at end of file