Example #1
0
        /// <param name="dllImportPath">will be used as the first argument in [DllImport]. Can be a path to some constant</param>
        public static void Generate(TypeMapper mapper, TemplateManager templateManager, string @namespace, string dllImportPath, string outCFile, string outCsFile)
        {
            var csFileSb = new StringBuilder();
            var cFileSb  = new StringBuilder();

            foreach (CppClassContainer cppClass in mapper.GetAllClasses())
            {
                // Header for C types:
                cFileSb.AppendLine();
                cFileSb.AppendLine();
                cFileSb.Append("/************* ");
                cFileSb.Append(cppClass.IsGlobal ? "Global functions:" : cppClass.Class.GetFullTypeName());
                cFileSb.Append(" *************/");
                cFileSb.AppendLine();
                cFileSb.AppendLine();

                var csDllImportsSb = new StringBuilder();
                var csApiSb        = new StringBuilder();
                var allFunctions   = new List <CppFunction>();

                // filter out functions we are not going to bind:
                foreach (var function in cppClass.Functions)
                {
                    if (function.Visibility == CppVisibility.Private)
                    {
                        continue;
                    }

                    if (mapper.IsMethodMarkedAsUnsupported(function) ||
                        !mapper.IsSupported(function.ReturnType.GetDisplayName()) ||
                        !function.Parameters.All(p => mapper.IsSupported(p.Type.GetDisplayName())) ||
                        function.IsOperator() ||
                        function.IsCopyConstructor())
                    {
                        cFileSb.AppendLine($"//NOT_BOUND:".PadRight(32) + function);
                        continue;
                    }

                    allFunctions.Add(function);
                }

                var propertyGenerator = new PropertyGenerator();
                propertyGenerator.RegisterCandidates(mapper, allFunctions);

                foreach (CppFunction function in allFunctions)
                {
                    // Type_MethodName_argsMask
                    string flatFunctionName = $"{cppClass.Name}_{function.Name}_{function.ParametersMask()}";

                    var cfunctionWriter   = new FunctionWriter();
                    var dllImportWriter   = new FunctionWriter();
                    var apiFunctionWriter = new FunctionWriter();

                    dllImportWriter
                    .Attribute($"[DllImport({dllImportPath}, CallingConvention = CallingConvention.Cdecl)]")
                    .Private()
                    .Static()
                    .Extern();

                    apiFunctionWriter
                    .SummaryComment(function.Comment?.ChildrenToString());

                    var propertyInfo = propertyGenerator.AsPropertyCandidate(function);
                    if (propertyInfo == null)
                    {
                        apiFunctionWriter.Public();
                    }
                    else
                    {
                        // TODO: convert to properties
                        //apiFunctionWriter.Private();
                        //csApiSb.AppendLine(propertyInfo.GenerateProperty().Tabify(2));
                        apiFunctionWriter.Public();
                    }

                    if (function.IsStatic() || cppClass.IsGlobal)
                    {
                        apiFunctionWriter.Static();
                    }

                    if (function.IsConstructor)
                    {
                        cfunctionWriter.ReturnType(cppClass.Class.GetFullTypeName() + "*", "EXPORTS", 32);
                        dllImportWriter.ReturnType(nameof(IntPtr));
                    }
                    else
                    {
                        cfunctionWriter.ReturnType(function.ReturnType.GetFullTypeName(), "EXPORTS", 32);
                        dllImportWriter.ReturnType(mapper.NativeToPinvokeType(function.ReturnType));
                        apiFunctionWriter.ReturnType(mapper.MapToManagedApiType(function.ReturnType));
                    }

                    cfunctionWriter.MethodName(flatFunctionName);
                    dllImportWriter.MethodName(flatFunctionName);
                    apiFunctionWriter.MethodName(mapper.RenameForApi(function.Name, isMethod: !function.IsConstructor));

                    if (!function.IsConstructor &&
                        !function.IsStatic() &&
                        !cppClass.IsGlobal)
                    {
                        // all instance methods will have "this" as the first argument
                        cfunctionWriter.Parameter(cppClass.Class.GetFullTypeName() + "*", "target");
                        dllImportWriter.Parameter(nameof(IntPtr), "target");
                    }

                    foreach (var parameter in function.Parameters)
                    {
                        cfunctionWriter.Parameter(
                            parameter.Type.GetDisplayName(),
                            parameter.Name);

                        dllImportWriter.Parameter(
                            mapper.NativeToPinvokeType(parameter.Type),
                            mapper.EscapeVariableName(parameter.Name));

                        apiFunctionWriter.Parameter(
                            mapper.MapToManagedApiType(parameter.Type),
                            mapper.EscapeVariableName(parameter.Name));
                    }

                    // append "return" if needed
                    cfunctionWriter.BodyStart();

                    if (cppClass.IsGlobal)
                    {
                        // GlobalMethod
                        cfunctionWriter.BodyCallMethod(function.Name);
                    }
                    else if (!function.IsConstructor && !function.IsStatic())
                    {
                        // target->InstanceMethod
                        cfunctionWriter.BodyCallMethod($"target->{function.Name}");
                    }
                    else if (function.IsStatic())
                    {
                        // Class1::StaticMethod
                        cfunctionWriter.BodyCallMethod(cppClass.Class.GetFullTypeName() + "::" + function.Name);
                    }
                    else
                    {
                        // new Class1
                        cfunctionWriter.BodyCallMethod($"new {cppClass.Class.GetFullTypeName()}");
                    }

                    if (function.IsConstructor)
                    {
                        apiFunctionWriter
                        .BaseCtor("ownsHandle: true")
                        .StartExpressionBody()
                        .BodyCallMethod("SetHandle");
                    }
                    else
                    {
                        apiFunctionWriter.StartExpressionBody();
                    }


                    if (mapper.IsKnownNativeType(function.ReturnType))
                    {
                        // call "ctor(IntPtr, bool)"
                        apiFunctionWriter.BodyCallMethod("new " + mapper.MapToManagedApiType(function.ReturnType));
                    }

                    // some API functions need special casts, e.g. IntPtr/*size_t*/ (nint) to long
                    if (mapper.NeedsCastForApi(function.ReturnType, out string returnTypeApiCast))
                    {
                        apiFunctionWriter.BodyCallMethod(returnTypeApiCast);
                    }

                    apiFunctionWriter.BodyCallMethod(flatFunctionName);

                    // all API functions pass "Handle" property as the first argument to DllImport methods
                    if (!function.IsConstructor && !cppClass.IsGlobal && !function.IsStatic())
                    {
                        apiFunctionWriter.PassParameter("Handle");
                    }

                    foreach (var parameter in function.Parameters)
                    {
                        cfunctionWriter.PassParameter(parameter.Name);

                        string dllImportType = mapper.NativeToPinvokeType(parameter.Type);
                        string escapedName   = mapper.EscapeVariableName(parameter.Name);

                        if (parameter.Type.IsBool()) // bool to byte
                        {
                            escapedName = $"(Byte)({escapedName} ? 1 : 0)";
                        }

                        // cast to DllImport's type if needed (TODO: wrap with checked {})
                        else if (dllImportType.Contains("/*"))
                        {
                            escapedName = $"({dllImportType.DropComments()}){escapedName}";
                        }

                        // if the parameter is a C# class-wrapper - pass its Handle
                        else if (mapper.IsKnownNativeType(parameter.Type))
                        {
                            escapedName = $"({escapedName} == null ? IntPtr.Zero : {escapedName}.Handle)";
                        }

                        apiFunctionWriter.PassParameter(escapedName);
                    }

                    if (function.ReturnType.IsBool())
                    {
                        csApiSb.AppendLine(apiFunctionWriter.Build(" > 0").Tabify(2)); // byte to bool
                    }
                    else if (mapper.IsKnownNativeType(function.ReturnType))
                    {
                        // pass "false" to "ownsHandle" argument
                        apiFunctionWriter.EndLastCall(true).PassParameter("false");
                        csApiSb.AppendLine(apiFunctionWriter.Build().Tabify(2));
                    }
                    else
                    {
                        csApiSb.AppendLine(apiFunctionWriter.Build().Tabify(2));
                    }

                    csApiSb.AppendLine();

                    csDllImportsSb.AppendLine(dllImportWriter.BuildWithoutBody().Tabify(2));
                    csDllImportsSb.AppendLine();

                    cFileSb.AppendLine(cfunctionWriter.Build());
                }

                if (cppClass.IsGlobal)
                {
                    csFileSb.Append(templateManager.CSharpGlobalClass(csDllImportsSb.ToString(), csApiSb.ToString(), dllImportPath));
                }
                else if (!cppClass.IsGlobal)
                {
                    csFileSb.Append(templateManager.CSharpClass(mapper.RenameForApi(cppClass.Name, false), cppClass.Name, csDllImportsSb.ToString(), csApiSb.ToString(), dllImportPath));

                    // Append "delete" method:
                    // EXPORTS(void) %class%_delete(%class%* target) { if (target) delete target; }
                    var cfunctionWriter = new FunctionWriter();
                    cfunctionWriter.ReturnType("void", "EXPORTS", 32)
                    .MethodName(cppClass.Name + "__delete")
                    .Parameter(cppClass.Class.GetFullTypeName() + "*", "target")
                    .BodyStart()
                    .Body("delete target");
                    cFileSb
                    .AppendLine(cfunctionWriter.Build());
                }
            }

            File.WriteAllText(outCFile, templateManager.CHeader() + cFileSb);
            File.WriteAllText(outCsFile, templateManager.CSharpHeader(@namespace, csFileSb.ToString()));
        }
