public MethodDefinition(string name, ClassDefinition parent, int numArgs)
        {
            Name = name;
            Parent = parent;
            Parameters = new ParameterDefinition[numArgs];

            if (parent != null)
            {
                parent.Methods.Add(this);
            }
        }
 public MethodDefinition Copy(ClassDefinition parent = null)
 {
     var m = new MethodDefinition(Name, parent ?? Parent, Parameters.Length)
     {
         Access = Access,
         Field = Field,
         IsAbstract = IsAbstract,
         IsConstructor = IsConstructor,
         IsExcluded = IsExcluded,
         IsStatic = IsStatic,
         OutValueParameter = OutValueParameter?.Copy(),
         ReturnType = ReturnType.Copy()
     };
     for (int i = 0; i < Parameters.Length; i++)
     {
         m.Parameters[i] = Parameters[i].Copy();
     }
     return m;
 }
        bool ClassNeedsExtensions(ClassDefinition c)
        {
            foreach (var prop in c.Properties)
            {
                if (_extensionClassesInternal.ContainsKey(prop.Type.ManagedName))
                {
                    return true;
                }
            }

            foreach (var method in c.Methods)
            {
                if (MethodNeedsExtensions(method))
                {
                    return true;
                }
            }
            return false;
        }
        private static bool RequiresMathNamespace(ClassDefinition @class)
        {
            if (@class.NestedClasses.Any(RequiresMathNamespace)) return true;
            if (@class.IsExcluded) return false;

            foreach (var method in @class.Methods.Where(m => !m.IsExcluded))
            {
                if (@class.HidePublicConstructors && method.IsConstructor) continue;

                if (DefaultParser.TypeRequiresMarshal(method.ReturnType)) return true;

                if (method.Parameters.Any(param => DefaultParser.TypeRequiresMarshal(param.Type)))
                {
                    return true;
                }
            }

            return false;
        }
 public EnumDefinition(string name, HeaderDefinition header = null, ClassDefinition parent = null)
     : base(name, header, parent)
 {
     EnumConstants = new List<string>();
     EnumConstantValues = new List<string>();
 }
 bool IsExcludedClass(ClassDefinition c)
 {
     return c.IsTypedef || c.IsPureEnum || c.IsExcluded;
 }
