diff --git a/src/AST/Class.cs b/src/AST/Class.cs index 9a1d25ab..b1893f71 100644 --- a/src/AST/Class.cs +++ b/src/AST/Class.cs @@ -201,6 +201,22 @@ namespace CppSharp.AST } } + public Method GetRootBaseMethod(Method @override) + { + return (from @base in Bases + let baseMethod = ( + from method in @base.Class.Methods + where + method.Name == @override.Name && + method.ReturnType == @override.ReturnType && + method.Parameters.Count == @override.Parameters.Count && + method.Parameters.SequenceEqual(@override.Parameters, + new ParameterTypeComparer()) + select method).FirstOrDefault() + let rootBaseMethod = @base.Class.GetRootBaseMethod(@override) + select rootBaseMethod ?? baseMethod).FirstOrDefault(); + } + public override T Visit(IDeclVisitor visitor) { return visitor.VisitClassDecl(this); diff --git a/src/AST/Function.cs b/src/AST/Function.cs index 04a20a0a..f922218c 100644 --- a/src/AST/Function.cs +++ b/src/AST/Function.cs @@ -60,6 +60,19 @@ namespace CppSharp.AST } } + public class ParameterTypeComparer : IEqualityComparer + { + public bool Equals(Parameter x, Parameter y) + { + return x.QualifiedType == y.QualifiedType; + } + + public int GetHashCode(Parameter obj) + { + return obj.Type.GetHashCode(); + } + } + public enum FunctionSynthKind { None, diff --git a/src/AST/Method.cs b/src/AST/Method.cs index 74285471..65bbf895 100644 --- a/src/AST/Method.cs +++ b/src/AST/Method.cs @@ -92,6 +92,12 @@ namespace CppSharp.AST Conversion = method.Conversion; } + public Method(Function function) + : base(function) + { + + } + public AccessSpecifierDecl AccessDecl { get; set; } public bool IsVirtual { get; set; } diff --git a/src/AST/TranslationUnit.cs b/src/AST/TranslationUnit.cs index ae3574ff..3cd75826 100644 --- a/src/AST/TranslationUnit.cs +++ b/src/AST/TranslationUnit.cs @@ -14,6 +14,7 @@ namespace CppSharp.AST { Macros = new List(); FilePath = file; + Access = AccessSpecifier.Public; } /// Contains the macros present in the unit. diff --git a/src/Generator/AST/Utils.cs b/src/Generator/AST/Utils.cs index 40dd92b1..ef905fe2 100644 --- a/src/Generator/AST/Utils.cs +++ b/src/Generator/AST/Utils.cs @@ -37,7 +37,7 @@ namespace CppSharp.AST if (method.Kind == CXXMethodKind.Conversion) return true; - if (method.Access == AccessSpecifier.Private) + if (method.Access == AccessSpecifier.Private && !method.IsOverride) return true; return false; diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 1e153420..3bf1e2c1 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -133,16 +133,17 @@ namespace CppSharp TranslationUnitPasses.AddPass(new ResolveIncompleteDeclsPass()); TranslationUnitPasses.AddPass(new CleanInvalidDeclNamesPass()); TranslationUnitPasses.AddPass(new CheckIgnoredDeclsPass()); + TranslationUnitPasses.AddPass(new GenerateInlinesCodePass()); + + library.SetupPasses(this); + TranslationUnitPasses.AddPass(new FindSymbolsPass()); TranslationUnitPasses.AddPass(new MoveOperatorToClassPass()); TranslationUnitPasses.AddPass(new CheckOperatorsOverloadsPass()); TranslationUnitPasses.AddPass(new CheckVirtualOverrideReturnCovariance()); TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); - library.SetupPasses(this); - Generator.SetupPasses(); - TranslationUnitPasses.AddPass(new FieldToPropertyPass()); TranslationUnitPasses.AddPass(new CleanInvalidDeclNamesPass()); TranslationUnitPasses.AddPass(new CheckIgnoredDeclsPass()); @@ -165,7 +166,7 @@ namespace CppSharp public void WriteCode(List outputs) { - var outputPath = Options.OutputDir ?? Directory.GetCurrentDirectory(); + var outputPath = Options.OutputDir; if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); @@ -208,6 +209,8 @@ namespace CppSharp SystemIncludeDirs = new List(); Headers = new List(); + OutputDir = Directory.GetCurrentDirectory(); + var platform = Environment.OSVersion.Platform; var isUnix = platform == PlatformID.Unix || platform == PlatformID.MacOSX; MicrosoftMode = !isUnix; @@ -271,6 +274,20 @@ namespace CppSharp public int MaxIndent; public string CommentPrefix; + private string inlinesLibraryName; + public string InlinesLibraryName + { + get + { + if (string.IsNullOrEmpty(inlinesLibraryName)) + { + return string.Format("{0}-inlines", OutputNamespace); + } + return inlinesLibraryName; + } + set { inlinesLibraryName = value; } + } + public bool IsCSharpGenerator { get { return GeneratorKind == LanguageGeneratorKind.CSharp; } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 83c11c47..3ffdb32c 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -899,7 +899,9 @@ namespace CppSharp.Generators.CSharp if (prop.Ignore) continue; PushBlock(CSharpBlockKind.Property); - WriteLine("public {0} {1}", prop.Type, SafeIdentifier(prop.Name)); + WriteLine("{0} {1} {2}", + prop.Access == AccessSpecifier.Public ? "public" : "protected", + prop.Type, SafeIdentifier(prop.Name)); WriteStartBraceIndent(); if (prop.Field != null) @@ -1481,8 +1483,15 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Method); GenerateDeclarationCommon(method); - Write(Driver.Options.GenerateAbstractImpls && - @class.IsAbstract && method.IsConstructor ? "protected " : "public "); + switch (GetValidMethodAccess(method, @class)) + { + case AccessSpecifier.Public: + Write("public "); + break; + case AccessSpecifier.Protected: + Write("protected "); + break; + } if (method.IsVirtual && !method.IsOverride && (!Driver.Options.GenerateAbstractImpls || !method.IsPure)) @@ -1568,6 +1577,18 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } + private static AccessSpecifier GetValidMethodAccess(Method method, Class @class) + { + switch (method.Access) + { + case AccessSpecifier.Public: + return AccessSpecifier.Public; + default: + return method.IsOverride ? + @class.GetRootBaseMethod(method).Access : method.Access; + } + } + private void GenerateVirtualTableFunctionCall(Function method, Class @class) { string delegateId; @@ -1756,10 +1777,16 @@ namespace CppSharp.Generators.CSharp var isIntPtr = retTypeName.Contains("IntPtr"); - if (retType.Type.IsPointer() && isIntPtr) + Type pointee; + if (retType.Type.IsPointerTo(out pointee) && isIntPtr) { - WriteLine("if ({0} == global::System.IntPtr.Zero) return null;", - Generator.GeneratedIdentifier("ret")); + PrimitiveType primitive; + string @null = (pointee.Desugar().IsPrimitiveType(out primitive) || + pointee.Desugar().IsPointer()) && + !CSharpTypePrinter.IsConstCharString(retType) ? + "IntPtr.Zero" : "null"; + WriteLine("if ({0} == global::System.IntPtr.Zero) return {1};", + Generator.GeneratedIdentifier("ret"), @null); } var ctx = new CSharpMarshalContext(Driver) @@ -1953,7 +1980,8 @@ namespace CppSharp.Generators.CSharp WriteLine("[UnmanagedFunctionPointerAttribute(CallingConvention.{0})]", Helpers.ToCSharpCallConv(functionType.CallingConvention)); TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); - WriteLine("public {0};", + WriteLine("{0} {1};", + typedef.Access == AccessSpecifier.Public ? "public" : "protected", string.Format(TypePrinter.VisitDelegate(functionType).Type, SafeIdentifier(typedef.Name))); TypePrinter.PopContext(); diff --git a/src/Generator/Passes/CheckIgnoredDecls.cs b/src/Generator/Passes/CheckIgnoredDecls.cs index b715faba..ea3246ef 100644 --- a/src/Generator/Passes/CheckIgnoredDecls.cs +++ b/src/Generator/Passes/CheckIgnoredDecls.cs @@ -5,15 +5,21 @@ namespace CppSharp.Passes { public class CheckIgnoredDeclsPass : TranslationUnitPass { - public CheckIgnoredDeclsPass() - { - } - public override bool VisitDeclaration(Declaration decl) { if (decl.ExplicityIgnored) return false; + if (decl.Access == AccessSpecifier.Private) + { + Method method = decl as Method; + if (method == null || !method.IsOverride) + { + decl.ExplicityIgnored = true; + return false; + } + } + if (decl.IsDependent) { decl.ExplicityIgnored = true; @@ -24,16 +30,6 @@ namespace CppSharp.Passes return true; } - public override bool VisitClassDecl(Class @class) - { - if (@class.Access == AccessSpecifier.Private) - { - @class.ExplicityIgnored = true; - return false; - } - return base.VisitClassDecl(@class); - } - public override bool VisitFieldDecl(Field field) { if (!VisitDeclaration(field)) @@ -86,6 +82,20 @@ namespace CppSharp.Passes function.Name, msg); return false; } + + if (param.Kind == ParameterKind.IndirectReturnType) + { + Class retClass; + param.Type.Desugar().IsTagDecl(out retClass); + if (retClass == null) + { + function.ExplicityIgnored = true; + Driver.Diagnostics.EmitWarning( + "Function '{0}' was ignored due to an indirect return param not of a tag type", + function.Name); + return false; + } + } } return true; @@ -93,16 +103,7 @@ namespace CppSharp.Passes public override bool VisitMethodDecl(Method method) { - if (!VisitDeclaration(method)) - return false; - - if (method.Access == AccessSpecifier.Private) - { - method.ExplicityIgnored = true; - return false; - } - - return base.VisitMethodDecl(method); + return VisitDeclaration(method) && base.VisitMethodDecl(method); } public override bool VisitTypedefDecl(TypedefDecl typedef) diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index df41f004..0e75f235 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -54,6 +54,8 @@ namespace CppSharp.Passes @operator.ExplicityIgnored = true; continue; } + if (@operator.SynthKind == FunctionSynthKind.NonMemberOperator) + continue; // Handle missing operator parameters if (@operator.IsStatic) @@ -84,10 +86,8 @@ namespace CppSharp.Passes var missingKind = CheckMissingOperatorOverloadPair(@class, out index, op1, op2, op.Parameters.Last().Type); - if (missingKind == CXXOperatorKind.None) - return; - - if (op.Ignore) continue; + if (missingKind == CXXOperatorKind.None || op.Ignore) + continue; var method = new Method() { diff --git a/src/Generator/Passes/CleanInvalidDeclNamesPass.cs b/src/Generator/Passes/CleanInvalidDeclNamesPass.cs index d69f42ea..fc2092e8 100644 --- a/src/Generator/Passes/CleanInvalidDeclNamesPass.cs +++ b/src/Generator/Passes/CleanInvalidDeclNamesPass.cs @@ -34,6 +34,13 @@ namespace CppSharp.Passes if (decl is Namespace) return true; + // types with empty names are assumed to be private + if (decl is Class && string.IsNullOrWhiteSpace(decl.Name)) + { + decl.ExplicityIgnored = true; + return false; + } + decl.Name = CheckName(decl.Name); StringHelpers.CleanupText(ref decl.DebugText); diff --git a/src/Generator/Passes/FieldToPropertyPass.cs b/src/Generator/Passes/FieldToPropertyPass.cs index 2997e1d6..a719ea80 100644 --- a/src/Generator/Passes/FieldToPropertyPass.cs +++ b/src/Generator/Passes/FieldToPropertyPass.cs @@ -16,11 +16,12 @@ namespace CppSharp.Passes if (ASTUtils.CheckIgnoreField(field)) return false; - var prop = new Property() + var prop = new Property { Name = field.Name, Namespace = field.Namespace, QualifiedType = field.QualifiedType, + Access = field.Access, Field = field }; @class.Properties.Add(prop); diff --git a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs index 3a614eff..6f17906a 100644 --- a/src/Generator/Passes/GenerateAbstractImplementationsPass.cs +++ b/src/Generator/Passes/GenerateAbstractImplementationsPass.cs @@ -34,7 +34,13 @@ namespace CppSharp.Passes return false; if (@class.IsAbstract) + { + foreach (var ctor in from ctor in @class.Constructors + where ctor.Access == AccessSpecifier.Public + select ctor) + ctor.Access = AccessSpecifier.Protected; internalImpls.Add(AddInternalImplementation(@class)); + } return base.VisitClassDecl(@class); } diff --git a/src/Generator/Passes/GenerateInlinesCodePass.cs b/src/Generator/Passes/GenerateInlinesCodePass.cs new file mode 100644 index 00000000..fb16da61 --- /dev/null +++ b/src/Generator/Passes/GenerateInlinesCodePass.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using CppSharp.AST; + +namespace CppSharp.Passes +{ + public class GenerateInlinesCodePass : TranslationUnitPass + { + private TranslationUnit currentUnit; + private readonly List headers = new List(); + private readonly List mangledInlines = new List(); + + public override bool VisitLibrary(Library library) + { + bool result = base.VisitLibrary(library); + Directory.CreateDirectory(Driver.Options.OutputDir); + WriteInlinesIncludes(); + WriteInlinedSymbols(); + return result; + } + + private void WriteInlinesIncludes() + { + var cppBuilder = new StringBuilder(); + headers.Sort(); + foreach (var header in headers) + cppBuilder.AppendFormat("#include \"{0}\"\n", header); + var cpp = string.Format("{0}.cpp", Driver.Options.InlinesLibraryName); + var path = Path.Combine(Driver.Options.OutputDir, cpp); + File.WriteAllText(path, cppBuilder.ToString()); + } + + private void WriteInlinedSymbols() + { + switch (Driver.Options.Abi) + { + case CppAbi.Microsoft: + var defBuilder = new StringBuilder("EXPORTS\r\n"); + for (int i = 0; i < mangledInlines.Count; i++) + defBuilder.AppendFormat(" {0} @{1}\r\n", + mangledInlines[i], i + 1); + var def = string.Format("{0}.def", Driver.Options.InlinesLibraryName); + File.WriteAllText(Path.Combine(Driver.Options.OutputDir, def), + defBuilder.ToString()); + break; + default: + var symbolsBuilder = new StringBuilder(); + foreach (var mangledInline in mangledInlines) + symbolsBuilder.AppendFormat("{0}\n", mangledInline); + var txt = string.Format("{0}.txt", Driver.Options.InlinesLibraryName); + File.WriteAllText(Path.Combine(Driver.Options.OutputDir, txt), + symbolsBuilder.ToString()); + break; + } + } + + public override bool VisitTranslationUnit(TranslationUnit unit) + { + currentUnit = unit; + return base.VisitTranslationUnit(unit); + } + + public override bool VisitFunctionDecl(Function function) + { + CheckForSymbols(function); + return base.VisitFunctionDecl(function); + } + + public override bool VisitVariableDecl(Variable variable) + { + CheckForSymbols(variable); + return base.VisitVariableDecl(variable); + } + + private void CheckForSymbols(IMangledDecl mangled) + { + string symbol = mangled.Mangled; + var declaration = (Declaration) mangled; + if (!declaration.Ignore && AccessValid(declaration) && + !Driver.LibrarySymbols.FindSymbol(ref symbol) && + !currentUnit.FilePath.EndsWith("_impl.h") && + !currentUnit.FilePath.EndsWith("_p.h")) + { + if (!headers.Contains(currentUnit.FileName)) + headers.Add(currentUnit.FileName); + if (!mangledInlines.Contains(mangled.Mangled)) + mangledInlines.Add(mangled.Mangled); + } + } + + private static bool AccessValid(Declaration declaration) + { + if (declaration.Access == AccessSpecifier.Private) + { + var method = declaration as Method; + return method != null && method.IsOverride; + } + return true; + } + } +} diff --git a/src/Generator/Passes/MoveOperatorToClassPass.cs b/src/Generator/Passes/MoveOperatorToClassPass.cs index c4cdf4d3..b35753b5 100644 --- a/src/Generator/Passes/MoveOperatorToClassPass.cs +++ b/src/Generator/Passes/MoveOperatorToClassPass.cs @@ -23,28 +23,20 @@ namespace CppSharp.Passes if (!FunctionToInstanceMethodPass.GetClassParameter(param, out @class)) return false; - function.ExplicityIgnored = true; - // Create a new fake method so it acts as a static method. - var method = new Method() + + var method = new Method(function) { Namespace = @class, - OriginalNamespace = function.Namespace, - Name = function.Name, - OriginalName = function.OriginalName, - Mangled = function.Mangled, - Access = AccessSpecifier.Public, Kind = CXXMethodKind.Operator, - ReturnType = function.ReturnType, - Parameters = new List(function.Parameters).Skip(1).ToList(), - CallingConvention = function.CallingConvention, - IsVariadic = function.IsVariadic, - IsInline = function.IsInline, OperatorKind = function.OperatorKind, SynthKind = FunctionSynthKind.NonMemberOperator, - OriginalFunction = function + OriginalFunction = null, + IsStatic = true }; + function.ExplicityIgnored = true; + @class.Methods.Add(method); Driver.Diagnostics.Debug("Function converted to operator: {0}::{1}", diff --git a/src/Generator/Utils/ParameterTypeComparer.cs b/src/Generator/Utils/ParameterTypeComparer.cs deleted file mode 100644 index a8740cdc..00000000 --- a/src/Generator/Utils/ParameterTypeComparer.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using CppSharp.AST; - -namespace CppSharp.Utils -{ - public class ParameterTypeComparer : IEqualityComparer - { - public bool Equals(Parameter x, Parameter y) - { - return x.QualifiedType == y.QualifiedType; - } - - public int GetHashCode(Parameter obj) - { - return obj.Type.GetHashCode(); - } - } -} diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index 9bc6fc5d..c08ec478 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -1,3 +1,4 @@ +using System.Reflection; using NUnit.Framework; using Basic; @@ -95,5 +96,12 @@ public class BasicTests Assert.AreEqual(abstractFoo.pureFunction1(), 10); Assert.AreEqual(abstractFoo.pureFunction2(), 15); } + + [Test, Ignore] + public void TestPropertyAccessModifier() + { + Assert.That(typeof(Foo2).GetProperty("P", + BindingFlags.Instance | BindingFlags.NonPublic), Is.Not.Null); + } } \ No newline at end of file diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 1ea349f0..4426f829 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -25,6 +25,10 @@ public: Foo2 operator<<(signed int i); Foo2 operator<<(signed long l); + + // TODO: uncomment when the C++/CLI back-end supports protected members +//protected: +// int P; }; struct DLL_API Bar