Example #2
0
        /// <param name="dllImportPath">will be used as the first argument in [DllImport]. Can be a path to some constant</param>
        /// <param name="generateCForGlobalFunctions">sometimes global functions are ready to be pinvoked as is</param>
        public static void Generate(TypeMapper mapper, TemplateManager templateManager, string @namespace, string dllImportPath, string outCFile, string outCsFile, bool generateCForGlobalFunctions = true)
        {
            var csFileSb = new StringBuilder();
            var cFileSb  = new StringBuilder();

            foreach (CppClassContainer cppClass in mapper.GetAllClasses())
            {
                var csClassSb = new StringBuilder();
                List <CppFunction> allFunctions = cppClass.Functions;
                for (int i = 0; i < allFunctions.Count; i++)
                {
                    CppFunction function = allFunctions[i];

                    if (!mapper.IsSupported(function.ReturnType.GetDisplayName()) ||
                        !function.Parameters.All(p => mapper.IsSupported(p.Type.GetDisplayName())) ||
                        function.IsOperator())
                    {
                        Logger.LogWarning($"Ignoring {function.Name}");
                        // we can't bind this method (yet)
                        continue;
                    }

                    // Type_MethodName1
                    string flatFunctionName = $"{cppClass.Name}_{function.Name}{i}";
                    if (!generateCForGlobalFunctions)
                    {
                        flatFunctionName = function.Name; // we are going to pinvoke it directly
                    }
                    var cfunctionWriter = new FunctionWriter();
                    var dllImportWriter = new FunctionWriter();
                    // TODO: apiFunctionWriter = new FunctionWriter();

                    dllImportWriter.Attribute($"[DllImport({dllImportPath}, CallingConvention = CallingConvention.Cdecl)]")
                    .Private().Static().Extern();

                    if (function.IsConstructor)
                    {
                        cfunctionWriter.ReturnType(cppClass.Class.GetFullTypeName() + "*", "EXPORTS");
                        dllImportWriter.ReturnType(nameof(IntPtr));
                    }
                    else
                    {
                        var funcReturnType = function.ReturnType.GetFullTypeName();
                        cfunctionWriter.ReturnType(funcReturnType, "EXPORTS");
                        dllImportWriter.ReturnType(mapper.MapToManagedType(funcReturnType));
                    }

                    // PS: should we generate C for global functions (we currently do)? probably it should be optional

                    cfunctionWriter.MethodName(flatFunctionName);
                    dllImportWriter.MethodName(flatFunctionName);

                    if (!function.IsConstructor &&
                        !function.IsStatic() &&
                        !cppClass.IsGlobal)
                    {
                        // all instance methods will have "this" as the first argument
                        cfunctionWriter.Parameter(cppClass.Class.GetFullTypeName() + "*", "target");
                        dllImportWriter.Parameter(nameof(IntPtr), "target");
                    }

                    foreach (var parameter in function.Parameters)
                    {
                        cfunctionWriter.Parameter(
                            parameter.Type.GetDisplayName(),
                            parameter.Name);

                        dllImportWriter.Parameter(
                            mapper.MapToManagedType(parameter.Type.GetDisplayName()),
                            mapper.EscapeVariableName(parameter.Name));
                    }

                    // append "return" if needed
                    cfunctionWriter.BodyStart();

                    if (cppClass.IsGlobal)
                    {
                        // GlobalMethod
                        cfunctionWriter.BodyCallMethod(function.Name);
                    }
                    else if (!function.IsConstructor && !function.IsStatic())
                    {
                        // target->InstanceMethod
                        cfunctionWriter.BodyCallMethod($"target->{function.Name}");
                    }
                    else if (function.IsStatic())
                    {
                        // Class1::StaticMethod
                        cfunctionWriter.BodyCallMethod(cppClass.Class.GetFullTypeName() + "::" + function.Name);
                    }
                    else
                    {
                        // new Class1
                        cfunctionWriter.BodyCallMethod($"new {cppClass.Class.GetFullTypeName()}");
                    }


                    foreach (var parameter in function.Parameters)
                    {
                        cfunctionWriter.PassParameter(parameter.Name);
                    }

                    csClassSb.AppendLine(dllImportWriter.BuildWithoutBody().Tabify(2));
                    csClassSb.AppendLine();

                    cFileSb.AppendLine(cfunctionWriter.Build());
                    cFileSb.AppendLine();
                }

                if (cppClass.IsGlobal && generateCForGlobalFunctions)
                {
                    csFileSb.Append(templateManager.CSharpGlobalClass(csClassSb.ToString(), dllImportPath));
                }
                else if (!cppClass.IsGlobal)
                {
                    csFileSb.Append(templateManager.CSharpClass(cppClass.Name, cppClass.Name, csClassSb.ToString(), dllImportPath));
                }
            }

            File.WriteAllText(outCFile, templateManager.CHeader() + cFileSb);
            File.WriteAllText(outCsFile, templateManager.CSharpHeader(@namespace, csFileSb.ToString()));
        }