Beispiel #7
0
        private void CopyTemplateMethods(ClassDefinition thisClass, ClassTemplateDefinition template,
                                         Dictionary <string, string> templateParams = null)
        {
            if (templateParams == null)
            {
                templateParams = new Dictionary <string, string>();
                var templateBase = template.BaseClass as ClassTemplateDefinition;

                for (int i = 0; i < template.TemplateParameters.Count; i++)
                {
                    string param      = templateBase.TemplateParameters[i];
                    string paramValue = template.TemplateParameters[i];
                    templateParams[param] = paramValue;
                }

                CopyTemplateMethods(thisClass, templateBase, templateParams);
                return;
            }

            thisClass.BaseClass = template.BaseClass;

            var scriptedNameMapping = Project.ClassNameMapping as ScriptedMapping;

            if (scriptedNameMapping != null)
            {
                scriptedNameMapping.Globals.Header = thisClass.Header;
            }
            // TODO:
            //template.ManagedName = Project.ClassNameMapping.Map(template.Name);

            foreach (var templateClass in template.NestedClasses)
            {
                var classSpec = new ClassDefinition(templateClass.Name, thisClass.Header, thisClass);
                Project.ClassDefinitions.Add(classSpec.FullyQualifiedName, classSpec);

                foreach (var templateMethod in templateClass.Methods)
                {
                    // Replace template parameters with concrete types
                    var methodSpec = templateMethod.Copy(classSpec);
                    if (methodSpec.ReturnType.HasTemplateTypeParameter)
                    {
                        methodSpec.ReturnType = methodSpec.ReturnType.CopyTemplated(templateParams);
                    }

                    foreach (var param in methodSpec.Parameters
                             .Where(p => p.Type.HasTemplateTypeParameter))
                    {
                        param.Type = param.Type.CopyTemplated(templateParams);
                    }
                }
            }

            foreach (var templateMethod in template.Methods.Where(m => !m.IsConstructor))
            {
                // Replace template parameters with concrete types
                var methodSpec = templateMethod.Copy(thisClass);
                if (methodSpec.ReturnType.HasTemplateTypeParameter)
                {
                    methodSpec.ReturnType = methodSpec.ReturnType.CopyTemplated(templateParams);
                }

                foreach (var param in methodSpec.Parameters
                         .Where(p => p.Type.HasTemplateTypeParameter))
                {
                    param.Type = param.Type.CopyTemplated(templateParams);
                }
            }
        }
        private static bool RequiresConversionHeader(ClassDefinition @class)
        {
            if (@class.NestedClasses.Any(RequiresConversionHeader)) return true;

            foreach (var method in @class.Methods.Where(m => !m.IsExcluded))
            {
                if (DefaultParser.TypeRequiresMarshal(method.ReturnType)) return true;

                if (method.Parameters.Any(p => DefaultParser.TypeRequiresMarshal(p.Type))) return true;
            }

            return false;
        }
        void OutputClass(ClassDefinition @class, int level)
        {
            EnsureHeaderWhiteSpace();
            EnsureSourceWhiteSpace();

            // Write access modifier
            WriteTabs(level);
            if (level == 1)
            {
                HeaderWrite("public ");
            }

            // Write class definition
            HeaderWrite(string.Format("ref class {0}", @class.ManagedName));
            if (@class.IsAbstract)
            {
                HeaderWrite(" abstract");
            }
            else if (@class.IsStaticClass)
            {
                HeaderWrite(" sealed");
            }
            if (@class.BaseClass != null)
            {
                HeaderWriteLine(string.Format(" : {0}", @class.BaseClass.ManagedName));
            }
            else if (@class.IsTrackingDisposable)
            {
                HeaderWriteLine(" : ITrackingDisposable");
            }
            else
            {
                // In C++/CLI, IDisposable is inherited automatically if the destructor and finalizer are defined
                //HeaderWrite(" : IDisposable");
                HeaderWriteLine();
            }

            WriteTabs(level);
            HeaderWriteLine("{");
            //hasHeaderWhiteSpace = true;

            // Default access for ref class
            var currentAccess = RefAccessSpecifier.Private;

            // Write child classes
            if ([email protected](cl => IsExcludedClass(cl)))
            {
                OutputClasses(@class.Classes, ref currentAccess, level);
                currentAccess = RefAccessSpecifier.Public;
                SourceWriteLine();
            }

            // Add a private constructor for classes without instances
            if (@class.IsStaticClass)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine(string.Format("{0}() {{}}", @class.ManagedName));
                hasHeaderWhiteSpace = false;
            }

            // Downcast native pointer if any methods in a derived class use it
            if (@class.BaseClass != null && @class.Methods.Any(m => !m.IsConstructor && !m.IsStatic))
            {
                EnsureSourceWhiteSpace();
                SourceWriteLine(string.Format("#define Native static_cast<{0}*>(_native)", @class.FullyQualifiedName));
                hasSourceWhiteSpace = false;
            }

            // Write the native pointer to the base class
            if (@class.BaseClass == null && [email protected])
            {
                if (@class.Classes.Any(c => !IsExcludedClass(c)))
                {
                    HeaderWriteLine();
                }
                if (@class.IsTrackingDisposable)
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                    WriteTabs(level + 1);
                    HeaderWriteLine("virtual event EventHandler^ OnDisposing;");
                    WriteTabs(level + 1);
                    HeaderWriteLine("virtual event EventHandler^ OnDisposed;");
                    hasHeaderWhiteSpace = false;
                }
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal);

                WriteTabs(level + 1);
                HeaderWrite(@class.FullyQualifiedName);
                HeaderWriteLine("* _native;");
                hasHeaderWhiteSpace = false;
            }

            EnsureHeaderWhiteSpace();
            EnsureSourceWhiteSpace();

            // Private fields
            // _isDisposed flag
            if (@class.IsTrackingDisposable)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine("bool _isDisposed;");
                hasHeaderWhiteSpace = false;
            }
            // _preventDelete flag
            if (@class.HasPreventDelete)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine("bool _preventDelete;");
                hasHeaderWhiteSpace = false;
            }

            // Write cached property fields
            foreach (var cachedProperty in @class.CachedProperties.OrderBy(p => p.Key))
            {
                EnsureAccess(level, ref currentAccess, cachedProperty.Value.Access);
                WriteTabs(level + 1);
                string name = cachedProperty.Key;
                name = char.ToLower(name[0]) + name.Substring(1);
                HeaderWriteLine(string.Format("{0} _{1};",
                                              BulletParser.GetTypeRefName(cachedProperty.Value.Property.Type), name));
                hasHeaderWhiteSpace = false;
            }

            // Write constructors and destructors if not static
            if ([email protected])
            {
                // Write unmanaged constructor
                // TODO: Write constructor from unmanaged pointer only if the class is ever instantiated in this way.
                if ([email protected])
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal);

                    WriteTabs(level + 1);
                    SourceWrite(string.Format("{0}::", @class.FullNameManaged));
                    Write(string.Format("{0}({1}* native)", @class.ManagedName, @class.FullyQualifiedName));
                    HeaderWriteLine(';');
                    SourceWriteLine();
                    if (@class.BaseClass != null)
                    {
                        WriteTabs(1, true);
                        SourceWriteLine(string.Format(": {0}(native)", @class.BaseClass.ManagedName));
                    }
                    SourceWriteLine('{');
                    if (@class.BaseClass == null)
                    {
                        WriteTabs(1, true);
                        SourceWriteLine("_native = native;");
                    }
                    SourceWriteLine('}');
                    hasHeaderWhiteSpace = false;
                    hasSourceWhiteSpace = false;
                }

                // Write destructor & finalizer
                if (@class.BaseClass == null)
                {
                    // ECMA-372 19.13.2: "The access-specifier of a finalizer in a ref class is ignored."
                    WriteTabs(level + 1);
                    HeaderWriteLine(string.Format("!{0}();", @class.ManagedName));
                    // ECMA-372 19.13.1: "The access-specifier of a destructor in a ref class is ignored."
                    WriteTabs(level + 1);
                    HeaderWriteLine(string.Format("~{0}();", @class.ManagedName));
                    hasHeaderWhiteSpace = false;

                    EnsureSourceWhiteSpace();
                    SourceWriteLine(string.Format("{0}::~{1}()", @class.FullNameManaged, @class.ManagedName));
                    SourceWriteLine('{');
                    SourceWriteLine(string.Format("\tthis->!{0}();", @class.ManagedName));
                    SourceWriteLine('}');
                    SourceWriteLine();

                    SourceWriteLine(string.Format("{0}::!{1}()", @class.FullNameManaged, @class.ManagedName));
                    SourceWriteLine('{');
                    if (@class.IsTrackingDisposable)
                    {
                        SourceWriteLine("\tif (this->IsDisposed)");
                        SourceWriteLine("\t\treturn;");
                        SourceWriteLine();
                        SourceWriteLine("\tOnDisposing(this, nullptr);");
                        SourceWriteLine();
                    }
                    if (@class.HasPreventDelete)
                    {
                        SourceWriteLine("\tif (!_preventDelete)");
                        SourceWriteLine("\t{");
                        SourceWriteLine("\t\tdelete _native;");
                        SourceWriteLine("\t}");
                    }
                    else
                    {
                        SourceWriteLine("\tdelete _native;");
                    }
                    if (@class.IsTrackingDisposable)
                    {
                        SourceWriteLine("\t_isDisposed = true;");
                        SourceWriteLine();
                        SourceWriteLine("\tOnDisposed(this, nullptr);");
                    }
                    else
                    {
                        SourceWriteLine("\t_native = NULL;");
                    }
                    SourceWriteLine('}');
                    hasSourceWhiteSpace = false;
                }

                // Write public constructors
                if ([email protected] && [email protected])
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);

                    var constructors = @class.Methods.Where(m => m.IsConstructor);
                    if (constructors.Any())
                    {
                        foreach (var constructor in constructors)
                        {
                            OutputMethod(constructor, level);
                        }
                    }
                    else
                    {
                        // Default constructor
                        MethodDefinition constructor = new MethodDefinition(@class.Name, @class, 0);
                        constructor.IsConstructor = true;
                        OutputMethod(constructor, level);
                    }
                }
            }

            // Write non-constructor methods
            var methods = @class.Methods.Where(m => !m.IsConstructor);

            if (methods.Any())
            {
                EnsureHeaderWhiteSpace();

                foreach (var method in methods)
                {
                    if (method.Property != null)
                    {
                        continue;
                    }

                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                    OutputMethod(method, level);
                }
            }

            // Write properties (includes unmanaged fields and getters/setters)
            foreach (PropertyDefinition prop in @class.Properties)
            {
                string typeConditional = GetTypeConditional(prop.Type, @class.Header);
                if (typeConditional != null)
                {
                    WriteLine(string.Format("#ifndef {0}", typeConditional));
                    hasSourceWhiteSpace = true;
                }
                else
                {
                    EnsureHeaderWhiteSpace();
                }

                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);

                WriteTabs(level + 1);
                HeaderWriteLine(string.Format("property {0} {1}",
                                              BulletParser.GetTypeRefName(prop.Type), prop.Name));
                WriteTabs(level + 1);
                HeaderWriteLine("{");

                // Getter/Setter
                OutputMethod(prop.Getter, level + 1);
                if (prop.Setter != null)
                {
                    OutputMethod(prop.Setter, level + 1);
                }

                WriteTabs(level + 1);
                HeaderWriteLine("}");

                if (typeConditional != null)
                {
                    WriteLine("#endif");
                    hasSourceWhiteSpace = false;
                }

                hasHeaderWhiteSpace = false;
            }

            WriteTabs(level);
            HeaderWriteLine("};");
            hasHeaderWhiteSpace = false;
        }
        private static bool RequiresConversionHeader(ClassDefinition cl)
        {
            if (cl.Classes.Any(RequiresConversionHeader))
            {
                return true;
            }

            foreach (var method in cl.Methods)
            {
                if (BulletParser.TypeRequiresMarshal(method.ReturnType))
                {
                    return true;
                }

                foreach (var param in method.Parameters)
                {
                    if (BulletParser.TypeRequiresMarshal(param.Type))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
Beispiel #11
0
        void WriteClass(ClassDefinition @class, int level)
        {
            if (_wrapperHeaderGuards.ContainsKey(@class.Name))
            {
                WriteWrapperClassConstructor(@class);
            }

            // Write child classes
            foreach (var c in @class.NestedClasses
                     .Where(c => !IsExcludedClass(c))
                     .OrderBy(c => c.Name))
            {
                WriteClass(c, level + 1);
            }

            // Group methods into constructors, destructors and methods
            var methodGroups = @class.Methods.GroupBy(m =>
            {
                if (m.IsExcluded)
                {
                    return((CursorKind)0);
                }
                if (m.IsConstructor)
                {
                    return(CursorKind.Constructor);
                }
                if (m.IsDestructor)
                {
                    return(CursorKind.Destructor);
                }
                return(CursorKind.CxxMethod);
            }).ToDictionary(g => g.Key);

            // Constructors
            if ([email protected] && [email protected] && [email protected])
            {
                IGrouping <CursorKind, MethodDefinition> constructors;
                if (methodGroups.TryGetValue(CursorKind.Constructor, out constructors))
                {
                    int overloadIndex = 0;
                    foreach (var constructor in methodGroups[CursorKind.Constructor])
                    {
                        WriteMethod(constructor, level, ref overloadIndex);
                    }
                }
            }

            // Methods
            IGrouping <CursorKind, MethodDefinition> methods;

            if (methodGroups.TryGetValue(CursorKind.CxxMethod, out methods))
            {
                foreach (var groupByName in methods.GroupBy(m => m.Name))
                {
                    int overloadIndex = 0;
                    foreach (var method in groupByName)
                    {
                        WriteMethod(method, level, ref overloadIndex);
                    }
                }
            }

            // Destructors
            IGrouping <CursorKind, MethodDefinition> destructors;

            if (methodGroups.TryGetValue(CursorKind.Destructor, out destructors))
            {
                int overloadIndex = 0;
                foreach (var method in destructors)
                {
                    WriteMethod(method, level, ref overloadIndex);
                }
            }

            _hasCppClassSeparatingWhitespace = false;
        }
Beispiel #12
0
 public ClassTemplateDefinition(string name, HeaderDefinition header = null, ClassDefinition parent = null)
     : base(name, header, parent)
 {
 }
Beispiel #13
0
 public ManagedClass(ClassDefinition nativeClass, string managedName, ManagedClass parent)
 {
     Native = nativeClass;
     Name   = managedName;
     Parent = parent;
 }
 public EnumDefinition(string name, HeaderDefinition header = null, ClassDefinition parent = null)
     : base(name, header, parent)
 {
     EnumConstants      = new List <string>();
     EnumConstantValues = new List <string>();
 }
 public ManagedClass GetManaged(ClassDefinition @class)
 {
     return(Classes[@class.FullyQualifiedName]);
 }
Beispiel #16
0
 protected bool IsExcludedClass(ClassDefinition @class)
 {
     return(@class is ClassTemplateDefinition || @class is EnumDefinition ||
            @class.IsPureEnum || @class.IsExcluded || @class.IsFunctionProto);
 }
Beispiel #17
0
 public ClassDefinition(string name, ClassDefinition parent)
     : this(name, parent.Header)
 {
     Parent = parent;
 }
        public void WriteClassWrapper(ClassDefinition cl)
        {
            List<MethodDefinition> baseAbstractMethods;
            List<MethodDefinition> thisAbstractMethods = cl.Methods.Where(x => x.IsVirtual).ToList();
            List<MethodDefinition> abstractMethods = thisAbstractMethods.ToList();
            if (cl.BaseClass != null)
            {
                baseAbstractMethods = cl.BaseClass.Target.Methods.Where(x => x.IsVirtual).ToList();
                abstractMethods.AddRange(baseAbstractMethods);
            }
            else
            {
                baseAbstractMethods = new List<MethodDefinition>();
            }

            if (!hasClassSeparatingWhitespace)
            {
                WriteLine(WriteTo.Header | WriteTo.Source);
                hasClassSeparatingWhitespace = true;
            }
            EnsureWhiteSpace(WriteTo.Source);

            // Wrapper C Constructor
            Write("\tEXPORT ", WriteTo.Header);
            Write(string.Format("{0}Wrapper* {0}Wrapper_new(", cl.Name), WriteTo.Header | WriteTo.Source);
            int numMethods = abstractMethods.Count;
            for (int i = 0; i < numMethods; i++)
            {
                var method = abstractMethods[i];
                string className = baseAbstractMethods.Contains(method) ? cl.BaseClass.ManagedName : cl.ManagedName;
                Write(string.Format("p{0}_{1} {2}Callback", className, method.ManagedName, method.Name), WriteTo.Header | WriteTo.Source);
                if (i != numMethods - 1)
                {
                    Write(", ", WriteTo.Header | WriteTo.Source);
                }
            }
            WriteLine(");", WriteTo.Header);
            WriteLine(')', WriteTo.Source);
            WriteLine('{', WriteTo.Source);
            Write(string.Format("\treturn new {0}Wrapper(", cl.Name), WriteTo.Source);
            for (int i = 0; i < numMethods; i++)
            {
                var method = abstractMethods[i];
                Write(string.Format("{0}Callback", method.Name), WriteTo.Source);
                if (i != numMethods - 1)
                {
                    Write(", ", WriteTo.Source);
                }
            }
            WriteLine(");", WriteTo.Source);
            WriteLine('}', WriteTo.Source);
            hasClassSeparatingWhitespace = false;
            hasSourceWhiteSpace = false;
        }
        public void WriteClassWrapperMethodDeclarations(ClassDefinition cl)
        {
            List<MethodDefinition> baseAbstractMethods;
            List<MethodDefinition> thisAbstractMethods = cl.Methods.Where(x => x.IsVirtual).ToList();
            List<MethodDefinition> abstractMethods = thisAbstractMethods.ToList();
            if (cl.BaseClass != null)
            {
                baseAbstractMethods = cl.BaseClass.Target.Methods.Where(x => x.IsVirtual).ToList();
                abstractMethods.AddRange(baseAbstractMethods);
            }
            else
            {
                baseAbstractMethods = new List<MethodDefinition>();
            }

            if (!hasClassSeparatingWhitespace)
            {
                WriteLine(WriteTo.Header);
                hasClassSeparatingWhitespace = true;
            }

            string headerGuard = wrapperHeaderGuards[cl.Name];
            foreach (MethodDefinition method in thisAbstractMethods)
            {
                Write(string.Format("typedef {0} (*p{1}_{2})(", method.ReturnType.Name, cl.ManagedName, method.ManagedName), WriteTo.Header);
                int numParameters = method.Parameters.Length;
                for (int i = 0; i < numParameters; i++)
                {
                    var param = method.Parameters[i];
                    WriteType(param.Type, WriteTo.Header);
                    Write(" ", WriteTo.Header);
                    Write(param.Name, WriteTo.Header);

                    if (i != numParameters - 1)
                    {
                        Write(", ", WriteTo.Header);
                    }
                }
                WriteLine(");", WriteTo.Header);
            }
            if (thisAbstractMethods.Count != 0)
            {
                WriteLine(WriteTo.Header);
            }
            WriteLine(string.Format("class {0}Wrapper : public {0}", cl.FullNameCS), WriteTo.Header);
            WriteLine("{", WriteTo.Header);
            WriteLine("private:", WriteTo.Header);
            foreach (MethodDefinition method in abstractMethods)
            {
                string className = baseAbstractMethods.Contains(method) ? cl.BaseClass.ManagedName : cl.ManagedName;
                WriteLine(string.Format("\tp{0}_{1} _{2}Callback;", className, method.ManagedName, method.Name), WriteTo.Header);
            }
            WriteLine(WriteTo.Header);
            WriteLine("public:", WriteTo.Header);

            // Wrapper constructor
            Write(string.Format("\t{0}Wrapper(", cl.FullNameCS), WriteTo.Header);
            int numMethods = abstractMethods.Count;
            for (int i = 0; i < numMethods; i++)
            {
                var method = abstractMethods[i];
                string className = baseAbstractMethods.Contains(method) ? cl.BaseClass.ManagedName : cl.ManagedName;
                Write(string.Format("p{0}_{1} {2}Callback", className, method.ManagedName, method.Name), WriteTo.Header);
                if (i != numMethods - 1)
                {
                    Write(", ", WriteTo.Header);
                }
            }
            WriteLine(");", WriteTo.Header);
            WriteLine(WriteTo.Header);

            foreach (MethodDefinition method in abstractMethods)
            {
                Write(string.Format("\tvirtual {0} {1}(", method.ReturnType.Name, method.Name), WriteTo.Header);
                int numParameters = method.Parameters.Length;
                for (int i = 0; i < numParameters; i++)
                {
                    var param = method.Parameters[i];
                    WriteType(param.Type, WriteTo.Header);
                    Write(" ", WriteTo.Header);
                    Write(param.Name, WriteTo.Header);

                    if (i != numParameters - 1)
                    {
                        Write(", ", WriteTo.Header);
                    }
                }
                WriteLine(");", WriteTo.Header);
            }

            WriteLine("};", WriteTo.Header);
            hasClassSeparatingWhitespace = false;
        }
Beispiel #20
0
        public void WriteWrapperClass(ClassDefinition @class)
        {
            List <MethodDefinition> baseVirtualMethods;
            var thisVirtualMethods = @class.Methods.Where(x => x.IsVirtual).ToList();
            var virtualMethods     = thisVirtualMethods.ToList();

            if (@class.BaseClass != null)
            {
                baseVirtualMethods = @class.BaseClass.Methods.Where(x => x.IsVirtual).ToList();
                virtualMethods.AddRange(baseVirtualMethods);
            }
            else
            {
                baseVirtualMethods = new List <MethodDefinition>();
            }
            var methodCallbacks = virtualMethods.Select(m =>
            {
                string className = baseVirtualMethods.Contains(m) ?
                                   GetFullNameC(@class.BaseClass) : GetFullNameC(@class);
                return($"p_{className}_{m.Name} {m.Name}Callback");
            }).ToList();

            if (!_hasCppClassSeparatingWhitespace)
            {
                WriteLine();
                _hasCppClassSeparatingWhitespace = true;
            }

            // TODO: string headerGuard = wrapperHeaderGuards[@class.Name];
            string parameters;

            if (thisVirtualMethods.Count != 0)
            {
                foreach (var method in thisVirtualMethods)
                {
                    string methodPtr = $"p_{GetFullNameC(@class)}_{method.Name}";
                    Write($"typedef {method.ReturnType.Name} (*{methodPtr})(", WriteTo.Header);
                    parameters = ListToLines(method.Parameters
                                             .Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"), WriteTo.Header);
                    WriteLine($"{parameters});", WriteTo.Header);
                }
                WriteLine();
            }

            // Wrapper class
            string wrapperClassName = $"{GetFullNameC(@class)}Wrapper";

            WriteLine($"class {wrapperClassName} : public {GetFullNameC(@class)}");
            WriteLine("{");
            WriteLine("private:");
            foreach (var m in virtualMethods)
            {
                string className = baseVirtualMethods.Contains(m) ?
                                   GetFullNameC(@class.BaseClass) : GetFullNameC(@class);
                WriteLine(1, $"p_{className}_{m.Name} _{m.Name}Callback;");
            }
            WriteLine();
            WriteLine("public:");

            // Wrapper constructor
            Write(1, $"{wrapperClassName}(");
            string constructorParams = ListToLines(methodCallbacks, WriteTo.Header, 1);

            WriteLine($"{constructorParams});");
            WriteLine();

            // Wrapper methods
            foreach (var m in virtualMethods)
            {
                Write(1, $"virtual {m.ReturnType.Name} {m.Name}(");
                string methodParams = ListToLines(
                    m.Parameters.Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"),
                    WriteTo.Header, 1);
                WriteLine(methodParams + ");");
            }

            WriteLine("};");
            _hasCppClassSeparatingWhitespace = false;


            var prevTo = To;

            To = WriteTo.Source;
            EnsureWhiteSpace();

            // Wrapper C++ Constructor
            Write($"{wrapperClassName}::{wrapperClassName}(");
            parameters = ListToLines(methodCallbacks, WriteTo.Source);
            WriteLine($"{parameters})");
            WriteLine('{');
            foreach (var method in virtualMethods)
            {
                WriteLine(1, string.Format("_{0}Callback = {0}Callback;", method.Name));
            }
            WriteLine('}');
            WriteLine();

            // Wrapper C++ methods
            foreach (var method in virtualMethods)
            {
                Write($"{method.ReturnType.Name} {wrapperClassName}::{method.Name}(");
                parameters = ListToLines(method.Parameters
                                         .Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"), WriteTo.Source);
                WriteLine($"{parameters})");

                WriteLine('{');
                WriteTabs(1);
                if (!method.IsVoid)
                {
                    Write("return ");
                }
                Write($"_{method.Name}Callback(");
                parameters = ListToLines(method.Parameters
                                         .Select(p => p.Name), WriteTo.Source, 1);
                WriteLine($"{parameters});");
                WriteLine('}');
                WriteLine();
            }
            WriteLine();

            To = prevTo;
        }
        void WriteClass(ClassDefinition c, int level)
        {
            if (c.IsExcluded || c.IsTypedef || c.IsPureEnum)
            {
                return;
            }

            if (wrapperHeaderGuards.ContainsKey(c.Name))
            {
                WriteClassWrapper(c);
            }

            EnsureWhiteSpace(WriteTo.Source | WriteTo.CS);

            // Write class definition
            WriteTabs(level, WriteTo.CS);
            Write("public class ", WriteTo.CS);
            Write(c.ManagedName, WriteTo.CS);
            if (c.BaseClass != null)
            {
                Write(" : ", WriteTo.CS);
                WriteLine(c.BaseClass.Target.FullNameManaged.Replace("::", "."), WriteTo.CS);
                //WriteLine(c.BaseClass.ManagedNameCS, WriteTo.CS);
            }
            else
            {
                WriteLine(" : IDisposable", WriteTo.CS);
            }
            WriteTabs(level, WriteTo.CS);
            WriteLine("{", WriteTo.CS);
            hasCSWhiteSpace = true;

            // Write child classes
            foreach (ClassDefinition cl in c.Classes.OrderBy(x => x.FullNameManaged))
            {
                WriteClass(cl, level + 1);
            }

            if (!hasClassSeparatingWhitespace)
            {
                WriteLine(WriteTo.Header | WriteTo.Source);
                hasClassSeparatingWhitespace = true;
            }

            // Write native pointer
            if (c.BaseClass == null)
            {
                EnsureWhiteSpace(WriteTo.CS);
                WriteTabs(level + 1, WriteTo.CS);
                WriteLine("internal IntPtr _native;", WriteTo.CS);
                if (c.HasPreventDelete)
                {
                    WriteTabs(level + 1, WriteTo.CS);
                    WriteLine("bool _preventDelete;", WriteTo.CS);
                    hasCSWhiteSpace = false;
                }
                hasCSWhiteSpace = false;
            }

            // Write methods
            int overloadIndex = 0;
            bufferBuilder.Clear();

            // Write C# internal constructor
            if (!c.NoInternalConstructor)
            {
                EnsureWhiteSpace(WriteTo.CS);
                WriteTabs(level + 1, WriteTo.CS);
                Write("internal ", WriteTo.CS);
                Write(c.ManagedName, WriteTo.CS);
                Write("(IntPtr native", WriteTo.CS);
                if (c.HasPreventDelete)
                {
                    Write(", bool preventDelete", WriteTo.CS);
                }
                WriteLine(')', WriteTo.CS);
                if (c.BaseClass != null)
                {
                    WriteTabs(level + 2, WriteTo.CS);
                    Write(": base(native", WriteTo.CS);
                    if (c.HasPreventDelete)
                    {
                        if (!c.BaseClass.Target.HasPreventDelete)
                        {
                            // Base class should also have preventDelete
                            //throw new NotImplementedException();
                        }
                        Write(", preventDelete", WriteTo.CS);
                    }
                    else
                    {
                        if (c.BaseClass.Target.HasPreventDelete)
                        {
                            Write(", true", WriteTo.CS);
                        }
                    }
                    WriteLine(')', WriteTo.CS);
                }
                WriteTabs(level + 1, WriteTo.CS);
                WriteLine('{', WriteTo.CS);
                if (c.BaseClass == null)
                {
                    WriteTabs(level + 2, WriteTo.CS);
                    WriteLine("_native = native;", WriteTo.CS);
                    if (c.HasPreventDelete)
                    {
                        WriteTabs(level + 2, WriteTo.CS);
                        WriteLine("_preventDelete = preventDelete;", WriteTo.CS);
                    }
                }
                WriteTabs(level + 1, WriteTo.CS);
                WriteLine('}', WriteTo.CS);
                hasCSWhiteSpace = false;
            }

            // Write constructors
            bool hasConstructors = false;
            if (!c.IsAbstract)
            {
                foreach (MethodDefinition method in c.Methods.Where(m => m.IsConstructor))
                {
                    if (!c.HidePublicConstructors)
                    {
                        WriteMethod(method, level, ref overloadIndex);
                    }
                    hasConstructors = true;
                }

                // Write default constructor
                if (!hasConstructors && !c.IsAbstract && !c.HidePublicConstructors)
                {
                    var constructor = new MethodDefinition(c.Name, c, 0);
                    constructor.IsConstructor = true;
                    WriteMethod(constructor, level, ref overloadIndex);
                }
                overloadIndex = 0;
            }

            // Write methods
            MethodDefinition previousMethod = null;
            foreach (MethodDefinition method in c.Methods.Where(m => !m.IsConstructor).OrderBy(m => m.Name))
            {
                if (previousMethod != null && previousMethod.Name != method.Name)
                {
                    overloadIndex = 0;
                }

                WriteMethod(method, level, ref overloadIndex);
                previousMethod = method;
            }
            overloadIndex = 0;

            // Write properties
            foreach (PropertyDefinition prop in c.Properties)
            {
                WriteProperty(prop, level);
            }

            // Write delete method
            if (c.BaseClass == null)
            {
                var del = new MethodDefinition("delete", c, 0);
                del.ReturnType = new TypeRefDefinition();
                WriteMethod(del, level, ref overloadIndex);
                c.Methods.Remove(del);
                overloadIndex = 0;
            }

            // Write DllImport clauses
            if (bufferBuilder.Length != 0)
            {
                EnsureWhiteSpace(WriteTo.CS);
                Write(bufferBuilder.ToString(), WriteTo.CS);
            }

            WriteTabs(level, WriteTo.CS);
            WriteLine("}", WriteTo.CS);
            hasCSWhiteSpace = false;
            hasClassSeparatingWhitespace = false;
        }
Beispiel #22
0
        private void WriteClass(ClassDefinition c)
        {
            foreach (var child in c.Classes)
            {
                WriteClass(child);
            }

            if (!ClassNeedsExtensions(c))
            {
                return;
            }

            _extensionMethods.Clear();

            EnsureWhiteSpace(WriteTo.CS);
            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine("[EditorBrowsable(EditorBrowsableState.Never)]");
            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine("public static class {0}Extensions", c.ManagedName);
            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine('{');

            foreach (var prop in c.Properties)
            {
                if (_extensionClassesInternal.ContainsKey(prop.Type.ManagedName))
                {
                    // Getter with out parameter
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public unsafe static void Get{0}(this {1} obj, out {2} value)",
                                            prop.Name, c.ManagedName, _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("fixed ({0}* valuePtr = &value)",
                                            _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);
                    WriteTabs(4, WriteTo.Buffer);
                    Write("*(", WriteTo.Buffer);
                    Write(_extensionClassesInternal[prop.Type.ManagedName], WriteTo.Buffer);
                    WriteLine(string.Format("*({0}*)valuePtr = obj.{1};",
                                            _extensionClassesInternal[prop.Type.ManagedName], prop.Name), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair <string, string>("Get" + prop.Name, bufferBuilder.ToString()));

                    // Getter with return value
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public static {0} Get{1}(this {1} obj)",
                                            _extensionClassesExternal[prop.Type.ManagedName], prop.Name, c.ManagedName), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("{0} value;", _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("Get{0}(obj, out value)", prop.Name), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine("return value;", WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair <string, string>("Get" + prop.Name, bufferBuilder.ToString()));

                    if (prop.Setter == null)
                    {
                        continue;
                    }

                    // Setter with ref parameter
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public unsafe static void Set{0}(this {1} obj, ref {2} value)",
                                            prop.Name, c.ManagedName, _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("fixed ({0}* valuePtr = &value)",
                                            _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);
                    WriteTabs(4, WriteTo.Buffer);
                    WriteLine(string.Format("obj.{0} = *({1}*)valuePtr;",
                                            prop.Name, _extensionClassesInternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair <string, string>("Set" + prop.Name, bufferBuilder.ToString()));

                    // Setter with non-ref parameter
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public static void Set{0}(this {1} obj, {2} value)",
                                            prop.Name, c.ManagedName, _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("Set{0}(obj, ref value);", prop.Name), WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair <string, string>("Set" + prop.Name, bufferBuilder.ToString()));
                }
            }

            foreach (var method in c.Methods)
            {
                if (!MethodNeedsExtensions(method))
                {
                    continue;
                }

                WriteMethod(method);
            }

            foreach (var method in _extensionMethods.OrderBy(key => key.Key))
            {
                EnsureWhiteSpace(WriteTo.CS);
                csWriter.Write(method.Value);
                hasCSWhiteSpace = false;
            }

            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine('}');
            hasCSWhiteSpace = false;
        }
        public void WriteWrapperClassConstructor(ClassDefinition @class)
        {
            List<MethodDefinition> baseVirtualMethods;
            var thisVirtualMethods = @class.Methods.Where(x => x.IsVirtual).ToList();
            var virtualMethods = thisVirtualMethods.ToList();
            if (@class.BaseClass != null)
            {
                baseVirtualMethods = @class.BaseClass.Methods.Where(x => x.IsVirtual).ToList();
                virtualMethods.AddRange(baseVirtualMethods);
            }
            else
            {
                baseVirtualMethods = new List<MethodDefinition>();
            }
            var methodCallbacks = virtualMethods.Select(m =>
            {
                string className = baseVirtualMethods.Contains(m) ?
                    GetFullNameC(@class.BaseClass) : GetFullNameC(@class);
                return $"p_{className}_{m.Name} {m.Name}Callback";
            }).ToList();

            if (!_hasCppClassSeparatingWhitespace)
            {
                WriteLine(WriteTo.Header | WriteTo.Source);
                _hasCppClassSeparatingWhitespace = true;
            }
            EnsureWhiteSpace(WriteTo.Source);

            // Wrapper C Constructor
            string wrapperClassName = $"{GetFullNameC(@class)}Wrapper";
            Write(1, "EXPORT ", WriteTo.Header);
            Write($"{wrapperClassName}* {wrapperClassName}_new(", WriteTo.Header | WriteTo.Source);
            WriteLine(ListToLines(methodCallbacks, WriteTo.Header, 1) + ");", WriteTo.Header);
            WriteLine(ListToLines(methodCallbacks, WriteTo.Source) + ")", WriteTo.Source);
            WriteLine('{', WriteTo.Source);
            Write(1, $"return new {wrapperClassName}(", WriteTo.Source);
            WriteLine(
                ListToLines(virtualMethods.Select(m => $"{m.Name}Callback"), WriteTo.Source, 1)
                 + ");", WriteTo.Source);
            WriteLine('}', WriteTo.Source);
            _hasCppClassSeparatingWhitespace = false;
            hasSourceWhiteSpace = false;
        }
        void OutputClass(ClassDefinition c, int level)
        {
            EnsureHeaderWhiteSpace();
            EnsureSourceWhiteSpace();

            // Write access modifier
            WriteTabs(level);
            if (level == 1)
            {
                HeaderWrite("public ");
            }

            // Write class definition
            HeaderWrite("ref class ");
            HeaderWrite(c.ManagedName);
            if (c.IsAbstract)
            {
                HeaderWrite(" abstract");
            }
            if (c.BaseClass != null)
            {
                HeaderWrite(" : ");
                HeaderWriteLine(c.BaseClass.ManagedName);
            }
            else
            {
                if (c.IsTrackingDisposable)
                {
                    HeaderWriteLine(" : ITrackingDisposable");
                }
                else
                {
                    // In C++/CLI, IDisposable is inherited automatically if the destructor and finalizer are defined
                    //HeaderWriteLine(" : IDisposable");
                    if (c.IsStaticClass)
                    {
                        HeaderWrite(" sealed");
                    }
                    HeaderWriteLine();
                }
            }

            WriteTabs(level);
            HeaderWriteLine("{");
            //hasHeaderWhiteSpace = false;

            // Default access for ref class
            var currentAccess = RefAccessSpecifier.Private;

            // Write child classes
            if (c.Classes.Count != 0)
            {
                OutputClasses(c.Classes, ref currentAccess, level);
                currentAccess = RefAccessSpecifier.Public;
                SourceWriteLine();
            }

            // Classes without instances
            if (c.IsStaticClass)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine(string.Format("{0}() {{}}", c.ManagedName));
            }

            // Downcast native pointer if any methods in a derived class use it
            if (c.BaseClass != null && c.Methods.Any(m => !m.IsConstructor && !m.IsStatic))
            {
                EnsureSourceWhiteSpace();
                SourceWriteLine(string.Format("#define Native static_cast<{0}*>(_native)", c.FullName));
                hasSourceWhiteSpace = false;
            }

            // Write the unmanaged pointer to the base class
            if (c.BaseClass == null && !c.IsStaticClass)
            {
                if (c.Classes.Count != 0)
                {
                    HeaderWriteLine();
                }
                if (c.IsTrackingDisposable)
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                    WriteTabs(level + 1);
                    HeaderWriteLine("virtual event EventHandler^ OnDisposing;");
                    WriteTabs(level + 1);
                    HeaderWriteLine("virtual event EventHandler^ OnDisposed;");
                    hasHeaderWhiteSpace = false;
                }
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal);

                WriteTabs(level + 1);
                HeaderWrite(c.FullName);
                HeaderWriteLine("* _native;");
                hasHeaderWhiteSpace = false;
            }

            EnsureHeaderWhiteSpace();
            EnsureSourceWhiteSpace();

            // Private fields
            // _isDisposed flag
            if (c.IsTrackingDisposable)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine("bool _isDisposed;");
                hasHeaderWhiteSpace = false;
            }
            // _preventDelete flag
            if (c.HasPreventDelete)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine("bool _preventDelete;");
                hasHeaderWhiteSpace = false;
            }

            // Write unmanaged constructor
            // TODO: Write constructor from unmanaged pointer only if the class is ever instantiated in this way.
            if (!c.NoInternalConstructor && !c.IsStaticClass)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal);

                WriteTabs(level + 1);
                SourceWrite(c.FullNameManaged);
                SourceWrite("::");
                Write(c.ManagedName);
                Write('(');
                Write(c.FullName);
                Write("* native)");
                HeaderWriteLine(';');
                SourceWriteLine();
                if (c.BaseClass != null)
                {
                    WriteTabs(1, true);
                    SourceWrite(": ");
                    SourceWrite(c.BaseClass.ManagedName);
                    SourceWriteLine("(native)");
                }
                SourceWriteLine('{');
                if (c.BaseClass == null)
                {
                    WriteTabs(1, true);
                    SourceWriteLine("_native = native;");
                }
                SourceWriteLine('}');
                hasHeaderWhiteSpace = false;
                hasSourceWhiteSpace = false;
            }

            // Write destructor & finalizer
            if (c.BaseClass == null && !c.IsStaticClass)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                WriteTabs(level + 1);
                HeaderWriteLine(string.Format("!{0}();", c.ManagedName));
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Protected);
                WriteTabs(level + 1);
                HeaderWriteLine(string.Format("~{0}();", c.ManagedName));
                hasHeaderWhiteSpace = false;

                EnsureSourceWhiteSpace();
                SourceWriteLine(string.Format("{0}::~{1}()", c.FullNameManaged, c.ManagedName));
                SourceWriteLine('{');
                SourceWriteLine(string.Format("\tthis->!{0}();", c.ManagedName));
                SourceWriteLine('}');
                SourceWriteLine();

                SourceWriteLine(string.Format("{0}::!{1}()", c.FullNameManaged, c.ManagedName));
                SourceWriteLine('{');
                if (c.IsTrackingDisposable)
                {
                    SourceWriteLine("\tif (this->IsDisposed)");
                    SourceWriteLine("\t\treturn;");
                    SourceWriteLine();
                    SourceWriteLine("\tOnDisposing(this, nullptr);");
                    SourceWriteLine();
                }
                if (c.HasPreventDelete)
                {
                    SourceWriteLine("\tif (!_preventDelete)");
                    SourceWriteLine("\t{");
                    SourceWriteLine("\t\tdelete _native;");
                    SourceWriteLine("\t}");
                }
                else
                {
                    SourceWriteLine("\tdelete _native;");
                }
                if (c.IsTrackingDisposable)
                {
                    SourceWriteLine("\t_isDisposed = true;");
                    SourceWriteLine();
                    SourceWriteLine("\tOnDisposed(this, nullptr);");
                }
                else
                {
                    SourceWriteLine("\t_native = NULL;");
                }
                SourceWriteLine('}');
                hasSourceWhiteSpace = false;
            }

            // Write constructors
            int constructorCount = 0;
            foreach (MethodDefinition method in c.Methods.Where(m => m.IsConstructor))
            {
                if (!c.HidePublicConstructors)
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                    OutputMethod(method, level);
                }
                constructorCount++;
            }

            // Write default constructor
            if (constructorCount == 0 && !c.IsAbstract && !c.HidePublicConstructors && !c.IsStaticClass)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);

                MethodDefinition constructor = new MethodDefinition(c.Name, c, 0);
                constructor.IsConstructor = true;
                OutputMethod(constructor, level);
                constructorCount++;
            }

            // Write methods
            if (c.Methods.Count - constructorCount != 0)
            {
                EnsureHeaderWhiteSpace();

                foreach (MethodDefinition method in c.Methods)
                {
                    if (!method.IsConstructor && method.Property == null)
                    {
                        EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                        OutputMethod(method, level);
                    }
                }
            }

            // Write properties (includes unmanaged fields and getters/setters)
            foreach (PropertyDefinition prop in c.Properties)
            {
                string typeConditional = GetTypeConditional(prop.Type, c.Header);
                if (typeConditional != null)
                {
                    Write("#ifndef ");
                    WriteLine(typeConditional);
                    hasSourceWhiteSpace = true;
                }
                else
                {
                    EnsureHeaderWhiteSpace();
                }

                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);

                string typeRefName = BulletParser.GetTypeRefName(prop.Type);

                WriteTabs(level + 1);
                HeaderWrite("property ");
                HeaderWrite(typeRefName);
                HeaderWrite(" ");
                HeaderWriteLine(prop.Name);
                WriteTabs(level + 1);
                HeaderWriteLine("{");

                // Getter/Setter
                OutputMethod(prop.Getter, level + 1);
                if (prop.Setter != null)
                {
                    OutputMethod(prop.Setter, level + 1);
                }

                WriteTabs(level + 1);
                HeaderWriteLine("}");

                if (typeConditional != null)
                {
                    WriteLine("#endif");
                    hasSourceWhiteSpace = false;
                }

                hasHeaderWhiteSpace = false;
            }

            WriteTabs(level);
            HeaderWriteLine("};");
            hasHeaderWhiteSpace = false;
        }
 public ClassDefinition(string name, HeaderDefinition header = null, ClassDefinition parent = null)
 {
     Name = name;
     Header = header;
     Parent = parent;
 }
