using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; namespace CppSharp.Generators.CSharp { public static class Helpers { // from https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs private static readonly 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 GeneratedIdentifier(string id) { return "__" + id; } public static string SafeIdentifier(string id) { id = new string(((IEnumerable)id) .Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()); return Keywords.Contains(id) ? "@" + id : id; } 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 class CSharpTextTemplate : TextTemplate { public CSharpTypePrinter TypePrinter { get; set; } public override string FileExtension { get { return "cs"; } } public CSharpTextTemplate(Driver driver, TranslationUnit unit) : base(driver, unit) { TypePrinter = new CSharpTypePrinter(driver.TypeDatabase, driver.Library); } #region Identifiers public string QualifiedIdentifier(Declaration decl) { if (Options.GenerateLibraryNamespace) return string.Format("{0}::{1}", Options.OutputNamespace, decl.QualifiedName); return string.Format("{0}", decl.QualifiedName); } public static string GeneratedIdentifier(string id) { return Helpers.GeneratedIdentifier(id); } public static string SafeIdentifier(string id) { return Helpers.SafeIdentifier(id); } #endregion public override void Generate() { GenerateHeader(); WriteLine("using System;"); WriteLine("using System.Runtime.InteropServices;"); WriteLine("using System.Security;"); NewLine(); if (Options.GenerateLibraryNamespace) { WriteLine("namespace {0}", SafeIdentifier(Driver.Options.OutputNamespace)); WriteStartBraceIndent(); } GenerateNamespace(TranslationUnit); if (Options.GenerateLibraryNamespace) WriteCloseBraceIndent(); } public void GenerateHeader() { if (Transform != null) { Transform.GenerateStart(this); return; } WriteLine("//----------------------------------------------------------------------------"); WriteLine("// This is autogenerated code by CppSharp."); WriteLine("// Do not edit this file or all your changes will be lost after re-generation."); WriteLine("//----------------------------------------------------------------------------"); } private void GenerateNamespace(Namespace @namespace) { bool isGlobalNamespace = @namespace is TranslationUnit; if (!isGlobalNamespace) { WriteLine("namespace {0}", @namespace.Name); WriteStartBraceIndent(); } // Generate all the enum declarations for the module. foreach (var @enum in @namespace.Enums) { if (@enum.Ignore || @enum.IsIncomplete) continue; NewLineIfNeeded(); GenerateEnum(@enum); NeedNewLine(); } // Generate all the typedef declarations for the module. foreach (var typedef in @namespace.Typedefs) { if (typedef.Ignore) continue; NewLineIfNeeded(); if (!GenerateTypedef(typedef)) continue; NeedNewLine(); } // Generate all the struct/class declarations for the module. foreach (var @class in @namespace.Classes) { if (@class.Ignore || @class.IsIncomplete) continue; if (@class.IsDependent) continue; NewLineIfNeeded(); GenerateClass(@class); NeedNewLine(); } if (@namespace.HasFunctions) { NewLineIfNeeded(); WriteLine("public partial class {0}{1}", SafeIdentifier(Options.LibraryName), TranslationUnit.FileNameWithoutExtension); WriteStartBraceIndent(); // Generate all the function declarations for the module. foreach (var function in @namespace.Functions) GenerateFunction(function); WriteCloseBraceIndent(); } foreach(var childNamespace in @namespace.Namespaces) GenerateNamespace(childNamespace); if (!isGlobalNamespace) WriteCloseBraceIndent(); } public void GenerateDeclarationCommon(Declaration decl) { GenerateSummary(decl.BriefComment); GenerateDebug(decl); } 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); } #region Classes public void GenerateClass(Class @class) { if (@class.Ignore || @class.IsIncomplete) return; GenerateDeclarationCommon(@class); if (@class.IsUnion) { // TODO: How to do wrapping of unions? throw new NotImplementedException(); } GenerateClassProlog(@class); NewLine(); WriteStartBraceIndent(); if (!@class.IsOpaque) { GenerateClassInternals(@class); if (ShouldGenerateClassNativeField(@class)) { NewLineIfNeeded(); WriteLine("public System.IntPtr Instance { get; protected set; }"); NeedNewLine(); } GenerateClassConstructors(@class); GenerateClassFields(@class); GenerateClassMethods(@class); GenerateClassVariables(@class); GenerateClassProperties(@class); } WriteCloseBraceIndent(); } public void GenerateClassInternals(Class @class) { var typePrinter = TypePrinter as CSharpTypePrinter; typePrinter.PushContext(CSharpTypePrinterContextKind.Native); WriteLine("[StructLayout(LayoutKind.Explicit, Size = {0})]", @class.Layout.Size); Write("internal "); if (@class.HasBaseClass) Write("new "); WriteLine("struct Internal"); WriteStartBraceIndent(); ResetNewLine(); foreach (var field in @class.Fields) { NewLineIfNeeded(); if (field.Ignore) continue; WriteLine("[FieldOffset({0})]", field.OffsetInBytes); WriteLine("public {0} {1};", field.Type, SafeIdentifier(field.OriginalName)); NeedNewLine(); } foreach (var ctor in @class.Constructors) { if (ctor.IsCopyConstructor || ctor.IsMoveConstructor) continue; NewLineIfNeeded(); GenerateFunction(ctor, @class); } foreach (var method in @class.Methods) { if (CheckIgnoreMethod(@class, method)) continue; if (method.IsConstructor) continue; if (method.IsSynthetized) continue; NewLineIfNeeded(); GenerateFunction(method, @class); } WriteCloseBraceIndent(); NeedNewLine(); typePrinter.PopContext(); } private void GenerateStructMarshaling(Class @class) { GenerateStructMarshalingFields(@class); } private void GenerateStructMarshalingFields(Class @class) { foreach (var @base in @class.Bases) { if (!@base.IsClass || @base.Class.Ignore) continue; GenerateStructMarshalingFields(@base.Class); } foreach (var field in @class.Fields) { if (CheckIgnoreField(@class, field)) continue; bool isRefClass; var nativeField = GetFieldLocation(field, "native", CSharpTypePrinterContextKind.ManagedPointer, out isRefClass); var ctx = new CSharpMarshalContext(Driver) { Kind = CSharpMarshalKind.NativeField, ArgName = field.Name, ReturnVarName = nativeField, ReturnType = field.QualifiedType }; var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); field.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); WriteLine("{0} = {1};", field.Name, marshal.Context.Return); } } public bool ShouldGenerateClassNativeField(Class @class) { if (!@class.IsRefType) return false; Class baseClass = null; if (@class.HasBaseClass) baseClass = @class.Bases[0].Class; var hasRefBase = baseClass != null && baseClass.IsRefType && !baseClass.Ignore; var hasIgnoredBase = baseClass != null && baseClass.Ignore; return !@class.HasBase || !hasRefBase || hasIgnoredBase; } public void GenerateClassProlog(Class @class) { if (@class.IsUnion) WriteLine("[StructLayout(LayoutKind.Explicit)]"); Write("public unsafe "); if (Options.GeneratePartialClasses) Write("partial "); Write(@class.IsValueType ? "struct " : "class "); Write("{0}", SafeIdentifier(@class.Name)); var needsBase = @class.HasBaseClass && !@class.IsValueType && !@class.Bases[0].Class.IsValueType && !@class.Bases[0].Class.Ignore; if (needsBase || @class.IsRefType) Write(" : "); if (needsBase) { Write("{0}", SafeIdentifier(@class.Bases[0].Class.Name)); if (@class.IsRefType) Write(", "); } if (@class.IsRefType) Write("IDisposable"); } public void GenerateClassFields(Class @class) { // Handle value-type inheritance if (@class.IsValueType) { foreach (var @base in @class.Bases) { Class baseClass; if (!@base.Type.IsTagDecl(out baseClass)) continue; if (!baseClass.IsValueType || baseClass.Ignore) continue; GenerateClassFields(baseClass); } } foreach (var field in @class.Fields) { if (CheckIgnoreField(@class, field)) continue; NewLineIfNeeded(); if (@class.IsRefType) { GenerateFieldProperty(field); } else { GenerateDeclarationCommon(field); if (@class.IsUnion) WriteLine("[FieldOffset({0})]", field.Offset); WriteLine("public {0} {1};", field.Type, SafeIdentifier(field.Name)); } NeedNewLine(); } } private void GenerateFieldProperty(Field field) { var @class = field.Class; GenerateDeclarationCommon(field); WriteLine("public {0} {1}", field.Type, SafeIdentifier(field.Name)); WriteStartBraceIndent(); GeneratePropertyGetter(field, @class); NewLine(); GeneratePropertySetter(field, @class); WriteCloseBraceIndent(); } enum PropertyMethodKind { Getter, Setter } private string GetFieldLocation(Field field, string instance, CSharpTypePrinterContextKind kind, out bool isRefClass) { isRefClass = false; var typePrinter = TypePrinter as CSharpTypePrinter; typePrinter.PushContext(kind); var type = field.Type.Visit(typePrinter); typePrinter.PopContext(); var fieldType = field.Type.Desugar(); Class fieldClass; if (fieldType.IsTagDecl(out fieldClass) && fieldClass.IsRefType) isRefClass = true; TagType tagType; if (fieldType.IsPointerTo(out tagType) && tagType.IsTagDecl(out fieldClass) && fieldClass.IsRefType) isRefClass = true; if (CSharpTypePrinter.IsConstCharString(field.QualifiedType)) isRefClass = true; FunctionType functionType; if (fieldType.IsPointerTo(out functionType)) isRefClass = true; if (isRefClass) type = "void*"; var location = string.Format("({0} + {1})", instance, field.OffsetInBytes); if (type.TypeMap == null) location = string.Format("*({0}*){1}", type, location); return location; } private string GetPropertyLocation(T decl, PropertyMethodKind kind, out bool isRefClass) where T : Declaration, ITypedDecl { isRefClass = false; if (decl is Variable) { var @var = decl as Variable; string symbol; if (!FindMangledDeclSymbol(@var, out symbol)) return string.Empty; NativeLibrary library; Driver.LibrarySymbols.FindLibraryBySymbol(symbol, out library); return string.Format("CppSharp.SymbolResolver.ResolveSymbol(\"{0}\", \"{1}\")", Path.GetFileNameWithoutExtension(library.FileName), symbol); } else if (decl is Field) { var field = decl as Field; var location = GetFieldLocation(field, "Instance", CSharpTypePrinterContextKind.Managed, out isRefClass); if (isRefClass && kind == PropertyMethodKind.Getter) location = string.Format("new System.IntPtr({0})", location); return location; } throw new NotSupportedException(); } private void GeneratePropertySetter(T decl, Class @class) where T : Declaration, ITypedDecl { WriteLine("set"); WriteStartBraceIndent(); var param = new Parameter { Name = "value", QualifiedType = decl.QualifiedType }; var ctx = new CSharpMarshalContext(Driver) { Parameter = param, ArgName = param.Name, }; if (decl is Function) { var function = decl as Function; var parameters = new List { param }; GenerateInternalFunctionCall(function, @class, parameters); } else { bool isRefClass; var variable = GetPropertyLocation(decl, PropertyMethodKind.Setter, out isRefClass); var marshal = new CSharpMarshalManagedToNativePrinter(ctx); param.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); Write("{0} = {1}", variable, marshal.Context.Return); if (isRefClass) Write(".ToPointer()"); WriteLine(";"); } WriteCloseBraceIndent(); } private void GeneratePropertyGetter(T decl, Class @class) where T : Declaration, ITypedDecl { WriteLine("get"); WriteStartBraceIndent(); var @return = string.Empty; if (decl is Function) { var function = decl as Function; GenerateInternalFunctionCall(function, @class); @return = "ret"; } else { bool isRefClass; @return = GetPropertyLocation(decl, PropertyMethodKind.Getter, out isRefClass); var ctx = new CSharpMarshalContext(Driver) { ArgName = decl.Name, ReturnVarName = @return, ReturnType = decl.QualifiedType }; var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); decl.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); WriteLine("return {0};", marshal.Context.Return); } WriteCloseBraceIndent(); } public void GenerateClassMethods(Class @class) { var staticMethods = new List(); foreach (var method in @class.Methods) { if (CheckIgnoreMethod(@class, method)) continue; if (method.IsConstructor) continue; if (method.IsStatic) { staticMethods.Add(method); continue; } NewLineIfNeeded(); GenerateMethod(method, @class); NeedNewLine(); } foreach (var method in staticMethods) { NewLineIfNeeded(); GenerateMethod(method, @class); NeedNewLine(); } } public void GenerateClassVariables(Class @class) { foreach (var variable in @class.Variables) { if (variable.Ignore) continue; if (variable.Access != AccessSpecifier.Public) continue; var type = variable.Type; NewLineIfNeeded(); GenerateVariable(@class, type, variable); NeedNewLine(); } } private void GenerateClassProperties(Class @class) { foreach (var prop in @class.Properties) { if (prop.Ignore) continue; NewLineIfNeeded(); WriteLine("public {0} {1}", prop.Type, prop.Name); WriteStartBraceIndent(); GeneratePropertyGetter(prop.GetMethod, @class); NeedNewLine(); if (prop.SetMethod != null) { NewLineIfNeeded(); GeneratePropertySetter(prop.SetMethod, @class); } WriteCloseBraceIndent(); NeedNewLine(); } } private void GenerateVariable(Class @class, Type type, Variable variable) { WriteLine("public static {0} {1}", type, variable.Name); WriteStartBraceIndent(); GeneratePropertyGetter(variable, @class); if (!variable.QualifiedType.Qualifiers.IsConst) GeneratePropertySetter(variable, @class); WriteCloseBraceIndent(); } #endregion #region Constructors public void GenerateClassConstructors(Class @class) { NewLineIfNeeded(); // Output a default constructor that takes the native pointer. GenerateNativeConstructor(@class); NeedNewLine(); foreach (var ctor in @class.Constructors) { if (CheckIgnoreMethod(@class, ctor)) continue; NewLineIfNeeded(); GenerateMethod(ctor, @class); NeedNewLine(); } if (@class.IsRefType) GenerateDisposeMethods(@class); } private void GenerateDisposeMethods(Class @class) { var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; // Generate the IDispose Dispose() method. if (!hasBaseClass) { NewLineIfNeeded(); WriteLine("public void Dispose()"); WriteStartBraceIndent(); WriteLine("Dispose(disposing: true);"); WriteLine("GC.SuppressFinalize(this);"); WriteCloseBraceIndent(); NeedNewLine(); } // Generate Dispose(bool) method NewLineIfNeeded(); Write("protected "); Write(hasBaseClass ? "override " : "virtual "); WriteLine("void Dispose(bool disposing)"); WriteStartBraceIndent(); if (ShouldGenerateClassNativeField(@class)) WriteLine("Marshal.FreeHGlobal(Instance);"); if (hasBaseClass) WriteLine("base.Dispose(disposing);"); WriteCloseBraceIndent(); NeedNewLine(); } private void GenerateNativeConstructor(Class @class) { WriteLine("internal {0}(System.IntPtr native)", SafeIdentifier(@class.Name)); var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; if (hasBaseClass) WriteLineIndent(": base(native)"); WriteStartBraceIndent(); if (@class.IsRefType) { if (ShouldGenerateClassNativeField(@class)) WriteLine("Instance = native;"); } else { GenerateStructMarshaling(@class); } WriteCloseBraceIndent(); } private bool GenerateClassConstructorBase(Class @class, Method method) { var hasBase = @class.HasBase && !@class.Bases[0].Class.Ignore; if (hasBase && !@class.IsValueType) { PushIndent(); Write(": this(", QualifiedIdentifier(@class.Bases[0].Class)); if (method != null) Write("IntPtr.Zero"); else Write("native"); WriteLine(")"); PopIndent(); } return hasBase; } #endregion #region Methods / Functions public void GenerateMethod(Method method, Class @class) { GenerateDeclarationCommon(method); Write("public "); if (method.Kind == CXXMethodKind.Operator) Write("static "); var functionName = GetFunctionIdentifier(method, @class); if (method.IsConstructor || method.IsDestructor) Write("{0}(", functionName); else Write("{0} {1}(", method.ReturnType, functionName); GenerateMethodParameters(method, @class); WriteLine(")"); if (method.Kind == CXXMethodKind.Constructor) GenerateClassConstructorBase(@class, method); WriteStartBraceIndent(); if (@class.IsRefType) { if (method.IsConstructor) { GenerateClassConstructor(method, @class); } else if (method.IsOperator) { GeneratedOperator(method, @class); } else { GenerateInternalFunctionCall(method, @class); } } else if (@class.IsValueType) { if (method.IsConstructor) { GenerateInternalFunctionCall(method, @class); } else if (method.IsOperator) { GeneratedOperator(method, @class); } else { GenerateInternalFunctionCall(method, @class); } } WriteCloseBraceIndent(); } private static string GetOperatorOverloadPair(CXXOperatorKind kind) { switch (kind) { case CXXOperatorKind.EqualEqual: return "!="; case CXXOperatorKind.ExclaimEqual: return "=="; case CXXOperatorKind.Less: return ">"; case CXXOperatorKind.Greater: return "<"; case CXXOperatorKind.LessEqual: return ">="; case CXXOperatorKind.GreaterEqual: return "<="; default: throw new NotSupportedException(); } } private void GeneratedOperator(Method method, Class @class) { if (method.IsSynthetized) { var @operator = GetOperatorOverloadPair(method.OperatorKind); WriteLine("return !({0} {1} {2});", method.Parameters[0].Name, @operator, method.Parameters[1].Name); return; } GenerateInternalFunctionCall(method, @class); } private void GenerateClassConstructor(Method method, Class @class) { var @params = GenerateFunctionParamsMarshal(method.Parameters, method); WriteLine("Instance = Marshal.AllocHGlobal({0});", @class.Layout.Size); Write("Internal.{0}(Instance", GetFunctionNativeIdentifier(method, @class)); if (@params.Any()) Write(", "); GenerateFunctionParams(@params); WriteLine(");"); } private void GenerateValueTypeConstructorCall(Method method, Class @class) { var names = new List(); foreach (var param in method.Parameters) { var ctx = new CSharpMarshalContext(Driver) { Parameter = param, ArgName = param.Name, }; var marshal = new CSharpMarshalManagedToNativePrinter(ctx); param.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); names.Add(marshal.Context.Return); } WriteLine("var {0} = new {1}.Internal();", GeneratedIdentifier("instance"), @class.QualifiedName); GenerateInternalFunctionCall(method, @class); GenerateValueTypeConstructorCallFields(@class); } private void GenerateValueTypeConstructorCallFields(Class @class) { foreach (var @base in @class.Bases) { if (!@base.IsClass || @base.Class.Ignore) continue; var baseClass = @base.Class; GenerateValueTypeConstructorCallFields(baseClass); } foreach (var field in @class.Fields) { if (CheckIgnoreField(@class, field)) continue; var nativeField = string.Format("*({0}*) (&{1} + {2})", field.Type, GeneratedIdentifier("instance"), field.OffsetInBytes); var ctx = new CSharpMarshalContext(Driver) { ReturnVarName = nativeField, ReturnType = field.QualifiedType }; var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); field.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); WriteLine("this.{0} = {1};", field.Name, marshal.Context.Return); } } public void GenerateInternalFunctionCall(Function function, Class @class, List parameters = null) { if (parameters == null) parameters = function.Parameters; var functionName = string.Format("Internal.{0}", GetFunctionNativeIdentifier(function, @class)); GenerateFunctionCall(functionName, parameters, function, @class); } public void GenerateFunctionCall(string functionName, List parameters, Function function, Class @class = null) { var retType = function.ReturnType; var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); var method = function as Method; var needsInstance = method != null && !method.IsStatic && !method.IsOperator; var isValueType = @class != null && @class.IsValueType; var needsFixedThis = needsInstance && isValueType; Class retClass = null; if (function.HasHiddenStructParameter) { function.ReturnType.Type.IsTagDecl(out retClass); WriteLine("var {0} = new {1}.Internal();", GeneratedIdentifier("udt"), retClass.OriginalName); retType.Type = new BuiltinType(PrimitiveType.Void); needsReturn = false; } var @params = GenerateFunctionParamsMarshal(parameters, function); var names = (from param in @params where !param.Param.Ignore select param.Name).ToList(); if (function.HasHiddenStructParameter) { var name = string.Format("new IntPtr(&{0})", GeneratedIdentifier("udt")); names.Insert(0, name); } if (needsInstance) { names.Insert(0, needsFixedThis ? string.Format("new System.IntPtr({0})", GeneratedIdentifier("instance")) : "Instance"); } if (needsFixedThis) { WriteLine("fixed({0}* {1} = &this)", @class.QualifiedName, GeneratedIdentifier("instance")); WriteStartBraceIndent(); } if (needsReturn) Write("var ret = "); WriteLine("{0}({1});", functionName, string.Join(", ", names)); var cleanups = new List(); GenerateFunctionCallOutParams(@params, cleanups); foreach (var param in @params) { var context = param.Context; if (context == null) continue; if (!string.IsNullOrWhiteSpace(context.Cleanup)) cleanups.Add(context.Cleanup); } foreach (var cleanup in cleanups) { Write(cleanup); } if (needsReturn) { var ctx = new CSharpMarshalContext(Driver) { ArgName = "ret", ReturnVarName = "ret", ReturnType = retType }; var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); function.ReturnType.Type.Visit(marshal, function.ReturnType.Qualifiers); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); WriteLine("return {0};", marshal.Context.Return); } if (function.HasHiddenStructParameter) { WriteLine("var ret = new {0}();", retClass.Name); if (retClass.IsValueType) WriteLine("*({0}.Internal*) &ret = {1};", retClass.Name, GeneratedIdentifier("udt")); else WriteLine("*({0}.Internal*) ret.Instance.ToPointer() = {1};", retClass.Name, GeneratedIdentifier("udt")); WriteLine("return ret;"); } if (needsFixedThis) WriteCloseBraceIndent(); } private void GenerateFunctionCallOutParams(IEnumerable @params, ICollection cleanups) { foreach (var paramInfo in @params) { var param = paramInfo.Param; if (param.Usage != ParameterUsage.Out && param.Usage != ParameterUsage.InOut) continue; var nativeVarName = paramInfo.Name; var ctx = new CSharpMarshalContext(Driver) { ArgName = nativeVarName, ReturnVarName = nativeVarName, ReturnType = param.QualifiedType }; var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); param.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); WriteLine("{0} = {1};", param.Name, marshal.Context.Return); if (!string.IsNullOrWhiteSpace(marshal.CSharpContext.Cleanup)) cleanups.Add(marshal.CSharpContext.Cleanup); } } private static bool IsInstanceFunction(Function function) { var isInstanceFunction = false; var method = function as Method; if (method != null) isInstanceFunction = method.Conversion == MethodConversionKind.None; return isInstanceFunction; } public struct ParamMarshal { public string Name; public Parameter Param; public CSharpMarshalContext Context; } public void GenerateFunctionParams(List @params) { var names = @params.Select(param => param.Name).ToList(); Write(string.Join(", ", names)); } public List GenerateFunctionParamsMarshal(IEnumerable @params, Function function = null) { var marshals = new List(); var paramIndex = 0; foreach (var param in @params) { marshals.Add(GenerateFunctionParamMarshal(param, paramIndex, function)); paramIndex++; } return marshals; } private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex, Function function = null) { if (param.Type is BuiltinType) { return new ParamMarshal { Name = param.Name, Param = param }; } var argName = "arg" + paramIndex.ToString(CultureInfo.InvariantCulture); var paramMarshal = new ParamMarshal { Name = argName, Param = param }; if (param.Usage == ParameterUsage.Out) { //var paramType = param.Type; //if (paramType.IsReference()) // paramType = (paramType as PointerType).Pointee; //var typePrinter = new CppTypePrinter(Driver.TypeDatabase); //var type = paramType.Visit(typePrinter); //WriteLine("{0} {1};", type, argName); } else { var ctx = new CSharpMarshalContext(Driver) { Parameter = param, ParameterIndex = paramIndex, ArgName = argName, Function = function }; paramMarshal.Context = ctx; var marshal = new CSharpMarshalManagedToNativePrinter(ctx); param.Visit(marshal); if (string.IsNullOrEmpty(marshal.Context.Return)) throw new Exception("Cannot marshal argument of function"); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); WriteLine("var {0} = {1};", SafeIdentifier(argName), marshal.Context.Return); } return paramMarshal; } private void GenerateMethodParameters(Method method, Class @class) { var @params = new List(); for (var i = 0; i < method.Parameters.Count; ++i) { var param = method.Parameters[i]; if (param.Kind == ParameterKind.HiddenStructureReturn) continue; @params.Add(string.Format("{0} {1}", param.Type, SafeIdentifier(param.Name))); } Write(string.Join(", ", @params)); } #endregion public bool GenerateTypedef(TypedefDecl typedef) { if (typedef.Ignore) return false; GenerateDeclarationCommon(typedef); FunctionType function; 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 function)) { WriteLine("public {0};", string.Format(TypePrinter.VisitDelegate(function).Type, SafeIdentifier(typedef.Name))); NeedNewLine(); } 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 string GetOperatorIdentifier(CXXOperatorKind kind) { // These follow the order described in MSDN (Overloadable Operators). switch (kind) { // These unary operators can be overloaded case CXXOperatorKind.Plus: return "operator +"; case CXXOperatorKind.Minus: return "operator -"; case CXXOperatorKind.Exclaim: return "operator !"; case CXXOperatorKind.Tilde: return "operator ~"; case CXXOperatorKind.PlusPlus: return "operator ++"; case CXXOperatorKind.MinusMinus: return "operator --"; // These binary operators can be overloaded case CXXOperatorKind.Star: return "operator *"; case CXXOperatorKind.Slash: return "operator /"; case CXXOperatorKind.Percent: return "operator +"; case CXXOperatorKind.Amp: return "operator &"; case CXXOperatorKind.Pipe: return "operator |"; case CXXOperatorKind.Caret: return "operator ^"; case CXXOperatorKind.LessLess: return "operator <<"; case CXXOperatorKind.GreaterGreater: return "operator >>"; // The comparison operators can be overloaded case CXXOperatorKind.EqualEqual: return "operator =="; case CXXOperatorKind.ExclaimEqual: return "operator !="; case CXXOperatorKind.Less: return "operator <"; case CXXOperatorKind.Greater: return "operator >"; case CXXOperatorKind.LessEqual: return "operator <="; case CXXOperatorKind.GreaterEqual: return "operator >="; // Assignment operators cannot be overloaded case CXXOperatorKind.PlusEqual: case CXXOperatorKind.MinusEqual: case CXXOperatorKind.StarEqual: case CXXOperatorKind.SlashEqual: case CXXOperatorKind.PercentEqual: case CXXOperatorKind.AmpEqual: case CXXOperatorKind.PipeEqual: case CXXOperatorKind.CaretEqual: case CXXOperatorKind.LessLessEqual: case CXXOperatorKind.GreaterGreaterEqual: // The array indexing operator cannot be overloaded case CXXOperatorKind.Subscript: // The conditional logical operators cannot be overloaded case CXXOperatorKind.AmpAmp: case CXXOperatorKind.PipePipe: // These operators cannot be overloaded. case CXXOperatorKind.Equal: case CXXOperatorKind.Comma: case CXXOperatorKind.ArrowStar: case CXXOperatorKind.Arrow: case CXXOperatorKind.Call: case CXXOperatorKind.Conditional: case CXXOperatorKind.New: case CXXOperatorKind.Delete: case CXXOperatorKind.Array_New: case CXXOperatorKind.Array_Delete: default: throw new NotImplementedException(); } } public string GetFunctionIdentifier(Function function, Class @class) { var identifier = SafeIdentifier(function.Name); var printer = TypePrinter as CSharpTypePrinter; var isNativeContext = printer.ContextKind == CSharpTypePrinterContextKind.Native; var method = function as Method; if (method != null && method.Kind == CXXMethodKind.Operator) { if (isNativeContext) identifier = "Operator" + method.OperatorKind.ToString(); else identifier = GetOperatorIdentifier(method.OperatorKind); } var overloads = AST.Utils.GetFunctionOverloads(function, @class); var index = overloads.IndexOf(function); if (isNativeContext && index >= 0) identifier += index.ToString(); return identifier; } public string GetFunctionNativeIdentifier(Function function, Class @class = null) { var typePrinter = TypePrinter as CSharpTypePrinter; typePrinter.PushContext(CSharpTypePrinterContextKind.Native); var name = GetFunctionIdentifier(function, @class); typePrinter.PopContext(); return name; } bool FindMangledDeclLibrary(IMangledDecl decl, out NativeLibrary library) { string symbol; if (!FindMangledDeclSymbol(decl, out symbol)) { library = null; return false; } Driver.LibrarySymbols.FindLibraryBySymbol(symbol, out library); return true; } bool FindMangledDeclSymbol(IMangledDecl decl, out string symbol) { symbol = decl.Mangled; if (!Driver.LibrarySymbols.FindSymbol(ref symbol)) { Driver.Diagnostics.EmitError(DiagnosticId.SymbolNotFound, "Symbol not found: {0}", symbol); symbol = null; return false; } return true; } public void GenerateFunction(Function function, Class @class = null) { if(function.Ignore) return; NativeLibrary library; if (!FindMangledDeclLibrary(function, out library)) return; GenerateDeclarationCommon(function); WriteLine("[SuppressUnmanagedCodeSecurity]"); Write("[DllImport(\"{0}\", ", library.FileName); WriteLine("CallingConvention = CallingConvention.{0},", Helpers.ToCSharpCallConv(function.CallingConvention)); WriteLineIndent("EntryPoint=\"{0}\")]", function.Mangled); if (function.ReturnType.Type.Desugar().IsPrimitiveType(PrimitiveType.Bool)) WriteLine("[return: MarshalAsAttribute(UnmanagedType.I1)]"); var @params = new List(); var typePrinter = TypePrinter as CSharpTypePrinter; var retType = typePrinter.VisitParameterDecl(new Parameter() { QualifiedType = function.ReturnType }); var method = function as Method; if (method != null && !method.IsStatic) { @params.Add("System.IntPtr instance"); if (method.IsConstructor && Options.IsMicrosoftAbi) retType = "System.IntPtr"; } for(var i = 0; i < function.Parameters.Count; ++i) { var param = function.Parameters[i]; if (param.Kind == ParameterKind.OperatorParameter) continue; var typeName = param.Visit(typePrinter); var paramName = param.IsSynthetized ? GeneratedIdentifier(param.Name) : SafeIdentifier(param.Name); @params.Add(string.Format("{0} {1}", typeName, paramName)); } if (method != null && method.IsConstructor) if (Options.IsMicrosoftAbi && @class.Layout.HasVirtualBases) @params.Add("int " + GeneratedIdentifier("forBases")); WriteLine("public unsafe static extern {0} {1}({2});", retType, GetFunctionIdentifier(function, @class), string.Join(", ", @params)); } } internal class SymbolNotFoundException : Exception { public SymbolNotFoundException(string msg) : base(msg) {} } }