Beispiel #26
0
 protected virtual bool IsExcludedClass(ClassDefinition cl)
 {
     return(false);
 }
        private void WriteClass(ClassDefinition c)
        {
            foreach (var child in c.Classes)
            {
                WriteClass(child);
            }

            if (!ClassNeedsExtensions(c))
            {
                return;
            }

            _extensionMethods.Clear();

            EnsureWhiteSpace(WriteTo.CS);
            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine("[EditorBrowsable(EditorBrowsableState.Never)]");
            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine("public static class {0}Extensions", c.ManagedName);
            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine('{');

            foreach (var prop in c.Properties)
            {
                if (_extensionClassesInternal.ContainsKey(prop.Type.ManagedName))
                {
                    // Getter with out parameter
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public unsafe static void Get{0}(this {1} obj, out {2} value)",
                        prop.Name, c.ManagedName, _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("fixed ({0}* valuePtr = &value)",
                        _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);
                    WriteTabs(4, WriteTo.Buffer);
                    Write("*(", WriteTo.Buffer);
                    Write(_extensionClassesInternal[prop.Type.ManagedName], WriteTo.Buffer);
                    WriteLine(string.Format("*({0}*)valuePtr = obj.{1};",
                        _extensionClassesInternal[prop.Type.ManagedName], prop.Name), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair<string, string>("Get" + prop.Name, bufferBuilder.ToString()));

                    // Getter with return value
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public static {0} Get{1}(this {1} obj)",
                        _extensionClassesExternal[prop.Type.ManagedName], prop.Name, c.ManagedName), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("{0} value;", _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("Get{0}(obj, out value)", prop.Name), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine("return value;", WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair<string, string>("Get" + prop.Name, bufferBuilder.ToString()));

                    if (prop.Setter == null)
                    {
                        continue;
                    }

                    // Setter with ref parameter
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public unsafe static void Set{0}(this {1} obj, ref {2} value)",
                        prop.Name, c.ManagedName, _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("fixed ({0}* valuePtr = &value)",
                        _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);
                    WriteTabs(4, WriteTo.Buffer);
                    WriteLine(string.Format("obj.{0} = *({1}*)valuePtr;",
                        prop.Name, _extensionClassesInternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair<string, string>("Set" + prop.Name, bufferBuilder.ToString()));

                    // Setter with non-ref parameter
                    bufferBuilder.Clear();
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine(string.Format("public static void Set{0}(this {1} obj, {2} value)",
                        prop.Name, c.ManagedName, _extensionClassesExternal[prop.Type.ManagedName]), WriteTo.Buffer);
                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('{', WriteTo.Buffer);

                    WriteTabs(3, WriteTo.Buffer);
                    WriteLine(string.Format("Set{0}(obj, ref value);", prop.Name), WriteTo.Buffer);

                    WriteTabs(2, WriteTo.Buffer);
                    WriteLine('}', WriteTo.Buffer);

                    _extensionMethods.Add(new KeyValuePair<string, string>("Set" + prop.Name, bufferBuilder.ToString()));
                }
            }

            foreach (var method in c.Methods)
            {
                if (!MethodNeedsExtensions(method))
                {
                    continue;
                }

                WriteMethod(method);
            }

            foreach (var method in _extensionMethods.OrderBy(key => key.Key))
            {
                EnsureWhiteSpace(WriteTo.CS);
                csWriter.Write(method.Value);
                hasCSWhiteSpace = false;
            }

            WriteTabs(1, WriteTo.CS);
            csWriter.WriteLine('}');
            hasCSWhiteSpace = false;
        }
        void ParseClassCursor(Cursor cursor)
        {
            string className          = cursor.Spelling;
            string fullyQualifiedName = TypeRefDefinition.GetFullyQualifiedName(cursor);

            ClassDefinition @class;

            if (project.ClassDefinitions.TryGetValue(fullyQualifiedName, out @class))
            {
                if (@class.IsParsed)
                {
                    return;
                }

                @class.Parent = _context.Class;
            }
            else
            {
                switch (cursor.Kind)
                {
                case CursorKind.ClassTemplate:
                    @class = new ClassTemplateDefinition(className, _context.Header, _context.Class);
                    break;

                case CursorKind.EnumDecl:
                    @class = new EnumDefinition(className, _context.Header, _context.Class);
                    break;

                default:
                    @class = new ClassDefinition(className, _context.Header, _context.Class);
                    break;
                }

                @class.NamespaceName = _context.Namespace;

                if (@class.FullyQualifiedName != fullyQualifiedName)
                {
                    Console.WriteLine("Parsing error at " + fullyQualifiedName);
                }
                project.ClassDefinitions[fullyQualifiedName] = @class;
            }

            _context.Class          = @class;
            _context.Class.IsParsed = true;

            // Unnamed struct escapes to the surrounding scope
            if (!(string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl))
            {
                if (_context.Class.Parent != null)
                {
                    _context.Class.Parent.NestedClasses.Add(_context.Class);
                }
                else
                {
                    _context.Header.Classes.Add(_context.Class);
                }
            }

            AccessSpecifier parentMemberAccess = _context.MemberAccess;

            // Default class/struct access specifier
            switch (cursor.Kind)
            {
            case CursorKind.ClassDecl:
                _context.MemberAccess = AccessSpecifier.Private;
                break;

            case CursorKind.StructDecl:
                _context.Class.IsStruct = true;
                _context.MemberAccess   = AccessSpecifier.Public;
                break;

            case CursorKind.ClassTemplate:
                if (cursor.TemplateCursorKind != CursorKind.ClassDecl)
                {
                    _context.MemberAccess = AccessSpecifier.Private;
                }
                else
                {
                    _context.MemberAccess = AccessSpecifier.Public;
                }
                break;

            case CursorKind.TypedefDecl:
                var underlying = cursor.TypedefDeclUnderlyingType.Canonical;
                if (underlying.TypeKind == ClangSharp.Type.Kind.Pointer &&
                    underlying.Pointee.TypeKind == ClangSharp.Type.Kind.FunctionProto)
                {
                    _context.Class.IsFunctionProto = true;
                }
                break;
            }

            if (cursor.Kind == CursorKind.EnumDecl)
            {
                var @enum = _context.Class as EnumDefinition;

                foreach (var constantDecl in cursor.Children
                         .Where(c => c.Kind == CursorKind.EnumConstantDecl))
                {
                    string valueSpelling = "";
                    var    value         = constantDecl.Children.FirstOrDefault();
                    if (value != null)
                    {
                        var valueTokens = _context.TranslationUnit.Tokenize(value.Extent)
                                          .Where(t => t.Kind != TokenKind.Comment &&
                                                 !t.Spelling.Equals(",") &&
                                                 !t.Spelling.Equals("}"));
                        valueSpelling = string.Join("", valueTokens.Select(t => t.Spelling));
                    }
                    @enum.EnumConstants.Add(new EnumConstant(constantDecl.Spelling, valueSpelling));
                }
            }
            else
            {
                cursor.VisitChildren(ClassVisitor);

                if (_context.Class.BaseClass == null)
                {
                    // Clang doesn't give the base class if it's a template,
                    // tokenize the class definition and extract the base template if it exists
                    var tokens = _context.TranslationUnit.Tokenize(cursor.Extent)
                                 .TakeWhile(t => !t.Spelling.Equals("{"))
                                 .SkipWhile(t => !t.Spelling.Equals(":"));
                    if (tokens.Any())
                    {
                        var baseTokens = tokens.ToList();
                        int templSpecStart = -1, templSpecEnd = -1;
                        for (int i = 0; i < baseTokens.Count; i++)
                        {
                            var token = baseTokens[i];
                            if (token.Spelling == "<")
                            {
                                templSpecStart = i;
                            }
                            else if (token.Spelling == ">")
                            {
                                templSpecEnd = i;
                            }
                        }
                        if (templSpecStart != -1 && templSpecEnd != -1)
                        {
                            string template     = baseTokens[templSpecStart - 1].Spelling;
                            string templateSpec = string.Join(" ",
                                                              baseTokens.GetRange(templSpecStart + 1, templSpecEnd - templSpecStart - 1)
                                                              .Select(t => t.Spelling));

                            string          templateName = $"{template}<{templateSpec}>";
                            ClassDefinition classTemplate;
                            if (!project.ClassDefinitions.TryGetValue(templateName, out classTemplate))
                            {
                                var classTemplateNew = new ClassTemplateDefinition(template, _context.Header);
                                classTemplateNew.TemplateParameters.Add(templateSpec);

                                var baseTemplate = project.ClassDefinitions.FirstOrDefault(c => c.Value.Name.Equals(template));
                                classTemplateNew.BaseClass = baseTemplate.Value;

                                project.ClassDefinitions[templateName] = classTemplateNew;
                                classTemplate = classTemplateNew;
                            }

                            _context.Class.BaseClass = classTemplate;
                        }
                    }
                }
            }

            // Restore parent state
            _context.Class        = _context.Class.Parent;
            _context.MemberAccess = parentMemberAccess;
        }
        void FindForwardReferences(List<ClassDefinition> forwardRefs, ClassDefinition c)
        {
            foreach (PropertyDefinition prop in c.Properties)
            {
                AddForwardReference(forwardRefs, prop.Type, c.Header);
            }

            foreach (MethodDefinition method in c.Methods)
            {
                if (method.IsConstructor && c.HidePublicConstructors)
                {
                    continue;
                }

                AddForwardReference(forwardRefs, method.ReturnType, c.Header);

                foreach (ParameterDefinition param in method.Parameters)
                {
                    AddForwardReference(forwardRefs, param.Type, c.Header);
                }
            }

            foreach (ClassDefinition cl in c.Classes)
            {
                FindForwardReferences(forwardRefs, cl);
            }
        }
        private static bool RequiresMathNamespace(ClassDefinition @class)
        {
            if (@class.Classes.Any(RequiresMathNamespace)) return true;
            if (@class.IsExcluded) return false;

            foreach (var method in @class.Methods.Where(m => !m.IsExcluded))
            {
                if (@class.HidePublicConstructors && method.IsConstructor) continue;

                if (_mathClasses.Contains(method.ReturnType.ManagedName)) return true;

                if (method.Parameters.Any(param => _mathClasses.Contains(param.Type.ManagedName)))
                {
                    return true;
                }
            }

            return false;
        }
        void OutputClass(ClassDefinition @class, int level)
        {
            EnsureHeaderWhiteSpace();
            EnsureSourceWhiteSpace();

            // Write access modifier
            WriteTabs(level);
            if (level == 1)
            {
                HeaderWrite("public ");
            }

            // Write class definition
            HeaderWrite($"ref class {@class.ManagedName}");
            if (@class.IsAbstract)
            {
                HeaderWrite(" abstract");
            }
            else if (@class.IsStaticClass)
            {
                HeaderWrite(" sealed");
            }
            if (@class.BaseClass != null)
            {
                HeaderWriteLine($" : {@class.BaseClass.ManagedName}");
            }
            else if (@class.IsTrackingDisposable)
            {
                HeaderWriteLine(" : ITrackingDisposable");
            }
            else
            {
                // In C++/CLI, IDisposable is inherited automatically if the destructor and finalizer are defined
                //HeaderWrite(" : IDisposable");
                HeaderWriteLine();
            }

            WriteTabs(level);
            HeaderWriteLine("{");
            //hasHeaderWhiteSpace = true;

            // Default access for ref class
            var currentAccess = RefAccessSpecifier.Private;

            // Write child classes
            if ([email protected](IsExcludedClass))
            {
                OutputClasses(@class.Classes, ref currentAccess, level);
                currentAccess = RefAccessSpecifier.Public;
                SourceWriteLine();
            }

            // Add a private constructor for classes without instances
            if (@class.IsStaticClass)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine($"{@class.ManagedName}() {{}}");
                hasHeaderWhiteSpace = false;
            }

            // Downcast native pointer if any methods in a derived class use it
            if (@class.BaseClass != null && @class.Methods.Any(m => !m.IsConstructor && !m.IsStatic && !m.IsExcluded))
            {
                EnsureSourceWhiteSpace();
                SourceWriteLine($"#define Native static_cast<{@class.FullyQualifiedName}*>(_native)");
                hasSourceWhiteSpace = false;
            }

            // Write the native pointer to the base class
            if (@class.BaseClass == null && [email protected])
            {
                if (@class.Classes.Any(c => !IsExcludedClass(c)))
                {
                    HeaderWriteLine();
                }
                if (@class.IsTrackingDisposable)
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                    WriteTabs(level + 1);
                    HeaderWriteLine("virtual event EventHandler^ OnDisposing;");
                    WriteTabs(level + 1);
                    HeaderWriteLine("virtual event EventHandler^ OnDisposed;");
                    hasHeaderWhiteSpace = false;
                }
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal);

                WriteTabs(level + 1);
                HeaderWrite(@class.FullyQualifiedName);
                HeaderWriteLine("* _native;");
                hasHeaderWhiteSpace = false;
            }

            EnsureHeaderWhiteSpace();
            EnsureSourceWhiteSpace();

            // Private fields
            // _isDisposed flag
            if (@class.IsTrackingDisposable)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine("bool _isDisposed;");
                hasHeaderWhiteSpace = false;
            }
            // _preventDelete flag
            if (@class.HasPreventDelete)
            {
                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private);
                WriteTabs(level + 1);
                HeaderWriteLine("bool _preventDelete;");
                hasHeaderWhiteSpace = false;
            }

            // Write cached property fields
            foreach (var cachedProperty in @class.CachedProperties.OrderBy(p => p.Key))
            {
                EnsureAccess(level, ref currentAccess, cachedProperty.Value.Access);
                WriteTabs(level + 1);
                string name = cachedProperty.Key;
                name = char.ToLower(name[0]) + name.Substring(1);
                HeaderWriteLine($"{BulletParser.GetTypeRefName(cachedProperty.Value.Property.Type)} _{name};");
                hasHeaderWhiteSpace = false;
            }

            // Write constructors and destructors if not static
            if ([email protected])
            {
                // Write unmanaged constructor
                // TODO: Write constructor from unmanaged pointer only if the class is ever instantiated in this way.
                if ([email protected])
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal);

                    WriteTabs(level + 1);
                    SourceWrite($"{@class.FullNameCppCli}::");
                    Write($"{@class.ManagedName}({@class.FullyQualifiedName}* native)");
                    HeaderWriteLine(';');
                    SourceWriteLine();
                    if (@class.BaseClass != null)
                    {
                        WriteTabs(1, true);
                        SourceWriteLine($": {@class.BaseClass.ManagedName}(native)");
                    }
                    SourceWriteLine('{');
                    if (@class.BaseClass == null)
                    {
                        WriteTabs(1, true);
                        SourceWriteLine("_native = native;");
                    }
                    SourceWriteLine('}');
                    hasHeaderWhiteSpace = false;
                    hasSourceWhiteSpace = false;
                }

                // Write destructor & finalizer
                if (@class.BaseClass == null)
                {
                    // ECMA-372 19.13.1: "The access-specifier of a destructor in a ref class is ignored."
                    WriteTabs(level + 1);
                    HeaderWriteLine($"~{@class.ManagedName}();");
                    // ECMA-372 19.13.2: "The access-specifier of a finalizer in a ref class is ignored."
                    WriteTabs(level + 1);
                    HeaderWriteLine($"!{@class.ManagedName}();");
                    hasHeaderWhiteSpace = false;

                    EnsureSourceWhiteSpace();
                    SourceWriteLine($"{@class.FullNameCppCli}::~{@class.ManagedName}()");
                    SourceWriteLine('{');
                    SourceWriteLine($"\tthis->!{@class.ManagedName}();");
                    SourceWriteLine('}');
                    SourceWriteLine();

                    SourceWriteLine($"{@class.FullNameCppCli}::!{@class.ManagedName}()");
                    SourceWriteLine('{');
                    if (@class.IsTrackingDisposable)
                    {
                        SourceWriteLine("\tif (this->IsDisposed)");
                        SourceWriteLine("\t\treturn;");
                        SourceWriteLine();
                        SourceWriteLine("\tOnDisposing(this, nullptr);");
                        SourceWriteLine();
                    }
                    if (@class.HasPreventDelete)
                    {
                        SourceWriteLine("\tif (!_preventDelete)");
                        SourceWriteLine("\t{");
                        SourceWriteLine("\t\tdelete _native;");
                        SourceWriteLine("\t}");
                    }
                    else
                    {
                        SourceWriteLine("\tdelete _native;");
                    }
                    if (@class.IsTrackingDisposable)
                    {
                        SourceWriteLine("\t_isDisposed = true;");
                        SourceWriteLine();
                        SourceWriteLine("\tOnDisposed(this, nullptr);");
                    }
                    else
                    {
                        SourceWriteLine("\t_native = NULL;");
                    }
                    SourceWriteLine('}');
                    hasSourceWhiteSpace = false;
                }

                // Write public constructors
                if ([email protected] && [email protected])
                {
                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);

                    var constructors = @class.Methods.Where(m => m.IsConstructor);
                    if (constructors.Any())
                    {
                        foreach (var constructor in constructors)
                        {
                            OutputMethod(constructor, level);
                        }
                    }
                }
            }

            // Write non-constructor methods
            var methods = @class.Methods.Where(m => !m.IsConstructor && !m.IsExcluded);
            if (methods.Any())
            {
                EnsureHeaderWhiteSpace();

                foreach (var method in methods)
                {
                    if (method.Property != null) continue;

                    EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);
                    OutputMethod(method, level);
                }
            }

            // Write properties (includes unmanaged fields and getters/setters)
            foreach (PropertyDefinition prop in @class.Properties)
            {
                string typeConditional = GetTypeConditional(prop.Type, @class.Header);
                if (typeConditional != null)
                {
                    WriteLine($"#ifndef {typeConditional}");
                    hasSourceWhiteSpace = true;
                }
                else
                {
                    EnsureHeaderWhiteSpace();
                }

                EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public);

                WriteTabs(level + 1);
                HeaderWriteLine($"property {BulletParser.GetTypeRefName(prop.Type)} {prop.Name}");
                WriteTabs(level + 1);
                HeaderWriteLine("{");

                // Getter/Setter
                OutputMethod(prop.Getter, level + 1);
                if (prop.Setter != null)
                {
                    OutputMethod(prop.Setter, level + 1);
                }

                WriteTabs(level + 1);
                HeaderWriteLine("}");

                if (typeConditional != null)
                {
                    WriteLine("#endif");
                    hasSourceWhiteSpace = false;
                }

                hasHeaderWhiteSpace = false;
            }

            WriteTabs(level);
            HeaderWriteLine("};");
            hasHeaderWhiteSpace = false;
        }
        // Accepts a ClassDefinition for recursion
        void WriteEnumClass(ClassDefinition @class, int level)
        {
            var @enum = @class as EnumDefinition;
            if (@enum == null)
            {
                foreach (var childClass in @class.Classes)
                {
                    WriteEnumClass(childClass, level);
                }
                return;
            }

            EnsureWhiteSpace(WriteTo.CS);

            if (@enum.IsFlags)
            {
                WriteTabs(level, WriteTo.CS);
                WriteLine("[Flags]", WriteTo.CS);
            }

            WriteTabs(level, WriteTo.CS);
            WriteLine($"public enum {@enum.ManagedName}", WriteTo.CS);
            WriteTabs(level, WriteTo.CS);
            WriteLine('{', WriteTo.CS);
            for (int i = 0; i < @enum.EnumConstants.Count; i++)
            {
                WriteTabs(level + 1, WriteTo.CS);
                if (@enum.EnumConstantValues[i].Equals(""))
                {
                    Write(@enum.EnumConstants[i], WriteTo.CS);
                }
                else
                {
                    Write($"{@enum.EnumConstants[i]} = {@enum.EnumConstantValues[i]}", WriteTo.CS);
                }
                if (i < @enum.EnumConstants.Count - 1)
                {
                    Write(',', WriteTo.CS);
                }
                WriteLine(WriteTo.CS);
            }
            WriteTabs(level, WriteTo.CS);
            WriteLine('}', WriteTo.CS);
            hasCSWhiteSpace = false;
        }
        private static bool RequiresInterop(ClassDefinition @class)
        {
            if (@class.Methods.Any(m => !m.IsConstructor && !m.IsExcluded)) return true;

            if ([email protected] && @class.BaseClass == null) return true;

            if ([email protected] && [email protected]) return true;

            return false;
        }
        void WriteClass(ClassDefinition c, int level)
        {
            if (c.IsExcluded || c.IsTypedef || c.IsPureEnum ||
                c is ClassTemplateDefinition || c is EnumDefinition)
            {
                return;
            }

            if (wrapperHeaderGuards.ContainsKey(c.Name))
            {
                WriteClassWrapper(c);
            }

            // Write class definition
            EnsureWhiteSpace(WriteTo.CS);
            WriteTabs(level, WriteTo.CS);
            Write("public ", WriteTo.CS);
            if (c.IsAbstract)
            {
                Write("abstract ", WriteTo.CS);
            }
            Write($"class {c.ManagedName}", WriteTo.CS);
            if (c.BaseClass != null)
            {
                Write(" : ", WriteTo.CS);
                WriteLine(c.BaseClass.FullNameCppCli.Replace("::", "."), WriteTo.CS);
            }
            else if (c.IsStaticClass)
            {
                WriteLine(WriteTo.CS);
            }
            else
            {
                WriteLine(" : IDisposable", WriteTo.CS);
            }
            WriteTabs(level, WriteTo.CS);
            WriteLine("{", WriteTo.CS);
            hasCSWhiteSpace = true;

            // Write child classes
            foreach (var cl in c.Classes.OrderBy(x => x.FullNameCppCli))
            {
                WriteClass(cl, level + 1);
            }

            // Write the native pointer to the base class
            if (c.BaseClass == null && !c.IsStaticClass)
            {
                EnsureWhiteSpace(WriteTo.CS);
                WriteTabs(level + 1, WriteTo.CS);
                WriteLine("internal IntPtr _native;", WriteTo.CS);
                if (c.HasPreventDelete)
                {
                    WriteTabs(level + 1, WriteTo.CS);
                    WriteLine("bool _preventDelete;", WriteTo.CS);
                    hasCSWhiteSpace = false;
                }
                hasCSWhiteSpace = false;
            }

            // Write cached property fields
            if (c.CachedProperties.Any())
            {
                EnsureWhiteSpace(WriteTo.CS);
                foreach (var cachedProperty in c.CachedProperties.OrderBy(p => p.Key))
                {
                    WriteTabs(level + 1, WriteTo.CS);
                    string name = cachedProperty.Key;
                    name = char.ToLower(name[0]) + name.Substring(1);
                    WriteLine(string.Format("{0} {1} _{2};",
                        cachedProperty.Value.Access.ToString().ToLower(),
                        cachedProperty.Value.Property.Type.ManagedNameCS,
                        name), WriteTo.CS);
                }
                hasCSWhiteSpace = false;
            }

            // Write methods
            bufferBuilder.Clear();

            // Write constructors
            if (!c.IsStaticClass)
            {
                // Write C# internal constructor
                if (!c.NoInternalConstructor)
                {
                    EnsureWhiteSpace(WriteTo.CS);
                    WriteTabs(level + 1, WriteTo.CS);
                    Write($"internal {c.ManagedName}(IntPtr native", WriteTo.CS);
                    if (c.HasPreventDelete)
                    {
                        Write(", bool preventDelete", WriteTo.CS);
                    }
                    WriteLine(')', WriteTo.CS);
                    if (c.BaseClass != null)
                    {
                        WriteTabs(level + 2, WriteTo.CS);
                        Write(": base(native", WriteTo.CS);
                        if (c.HasPreventDelete)
                        {
                            if (!c.BaseClass.HasPreventDelete)
                            {
                                // Base class should also have preventDelete
                                //throw new NotImplementedException();
                            }
                            Write(", preventDelete", WriteTo.CS);
                        }
                        else
                        {
                            if (c.BaseClass.HasPreventDelete)
                            {
                                Write(", true", WriteTo.CS);
                            }
                        }
                        WriteLine(')', WriteTo.CS);
                    }
                    WriteTabs(level + 1, WriteTo.CS);
                    WriteLine('{', WriteTo.CS);
                    if (c.BaseClass == null)
                    {
                        WriteTabs(level + 2, WriteTo.CS);
                        WriteLine("_native = native;", WriteTo.CS);
                        if (c.HasPreventDelete)
                        {
                            WriteTabs(level + 2, WriteTo.CS);
                            WriteLine("_preventDelete = preventDelete;", WriteTo.CS);
                        }
                    }
                    WriteTabs(level + 1, WriteTo.CS);
                    WriteLine('}', WriteTo.CS);
                    hasCSWhiteSpace = false;
                }

                // Write public constructors
                if (!c.HidePublicConstructors && !c.IsAbstract)
                {
                    int overloadIndex = 0;
                    var constructors = c.Methods.Where(m => m.IsConstructor && !m.IsExcluded);
                    if (constructors.Any())
                    {
                        foreach (var constructor in constructors)
                        {
                            WriteMethod(constructor, level, ref overloadIndex);
                        }
                    }
                }
            }

            // Write methods
            var methods = c.Methods.Where(m => !m.IsConstructor && !m.IsExcluded).OrderBy(m => m.Name);
            var methodsOverloads = methods.GroupBy(m => m.Name);
            foreach (var groupByName in methodsOverloads)
            {
                int overloadIndex = 0;
                foreach (var method in groupByName)
                {
                    WriteMethod(method, level, ref overloadIndex);
                }
            }

            // Write properties
            foreach (var prop in c.Properties)
            {
                WriteProperty(prop, level);
            }

            // Write delete method
            if (c.BaseClass == null && !c.IsStaticClass)
            {
                int overloadIndex = 0;
                var del = new MethodDefinition("delete", c, 0);
                del.ReturnType = new TypeRefDefinition();
                WriteMethod(del, level, ref overloadIndex);
                c.Methods.Remove(del);
            }

            // Write DllImport clauses
            if (bufferBuilder.Length != 0)
            {
                EnsureWhiteSpace(WriteTo.CS);
                Write(bufferBuilder.ToString(), WriteTo.CS);
            }

            WriteTabs(level, WriteTo.CS);
            WriteLine("}", WriteTo.CS);
            hasCSWhiteSpace = false;
            hasCppClassSeparatingWhitespace = false;
        }
 public ClassTemplateDefinition(string name, HeaderDefinition header = null, ClassDefinition parent = null)
     : base(name, header, parent)
 {
 }
 protected bool IsExcludedClass(ClassDefinition @class)
 {
     return @class is ClassTemplateDefinition || @class is EnumDefinition ||
         @class.IsPureEnum || @class.IsExcluded || @class.IsFunctionProto;
 }
        public void WriteClassWrapperDefinition(ClassDefinition cl)
        {
            List<MethodDefinition> baseAbstractMethods;
            List<MethodDefinition> thisAbstractMethods = cl.Methods.Where(x => x.IsVirtual).ToList();
            List<MethodDefinition> abstractMethods = thisAbstractMethods.ToList();
            if (cl.BaseClass != null)
            {
                baseAbstractMethods = cl.BaseClass.Target.Methods.Where(x => x.IsVirtual).ToList();
                abstractMethods.AddRange(baseAbstractMethods);
            }
            else
            {
                baseAbstractMethods = new List<MethodDefinition>();
            }

            EnsureWhiteSpace(WriteTo.Source);

            // Wrapper C++ Constructor
            Write(string.Format("{0}Wrapper::{0}Wrapper(", cl.Name), WriteTo.Source);
            int numMethods = abstractMethods.Count;
            for (int i = 0; i < numMethods; i++)
            {
                var method = abstractMethods[i];
                string className = baseAbstractMethods.Contains(method) ? cl.BaseClass.ManagedName : cl.ManagedName;
                Write(string.Format("p{0}_{1} {2}Callback", className, method.ManagedName, method.Name), WriteTo.Source);
                if (i != numMethods - 1)
                {
                    Write(", ", WriteTo.Source);
                }
            }
            WriteLine(')', WriteTo.Source);
            WriteLine('{', WriteTo.Source);
            foreach (MethodDefinition method in abstractMethods)
            {
                WriteLine(string.Format("\t_{0}Callback = {0}Callback;", method.Name), WriteTo.Source);
            }
            WriteLine('}', WriteTo.Source);
            WriteLine(WriteTo.Source);

            // Wrapper C++ methods
            foreach (MethodDefinition method in abstractMethods)
            {
                Write(string.Format("{0} {1}Wrapper::{2}(", method.ReturnType.Name, cl.Name, method.Name), WriteTo.Source);
                int numParameters = method.Parameters.Length;
                for (int i = 0; i < numParameters; i++)
                {
                    var param = method.Parameters[i];
                    WriteType(param.Type, WriteTo.Source);
                    Write(" ", WriteTo.Source);
                    Write(param.Name, WriteTo.Source);

                    if (i != numParameters - 1)
                    {
                        Write(", ", WriteTo.Source);
                    }
                }
                WriteLine(')', WriteTo.Source);
                WriteLine('{', WriteTo.Source);
                Write("\t", WriteTo.Source);
                if (!method.IsVoid)
                {
                    Write("return ", WriteTo.Source);
                }
                Write(string.Format("_{0}Callback(", method.Name), WriteTo.Source);
                for (int i = 0; i < numParameters; i++)
                {
                    var param = method.Parameters[i];
                    Write(param.Name, WriteTo.Source);

                    if (i != numParameters - 1)
                    {
                        Write(", ", WriteTo.Source);
                    }
                }
                WriteLine(");", WriteTo.Source);
                WriteLine('}', WriteTo.Source);
                WriteLine(WriteTo.Source);
            }
            WriteLine(WriteTo.Source);
        }
        protected static string GetFullNameC(ClassDefinition @class)
        {
            string className;
            ClassTemplateDefinition template = @class as ClassTemplateDefinition;
            if (template != null)
            {
                className = @class.Name + string.Join("_", template.TemplateParameters);
            }
            else
            {
                className = @class.Name;
            }

            if (@class.Parent != null)
            {
                return $"{GetFullNameC(@class.Parent)}_{@class.Name}";
            }
            if (@class.NamespaceName != "")
            {
                return $"{@class.NamespaceName}_{@class.Name}";
            }
            return @class.Name;
        }
        public void WriteClassWrapperMethodPointers(ClassDefinition cl)
        {
            List<MethodDefinition> baseAbstractMethods;
            List<MethodDefinition> thisAbstractMethods = cl.Methods.Where(x => x.IsVirtual).ToList();
            List<MethodDefinition> abstractMethods = thisAbstractMethods.ToList();
            if (cl.BaseClass != null)
            {
                baseAbstractMethods = cl.BaseClass.Target.Methods.Where(x => x.IsVirtual).ToList();
                abstractMethods.AddRange(baseAbstractMethods);
            }
            else
            {
                baseAbstractMethods = new List<MethodDefinition>();
            }

            foreach (MethodDefinition method in thisAbstractMethods)
            {
                WriteLine(string.Format("#define p{0}_{1} void*", cl.ManagedName, method.ManagedName), WriteTo.Header);
            }
        }
        void WriteClass(ClassDefinition @class, int level)
        {
            if (_wrapperHeaderGuards.ContainsKey(@class.Name))
            {
                WriteWrapperClassConstructor(@class);
            }

            // Write child classes
            foreach (var c in @class.NestedClasses
                .Where(c => !IsExcludedClass(c))
                .OrderBy(c => c.Name))
            {
                WriteClass(c, level + 1);
            }

            // Group methods into constructors, destructors and methods
            var methodGroups = @class.Methods.GroupBy(m =>
            {
                if (m.IsExcluded) return (CursorKind)0;
                if (m.IsConstructor) return CursorKind.Constructor;
                if (m.IsDestructor) return CursorKind.Destructor;
                return CursorKind.CxxMethod;
            }).ToDictionary(g => g.Key);

            // Constructors
            if ([email protected] && [email protected] && [email protected])
            {
                IGrouping<CursorKind, MethodDefinition> constructors;
                if (methodGroups.TryGetValue(CursorKind.Constructor, out constructors))
                {
                    int overloadIndex = 0;
                    foreach (var constructor in methodGroups[CursorKind.Constructor])
                    {
                        WriteMethod(constructor, level, ref overloadIndex);
                    }
                }
            }

            // Methods
            IGrouping<CursorKind, MethodDefinition> methods;
            if (methodGroups.TryGetValue(CursorKind.CxxMethod, out methods))
            {
                foreach (var groupByName in methods.GroupBy(m => m.Name))
                {
                    int overloadIndex = 0;
                    foreach (var method in groupByName)
                    {
                        WriteMethod(method, level, ref overloadIndex);
                    }
                }
            }

            // Destructors
            IGrouping<CursorKind, MethodDefinition> destructors;
            if (methodGroups.TryGetValue(CursorKind.Destructor, out destructors))
            {
                int overloadIndex = 0;
                foreach (var method in destructors)
                {
                    WriteMethod(method, level, ref overloadIndex);
                }
            }

            _hasCppClassSeparatingWhitespace = false;
        }
        private static bool RequiresMathNamespace(ClassDefinition cl)
        {
            if (cl.Classes.Any(RequiresMathNamespace))
            {
                return true;
            }

            foreach (var method in cl.Methods)
            {
                if (method.ReturnType.ManagedName.Equals("Quaternion") ||
                    method.ReturnType.ManagedName.Equals("Transform") ||
                    method.ReturnType.ManagedName.Equals("Vector3"))
                {
                    return true;
                }

                foreach (var param in method.Parameters)
                {
                    if (param.Type.ManagedName.Equals("Quaternion") ||
                        param.Type.ManagedName.Equals("Transform") ||
                        param.Type.ManagedName.Equals("Vector3"))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
        public void WriteWrapperClass(ClassDefinition @class)
        {
            List<MethodDefinition> baseVirtualMethods;
            var thisVirtualMethods = @class.Methods.Where(x => x.IsVirtual).ToList();
            var virtualMethods = thisVirtualMethods.ToList();
            if (@class.BaseClass != null)
            {
                baseVirtualMethods = @class.BaseClass.Methods.Where(x => x.IsVirtual).ToList();
                virtualMethods.AddRange(baseVirtualMethods);
            }
            else
            {
                baseVirtualMethods = new List<MethodDefinition>();
            }
            var methodCallbacks = virtualMethods.Select(m =>
            {
                string className = baseVirtualMethods.Contains(m) ?
                    GetFullNameC(@class.BaseClass) : GetFullNameC(@class);
                return $"p_{className}_{m.Name} {m.Name}Callback";
            }).ToList();

            if (!_hasCppClassSeparatingWhitespace)
            {
                WriteLine();
                _hasCppClassSeparatingWhitespace = true;
            }

            // TODO: string headerGuard = wrapperHeaderGuards[@class.Name];
            string parameters;
            if (thisVirtualMethods.Count != 0)
            {
                foreach (var method in thisVirtualMethods)
                {
                    string methodPtr = $"p_{GetFullNameC(@class)}_{method.Name}";
                    Write($"typedef {method.ReturnType.Name} (*{methodPtr})(", WriteTo.Header);
                    parameters = ListToLines(method.Parameters
                        .Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"), WriteTo.Header);
                    WriteLine($"{parameters});", WriteTo.Header);
                }
                WriteLine();
            }

            // Wrapper class
            string wrapperClassName = $"{GetFullNameC(@class)}Wrapper";
            WriteLine($"class {wrapperClassName} : public {GetFullNameC(@class)}");
            WriteLine("{");
            WriteLine("private:");
            foreach (var m in virtualMethods)
            {
                string className = baseVirtualMethods.Contains(m) ?
                    GetFullNameC(@class.BaseClass) : GetFullNameC(@class);
                WriteLine(1, $"p_{className}_{m.Name} _{m.Name}Callback;");
            }
            WriteLine();
            WriteLine("public:");

            // Wrapper constructor
            Write(1, $"{wrapperClassName}(");
            string constructorParams = ListToLines(methodCallbacks, WriteTo.Header, 1);
            WriteLine($"{constructorParams});");
            WriteLine();

            // Wrapper methods
            foreach (var m in virtualMethods)
            {
                Write(1, $"virtual {m.ReturnType.Name} {m.Name}(");
                string methodParams = ListToLines(
                    m.Parameters.Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"),
                    WriteTo.Header, 1);
                WriteLine(methodParams + ");");
            }

            WriteLine("};");
            _hasCppClassSeparatingWhitespace = false;


            var prevTo = To;
            To = WriteTo.Source;
            EnsureWhiteSpace();

            // Wrapper C++ Constructor
            Write($"{wrapperClassName}::{wrapperClassName}(");
            parameters = ListToLines(methodCallbacks, WriteTo.Source);
            WriteLine($"{parameters})");
            WriteLine('{');
            foreach (var method in virtualMethods)
            {
                WriteLine(1, string.Format("_{0}Callback = {0}Callback;", method.Name));
            }
            WriteLine('}');
            WriteLine();

            // Wrapper C++ methods
            foreach (var method in virtualMethods)
            {
                Write($"{method.ReturnType.Name} {wrapperClassName}::{method.Name}(");
                parameters = ListToLines(method.Parameters
                    .Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"), WriteTo.Source);
                WriteLine($"{parameters})");

                WriteLine('{');
                WriteTabs(1);
                if (!method.IsVoid)
                {
                    Write("return ");
                }
                Write($"_{method.Name}Callback(");
                parameters = ListToLines(method.Parameters
                    .Select(p => p.Name), WriteTo.Source, 1);
                WriteLine($"{parameters});");
                WriteLine('}');
                WriteLine();
            }
            WriteLine();

            To = prevTo;
        }
        void ParseClassCursor(Cursor cursor)
        {
            string className = cursor.Spelling;
            string fullyQualifiedName = TypeRefDefinition.GetFullyQualifiedName(cursor);

            ClassDefinition @class;
            if (project.ClassDefinitions.TryGetValue(fullyQualifiedName, out @class))
            {
                if (@class.IsParsed) return;

                @class.Parent = _context.Class;
            }
            else
            {
                switch(cursor.Kind)
                {
                    case CursorKind.ClassTemplate:
                        @class = new ClassTemplateDefinition(className, _context.Header, _context.Class);
                        break;
                    case CursorKind.EnumDecl:
                        @class = new EnumDefinition(className, _context.Header, _context.Class);
                        break;
                    default:
                        @class = new ClassDefinition(className, _context.Header, _context.Class);
                        break;
                }

                @class.NamespaceName = _context.Namespace;

                if (@class.FullyQualifiedName != fullyQualifiedName)
                {
                    Console.WriteLine("Parsing error at " + fullyQualifiedName);
                }
                project.ClassDefinitions[fullyQualifiedName] = @class;
            }

            _context.Class = @class;
            _context.Class.IsParsed = true;

            // Unnamed struct escapes to the surrounding scope
            if (!(string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl))
            {
                if (_context.Class.Parent != null)
                {
                    _context.Class.Parent.NestedClasses.Add(_context.Class);
                }
                else
                {
                    _context.Header.Classes.Add(_context.Class);
                }
            }

            AccessSpecifier parentMemberAccess = _context.MemberAccess;

            // Default class/struct access specifier
            switch (cursor.Kind)
            {
                case CursorKind.ClassDecl:
                    _context.MemberAccess = AccessSpecifier.Private;
                    break;
                case CursorKind.StructDecl:
                    _context.Class.IsStruct = true;
                    _context.MemberAccess = AccessSpecifier.Public;
                    break;
                case CursorKind.ClassTemplate:
                    if (cursor.TemplateCursorKind != CursorKind.ClassDecl)
                    {
                        _context.MemberAccess = AccessSpecifier.Private;
                    }
                    else
                    {
                        _context.MemberAccess = AccessSpecifier.Public;
                    }
                    break;
                case CursorKind.TypedefDecl:
                    var underlying = cursor.TypedefDeclUnderlyingType.Canonical;
                    if (underlying.TypeKind == ClangSharp.Type.Kind.Pointer &&
                        underlying.Pointee.TypeKind == ClangSharp.Type.Kind.FunctionProto)
                    {
                        _context.Class.IsFunctionProto = true;
                    }
                    break;
            }

            if (cursor.Kind == CursorKind.EnumDecl)
            {
                var @enum = _context.Class as EnumDefinition;

                foreach (var constantDecl in cursor.Children
                    .Where(c => c.Kind == CursorKind.EnumConstantDecl))
                {
                    string valueSpelling = "";
                    var value = constantDecl.Children.FirstOrDefault();
                    if (value != null)
                    {
                        var valueTokens = _context.TranslationUnit.Tokenize(value.Extent)
                            .Where(t => t.Kind != TokenKind.Comment &&
                                !t.Spelling.Equals(",") &&
                                !t.Spelling.Equals("}"));
                        valueSpelling = string.Join("", valueTokens.Select(t => t.Spelling));
                    }
                    @enum.EnumConstants.Add(new EnumConstant(constantDecl.Spelling, valueSpelling));
                }
            }
            else
            {
                cursor.VisitChildren(ClassVisitor);

                if (_context.Class.BaseClass == null)
                {
                    // Clang doesn't give the base class if it's a template,
                    // tokenize the class definition and extract the base template if it exists
                    ParseTemplateBaseCursor(cursor);
                }
            }

            // Restore parent state
            _context.Class = _context.Class.Parent;
            _context.MemberAccess = parentMemberAccess;
        }
 bool IsExcludedClass(ClassDefinition c)
 {
     return(c.IsTypedef || c.IsPureEnum || c.IsExcluded);
 }