private void WriteMethodMarshal(ManagedMethod method, int numParameters) { var nativeMethod = method.Native; string methodName = nativeMethod.IsConstructor ? $"{nativeMethod.Parent.FullyQualifiedName}" : $"{nativeMethod.Name}"; var currentParams = method.Parameters.Take(numParameters); var paramStrings = currentParams.Select(p => { string marshal = BulletParser.GetTypeMarshalCppCli(p); if (!string.IsNullOrEmpty(marshal)) { return(marshal); } var paramType = p.Native.Type; if (paramType.IsBasic) { return(p.Name); } string paramString = ""; switch (paramType.Kind) { case TypeKind.Pointer: case TypeKind.LValueReference: if (paramType.Kind == TypeKind.LValueReference) { // Dereference paramString = "*"; } if (paramType.Referenced.Target?.BaseClass != null) { // Cast native pointer from base class paramString += $"({paramType.Referenced.FullName}*)"; } break; } paramString += p.Name; if (paramType.Kind == TypeKind.Pointer && paramType.Referenced.Kind == TypeKind.Void) { paramString += ".ToPointer()"; } else { paramString += "->_native"; } return(paramString); }); Write($"{methodName}("); string parameters = ListToLines(paramStrings, WriteTo.Source, 1); Write($"{parameters})"); }
static void Main(string[] args) { // If true, outputs C++/CLI wrapper, // if false, outputs C wrapper with C# code. bool cppCliMode = false; var project = WrapperProject.FromFile("bullet3.xml"); if (!project.VerifyFiles()) { Console.ReadKey(); return; } project.ReadCpp(); var parser = new BulletParser(project); parser.Parse(); Console.WriteLine("Parsing complete"); CWriter cWriter = new CWriter(parser); //project.CProjectPath = "c_temp"; cWriter.Output(); DotNetWriter dotNetWriter; if (cppCliMode) { dotNetWriter = new CppCliWriter(parser); } else { dotNetWriter = new PInvokeWriter(parser); var extensionsWriter = new ExtensionsWriter(parser); extensionsWriter.Output(); } dotNetWriter.Output(); OutputSolution(TargetVS.VS2008, parser); OutputSolution(TargetVS.VS2010, parser); OutputSolution(TargetVS.VS2012, parser); OutputSolution(TargetVS.VS2013, parser); OutputSolution(TargetVS.VS2015, parser); //project.Save(); CMakeWriter cmake = new CMakeWriter(parser); cmake.Output(); Console.Write("Press any key to continue..."); Console.ReadKey(); }
static void Main(string[] args) { // If true, outputs C++/CLI wrapper, // if false, outputs C wrapper with C# code. bool cppCliMode = false; //var subset = new AssemblySubset(); //subset.LoadAssembly("..\\..\\..\\bulletsharp\\demos\\Generic\\bin\\Release\\BasicDemo.exe", "BulletSharp"); //subset.LoadAssembly("..\\..\\..\\bulletsharp\\demos\\Generic\\bin\\Release\\DemoFramework.dll", "BulletSharp"); string sourceFolder = "D:\\src\\bullet3\\src\\"; //sourceFolder = "..\\..\\..\\bullet\\src\\"; if (!Directory.Exists(sourceFolder)) { Console.WriteLine("Source folder \"" + sourceFolder + "\" not found"); Console.Write("Press any key to continue..."); Console.ReadKey(); return; } var reader = new CppReader(sourceFolder); var parser = new BulletParser(reader.ClassDefinitions, reader.HeaderDefinitions); var externalHeaders = parser.ExternalHeaders.Values; if (cppCliMode) { var writer = new CppCliWriter(externalHeaders, NamespaceName); writer.Output(); } else { var writer = new PInvokeWriter(externalHeaders, NamespaceName); writer.Output(); var extensionWriter = new ExtensionsWriter(externalHeaders, NamespaceName); extensionWriter.Output(); } OutputSolution(TargetVS.VS2008, externalHeaders); OutputSolution(TargetVS.VS2010, externalHeaders); OutputSolution(TargetVS.VS2012, externalHeaders); OutputSolution(TargetVS.VS2013, externalHeaders); CMakeWriter cmake = new CMakeWriter(parser.ExternalHeaders, NamespaceName); cmake.Output(); Console.Write("Press any key to continue..."); Console.ReadKey(); }
static void Main(string[] args) { // If true, outputs C++/CLI wrapper, // if false, outputs C wrapper with C# code. bool cppCliMode = false; var project = WrapperProject.FromFile("bullet3.xml"); if (!project.VerifyFiles()) { Console.ReadKey(); return; } var reader = new CppReader(project); var parser = new BulletParser(project); parser.Parse(); Console.WriteLine("Parsing complete"); WrapperWriter writer; if (cppCliMode) { writer = new CppCliWriter(project.HeaderDefinitions.Values, project.NamespaceName); } else { writer = new PInvokeWriter(project); var extensionWriter = new ExtensionsWriter(project.HeaderDefinitions.Values, project.NamespaceName); extensionWriter.Output(); } writer.Output(); OutputSolution(TargetVS.VS2008, project); OutputSolution(TargetVS.VS2010, project); OutputSolution(TargetVS.VS2012, project); OutputSolution(TargetVS.VS2013, project); OutputSolution(TargetVS.VS2015, project); //project.Save(); CMakeWriter cmake = new CMakeWriter(project.HeaderDefinitions, project.NamespaceName); cmake.Output(); Console.Write("Press any key to continue..."); Console.ReadKey(); }
static void Main(string[] args) { // If true, outputs C++/CLI wrapper, // if false, outputs C wrapper with C# code. bool cppCliMode = false; var project = WrapperProject.FromFile("bullet3.xml"); if (!project.VerifyFiles()) { Console.ReadKey(); return; } var reader = new CppReader(project); var parser = new BulletParser(project); parser.Parse(); Console.WriteLine("Parsing complete"); WrapperWriter writer; if (cppCliMode) { writer = new CppCliWriter(project); } else { writer = new PInvokeWriter(project); var extensionWriter = new ExtensionsWriter(project.HeaderDefinitions.Values, project.NamespaceName); extensionWriter.Output(); } writer.Output(); OutputSolution(TargetVS.VS2008, project); OutputSolution(TargetVS.VS2010, project); OutputSolution(TargetVS.VS2012, project); OutputSolution(TargetVS.VS2013, project); OutputSolution(TargetVS.VS2015, project); //project.Save(); CMakeWriter cmake = new CMakeWriter(project); cmake.Output(); Console.Write("Press any key to continue..."); Console.ReadKey(); }
void WriteProperty(PropertyDefinition prop, int level) { var getterNative = prop.Getter.Native; var setterNative = prop.Setter?.Native; EnsureWhiteSpace(); Write(level + 1, $"public "); if (getterNative.IsStatic) { Write("static "); } WriteLine($"{GetTypeNameCS(prop.Type)} {prop.Name}"); WriteLine(level + 1, "{"); if (prop.Parent.CachedProperties.ContainsKey(prop.Name)) { var cachedProperty = prop.Parent.CachedProperties[prop.Name]; WriteLine(level + 2, $"get {{ return {cachedProperty.CacheFieldName}; }}"); if (setterNative != null) { WriteLine(level + 2, "set"); WriteLine(level + 2, "{"); Write(level + 3, $"{GetFullNameC(prop.Parent.Native)}_{setterNative.Name}("); if (!setterNative.IsStatic) { Write("_native, "); } WriteLine($"{GetTypeSetterCSMarshal(prop.Type)});"); WriteLine(level + 3, $"{cachedProperty.CacheFieldName} = value;"); WriteLine(level + 2, "}"); } } else { var type = prop.Type; if ((type.Target != null && type.Target.MarshalAsStruct) || (type.Kind == TypeKind.LValueReference && type.Referenced.Target != null && type.Referenced.Target.MarshalAsStruct)) { WriteLine(level + 2, "get"); WriteLine(level + 2, "{"); WriteLine(level + 3, $"{GetTypeNameCS(type)} value;"); Write(level + 3, $"{GetFullNameC(prop.Parent.Native)}_{getterNative.Name}("); if (!getterNative.IsStatic) { Write("_native, "); } WriteLine("out value);"); WriteLine(level + 3, "return value;"); WriteLine(level + 2, "}"); } else { string objPtr = getterNative.IsStatic ? "" : "_native"; WriteLine(level + 2, string.Format("get {{ return {0}{1}_{2}({3}){4}; }}", BulletParser.GetTypeMarshalConstructorStartCS(getterNative), GetFullNameC(prop.Parent.Native), getterNative.Name, objPtr, BulletParser.GetTypeMarshalConstructorEndCS(getterNative))); } if (setterNative != null) { string marshal = GetTypeSetterCSMarshal(prop.Type); Write(level + 2, $"set {{ {GetFullNameC(prop.Parent.Native)}_{setterNative.Name}("); if (!setterNative.IsStatic) { Write("_native, "); } WriteLine($"{marshal}); }}"); } } WriteLine(level + 1, "}"); hasCSWhiteSpace = false; }
void WriteMethodDefinition(ManagedMethod method, int numParameters, int overloadIndex, int level, ManagedParameter outValueParameter) { var nativeMethod = method.Native; if (nativeMethod.IsConstructor) { if (method.Parent.BaseClass == null) { Write("_native = "); } Write($"{GetFullNameC(method.Parent.Native)}_new"); } else { if (!nativeMethod.IsVoid) { if (outValueParameter != null) { // Temporary variable WriteLine(string.Format("{0} {1};", DotNetParser.GetManaged(outValueParameter.Native.Type.Referenced.Target).Name, outValueParameter.Name)); WriteTabs(level + 2); } else { Write($"return {BulletParser.GetTypeMarshalConstructorStartCS(nativeMethod)}"); } } Write($"{GetFullNameC(method.Parent.Native)}_{method.Native.Name}"); } if (overloadIndex != 0) { Write((overloadIndex + 1).ToString()); } Write('('); var parameters = method.Parameters.Take(numParameters) .Select(p => GetParameterMarshal(p)); if (outValueParameter != null) { parameters = parameters.Concat(new[] { $"out {outValueParameter.Name }" }); } // The first parameter is the instance pointer (if not constructor or static method) if (!nativeMethod.IsConstructor && !nativeMethod.IsStatic) { parameters = new[] { "_native" }.Concat(parameters); } Write(ListToLines(parameters, WriteTo.CS, level + 2)); if (nativeMethod.IsConstructor && method.Parent.BaseClass != null) { Write(")"); if (method.Parent.BaseClass.Native.HasPreventDelete) { Write(", false"); } WriteLine(")"); WriteLine(level + 1, "{"); } else { if (!nativeMethod.IsConstructor && !nativeMethod.IsVoid) { Write(BulletParser.GetTypeMarshalConstructorEndCS(nativeMethod)); } WriteLine(");"); } // Cache property values if (nativeMethod.IsConstructor) { var methodParent = method.Parent; while (methodParent != null) { foreach (var cachedProperty in methodParent.CachedProperties.OrderBy(p => p.Key)) { foreach (var param in method.Parameters) { if (param.Name.ToLower().Equals(cachedProperty.Key.ToLower()) && GetName(param.Native.Type).Equals(GetName(cachedProperty.Value.Property.Type))) { WriteLine(level + 2, $"{cachedProperty.Value.CacheFieldName} = {param.Name};"); } } } methodParent = methodParent.BaseClass; } } // Return temporary variable if (outValueParameter != null) { WriteLine(level + 2, $"return {outValueParameter.Name};"); } }
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; }
void OutputMethod(MethodDefinition method, int level, int numOptionalParams = 0) { var parentClass = method.Parent; // No whitespace between get/set methods if (!(method.Property != null && method.Equals(method.Property.Setter))) { EnsureSourceWhiteSpace(); hasHeaderWhiteSpace = false; } // #ifndef DISABLE_FEATURE bool hasConditional = false; if (method.Property == null) { foreach (var param in method.Parameters) { string typeConditional = GetTypeConditional(param.Type, parentClass.Header); if (typeConditional != null) { Write("#ifndef "); WriteLine(typeConditional); hasSourceWhiteSpace = true; hasConditional = true; } } } WriteTabs(level + 1); // "static" if (method.IsStatic) { HeaderWrite("static "); } // Definition: return type if (!method.IsConstructor) { var returnType = method.ReturnType; if (method.Property != null) { if (method.Equals(method.Property.Getter)) { // If property name matches type name, resolve ambiguity if (method.Property.Name.Equals(method.Property.Type.ManagedName)) { HeaderWrite(NamespaceName + "::"); } // Getter with parameter for return value if (method.Parameters.Length == 1) { returnType = method.Parameters[0].Type; } } } Write(BulletParser.GetTypeRefName(returnType)); Write(' '); } // Definition: name SourceWrite(parentClass.FullNameManaged); SourceWrite("::"); if (method.IsConstructor) { Write(parentClass.ManagedName); } else { if (method.Property != null) { SourceWrite(method.Property.Name); SourceWrite("::"); if (method.Property.Getter.Equals(method)) { Write("get"); } else { Write("set"); } } else { Write(method.ManagedName); } } Write('('); // Definition: parameters int numParameters = method.Parameters.Length - numOptionalParams; // Getters with parameter for return value if (numParameters == 1 && method.Property != null && method.Equals(method.Property.Getter)) { numParameters = 0; } bool hasOptionalParam = false; for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; Write(BulletParser.GetTypeRefName(param.Type)); Write(' '); Write(param.ManagedName); if (param.IsOptional) { hasOptionalParam = true; } if (i != numParameters - 1) { if (_headerLineLength >= LineBreakWidth) { HeaderWriteLine(","); WriteTabs(level + 2); } else { HeaderWrite(", "); } if (_sourceLineLength >= LineBreakWidth) { SourceWriteLine(","); WriteTabs(1, true); } else { SourceWrite(", "); } } } HeaderWriteLine(");"); SourceWriteLine(')'); // Constructor chaining bool doConstructorChaining = false; if (method.IsConstructor && parentClass.BaseClass != null) { // If there is no need for marshalling code, we can chain constructors doConstructorChaining = true; foreach (var param in method.Parameters) { if (BulletParser.TypeRequiresMarshal(param.Type)) { doConstructorChaining = false; break; } } WriteTabs(1, true); SourceWrite(": "); SourceWrite(parentClass.BaseClass.ManagedName); SourceWrite('('); if (doConstructorChaining) { SourceWrite("new "); OutputMethodMarshal(method, numParameters); if (parentClass.BaseClass.Target.HasPreventDelete) { SourceWrite(", false"); } } else { SourceWrite('0'); } SourceWriteLine(')'); } // Method definition SourceWriteLine('{'); if (!doConstructorChaining) { // Type marshalling prologue bool needTypeMarshalEpilogue = false; if (method.Field == null) { for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; string prologue = BulletParser.GetTypeMarshalPrologueCppCli(param); if (!string.IsNullOrEmpty(prologue)) { WriteTabs(1, true); SourceWriteLine(prologue); } // Do we need a type marshalling epilogue? if (!needTypeMarshalEpilogue) { string epilogue = BulletParser.GetTypeMarshalEpilogueCppCli(param); if (!string.IsNullOrEmpty(epilogue)) { needTypeMarshalEpilogue = true; } } } } WriteTabs(1, true); if (method.IsConstructor) { SourceWrite("_native = new "); } else { if (!method.IsVoid) { //if (method.ReturnType.IsBasic || method.ReturnType.Referenced != null) if (needTypeMarshalEpilogue) { // Return after epilogue (cleanup) SourceWrite(BulletParser.GetTypeRefName(method.ReturnType)); SourceWrite(" ret = "); } else { // Return immediately SourceWrite("return "); } SourceWrite(BulletParser.GetTypeMarshalConstructorStart(method)); } else { if (method.Property != null && method.Equals(method.Property.Getter)) { SourceWrite(BulletParser.GetTypeMarshalConstructorStart(method)); } } } // Native is defined as static_cast<className*>(_native) string nativePointer = (parentClass.BaseClass != null) ? "Native" : "_native"; if (method.Field != null) { if (method.Equals(method.Property.Getter)) { SourceWrite(nativePointer); SourceWrite("->"); SourceWrite(method.Field.Name); } var setter = method.Property.Setter; if (setter != null && method.Equals(setter)) { var param = method.Parameters[0]; var fieldSet = BulletParser.GetTypeMarshalFieldSetCppCli(method.Field, param, nativePointer); if (!string.IsNullOrEmpty(fieldSet)) { SourceWrite(fieldSet); } else { SourceWrite(string.Format("{0}->{1} = ", nativePointer, method.Field.Name)); if (param.Type.IsPointer || param.Type.IsReference) { if (param.Type.IsReference) { // Dereference SourceWrite('*'); } if (param.Type.Referenced.Target != null && param.Type.Referenced.Target.BaseClass != null) { // Cast native pointer from base class SourceWrite(string.Format("({0}*)", param.Type.Referenced.FullName)); } } SourceWrite(param.ManagedName); if (!param.Type.IsBasic) { SourceWrite("->_native"); } } } } else { if (!method.IsConstructor) { if (method.IsStatic) { SourceWrite(parentClass.FullName); SourceWrite("::"); } else { SourceWrite(nativePointer); SourceWrite("->"); } } OutputMethodMarshal(method, numParameters); } if (!method.IsConstructor && !method.IsVoid) { SourceWrite(BulletParser.GetTypeMarshalConstructorEnd(method)); } SourceWriteLine(';'); // Write type marshalling epilogue if (needTypeMarshalEpilogue) { for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; string epilogue = BulletParser.GetTypeMarshalEpilogueCppCli(param); if (!string.IsNullOrEmpty(epilogue)) { WriteTabs(1, true); SourceWriteLine(epilogue); } } if (!method.IsVoid) { WriteTabs(1, true); SourceWriteLine("return ret;"); } } } SourceWriteLine('}'); hasSourceWhiteSpace = false; // #endif // DISABLE_FEATURE if (hasConditional) { foreach (var param in method.Parameters) { string typeConditional = GetTypeConditional(param.Type, method.Parent.Header); if (typeConditional != null) { WriteLine("#endif"); hasHeaderWhiteSpace = true; } } } // If there are optional parameters, then output all possible combinations of calls if (hasOptionalParam) { OutputMethod(method, level, numOptionalParams + 1); } }
void OutputMethodMarshal(MethodDefinition method, int numParameters) { if (method.IsConstructor) { SourceWrite(method.Parent.FullName); } else { SourceWrite(method.Name); } SourceWrite('('); for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; string marshal = BulletParser.GetTypeMarshalCppCli(param); if (!string.IsNullOrEmpty(marshal)) { SourceWrite(marshal); } else if (param.Type.IsBasic) { SourceWrite(param.ManagedName); } else { if (param.Type.IsPointer || param.Type.IsReference) { if (param.Type.IsReference) { // Dereference SourceWrite('*'); } if (param.Type.Referenced.Target != null && param.Type.Referenced.Target.BaseClass != null) { // Cast native pointer from base class SourceWrite(string.Format("({0}*)", param.Type.Referenced.FullName)); } } SourceWrite(param.ManagedName); if (param.Type.IsPointer && param.Type.ManagedName.Equals("void")) { SourceWrite(".ToPointer()"); } else { SourceWrite("->_native"); } } // Any more parameters? if (i != numParameters - 1) { if (_sourceLineLength >= LineBreakWidth) { SourceWriteLine(","); WriteTabs(2, true); } else { SourceWrite(", "); } } } SourceWrite(')'); }
void WriteMethodDefinition(ManagedMethod method, int numParameters) { var nativeMethod = method.Native; var parentClass = method.Parent.Native; // Type marshalling prologue bool needTypeMarshalEpilogue = false; if (nativeMethod.Field == null) { foreach (var param in method.Parameters) { string prologue = BulletParser.GetTypeMarshalPrologueCppCli(param); if (!string.IsNullOrEmpty(prologue)) { WriteLine(1, prologue); } // Do we need a type marshalling epilogue? if (!needTypeMarshalEpilogue) { string epilogue = BulletParser.GetTypeMarshalEpilogueCppCli(param); if (!string.IsNullOrEmpty(epilogue)) { needTypeMarshalEpilogue = true; } } } } WriteTabs(1); if (nativeMethod.IsConstructor) { Write("_native = new "); } else if (!nativeMethod.IsVoid) { //if (method.ReturnType.IsBasic || method.ReturnType.Referenced != null) if (needTypeMarshalEpilogue) { // Return after epilogue (cleanup) Write($"{GetTypeName(nativeMethod.ReturnType)} ret = "); } else { // Return immediately Write("return "); } Write(BulletParser.GetTypeMarshalConstructorStart(nativeMethod)); } else if (method.Property != null && method.Equals(method.Property.Getter)) { Write(BulletParser.GetTypeMarshalConstructorStart(nativeMethod)); } // Native is defined as static_cast<className*>(_native) string nativePointer = (parentClass.BaseClass != null) ? "Native" : "_native"; if (nativeMethod.Field != null) { var property = method.Property; if (method.Equals(property.Getter)) { CachedProperty cachedProperty; if (method.Parent.CachedProperties.TryGetValue(property.Name, out cachedProperty)) { Write(cachedProperty.CacheFieldName); } else { Write($"{nativePointer}->{nativeMethod.Field.Name}"); } } else if (property.Setter != null && method.Equals(property.Setter)) { var param = method.Parameters[0]; var paramType = param.Native.Type; var fieldSet = BulletParser.GetTypeMarshalFieldSetCppCli(nativeMethod.Field, param, nativePointer); if (!string.IsNullOrEmpty(fieldSet)) { Write(fieldSet); } else { Write($"{nativePointer}->{nativeMethod.Field.Name} = "); switch (paramType.Kind) { case TypeKind.Pointer: case TypeKind.LValueReference: if (paramType.Kind == TypeKind.LValueReference) { // Dereference Write('*'); } if (paramType.Referenced.Target != null && paramType.Referenced.Target.BaseClass != null) { // Cast native pointer from base class Write($"({paramType.Referenced.FullName}*)"); } break; } Write(param.Name); if (!paramType.IsBasic) { Write("->_native"); } } } } else { if (nativeMethod.IsConstructor) { } else if (nativeMethod.IsStatic) { Write(parentClass.FullyQualifiedName + "::"); } else { Write(nativePointer + "->"); } To = WriteTo.Source; WriteMethodMarshal(method, numParameters); } if (!nativeMethod.IsConstructor && !nativeMethod.IsVoid) { Write(BulletParser.GetTypeMarshalConstructorEnd(nativeMethod)); } WriteLine(';'); // Write type marshalling epilogue if (needTypeMarshalEpilogue) { foreach (var param in method.Parameters) { string epilogue = BulletParser.GetTypeMarshalEpilogueCppCli(param); if (!string.IsNullOrEmpty(epilogue)) { WriteLine(1, epilogue); } } if (!nativeMethod.IsVoid) { WriteLine(1, "return ret;"); } } }
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; }
void OutputMethod(MethodDefinition method, int level, int numOptionalParams = 0) { var parentClass = method.Parent; // No whitespace between get/set methods if (!(method.Property != null && method.Equals(method.Property.Setter))) { EnsureSourceWhiteSpace(); hasHeaderWhiteSpace = false; } // #ifndef DISABLE_FEATURE bool hasConditional = false; if (method.Property == null) { foreach (var param in method.Parameters) { string typeConditional = GetTypeConditional(param.Type, parentClass.Header); if (typeConditional != null) { WriteLine(string.Format("#ifndef {0}", typeConditional)); hasSourceWhiteSpace = true; hasConditional = true; } } } WriteTabs(level + 1); // "static" if (method.IsStatic) { HeaderWrite("static "); } // Definition: return type if (!method.IsConstructor) { var returnType = method.ReturnType; if (method.Property != null) { if (method.Equals(method.Property.Getter)) { // If property name matches type name, resolve ambiguity if (method.Property.Name.Equals(method.Property.Type.ManagedName)) { HeaderWrite(NamespaceName + "::"); } // Getter with parameter for return value if (method.Parameters.Length == 1) { returnType = method.Parameters[0].Type; } } } Write(BulletParser.GetTypeRefName(returnType)); Write(' '); } // Definition: name string headerMethodName; string sourceMethodName; if (method.IsConstructor) { headerMethodName = parentClass.ManagedName; sourceMethodName = headerMethodName; } else if (method.Property != null) { headerMethodName = method.Property.Getter.Equals(method) ? "get" : "set"; sourceMethodName = string.Format("{0}::{1}", method.Property.Name, headerMethodName); } else { headerMethodName = method.ManagedName; sourceMethodName = headerMethodName; } HeaderWrite(string.Format("{0}(", headerMethodName)); SourceWrite(string.Format("{0}::{1}(", parentClass.FullNameManaged, sourceMethodName)); // Definition: parameters int numParameters = method.Parameters.Length - numOptionalParams; // Getters with parameter for return value if (numParameters == 1 && method.Property != null && method.Equals(method.Property.Getter)) { numParameters = 0; } bool hasOptionalParam = false; for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; Write(string.Format("{0} {1}", BulletParser.GetTypeRefName(param.Type), param.ManagedName)); if (param.IsOptional) { hasOptionalParam = true; } if (i != numParameters - 1) { if (_headerLineLength >= LineBreakWidth) { HeaderWriteLine(","); WriteTabs(level + 2); } else { HeaderWrite(", "); } if (_sourceLineLength >= LineBreakWidth) { SourceWriteLine(","); WriteTabs(1, true); } else { SourceWrite(", "); } } } HeaderWriteLine(");"); SourceWriteLine(')'); // Constructor chaining bool doConstructorChaining = false; if (method.IsConstructor && parentClass.BaseClass != null) { // If there is no need for marshalling code, we can chain constructors doConstructorChaining = method.Parameters.All(p => !BulletParser.TypeRequiresMarshal(p.Type)); WriteTabs(1, true); SourceWrite(string.Format(": {0}(", parentClass.BaseClass.ManagedName)); if (doConstructorChaining) { SourceWrite("new "); OutputMethodMarshal(method, numParameters); if (parentClass.BaseClass.HasPreventDelete) { SourceWrite(", false"); } } else { SourceWrite('0'); } SourceWriteLine(')'); } // Method definition SourceWriteLine('{'); if (!doConstructorChaining) { WriteMethodDefinition(method, numParameters); } // Cache property values if (method.IsConstructor) { var assignments = new List <string>(); var methodParent = method.Parent; while (methodParent != null) { foreach (var cachedProperty in methodParent.CachedProperties.OrderBy(p => p.Key)) { foreach (var param in method.Parameters) { if (param.ManagedName.ToLower() == cachedProperty.Key.ToLower() && param.Type.ManagedName == cachedProperty.Value.Property.Type.ManagedName) { string assignment = string.Format("\t{0} = {1};", cachedProperty.Value.CacheFieldName, param.ManagedName); assignments.Add(assignment); } } } methodParent = methodParent.BaseClass; } if (assignments.Count != 0) { EnsureSourceWhiteSpace(); foreach (string assignment in assignments) { SourceWriteLine(assignment); } hasSourceWhiteSpace = false; } } SourceWriteLine('}'); hasSourceWhiteSpace = false; // #endif // DISABLE_FEATURE if (hasConditional) { foreach (var param in method.Parameters) { string typeConditional = GetTypeConditional(param.Type, method.Parent.Header); if (typeConditional != null) { WriteLine("#endif"); hasHeaderWhiteSpace = true; } } } // If there are optional parameters, then output all possible combinations of calls if (hasOptionalParam) { OutputMethod(method, level, numOptionalParams + 1); } }
void WriteMethodDefinition(MethodDefinition method, int numParameters) { var parentClass = method.Parent; // Type marshalling prologue bool needTypeMarshalEpilogue = false; if (method.Field == null) { foreach (var param in method.Parameters) { string prologue = BulletParser.GetTypeMarshalPrologueCppCli(param); if (!string.IsNullOrEmpty(prologue)) { WriteTabs(1, true); SourceWriteLine(prologue); } // Do we need a type marshalling epilogue? if (!needTypeMarshalEpilogue) { string epilogue = BulletParser.GetTypeMarshalEpilogueCppCli(param); if (!string.IsNullOrEmpty(epilogue)) { needTypeMarshalEpilogue = true; } } } } WriteTabs(1, true); if (method.IsConstructor) { SourceWrite("_native = new "); } else if (!method.IsVoid) { //if (method.ReturnType.IsBasic || method.ReturnType.Referenced != null) if (needTypeMarshalEpilogue) { // Return after epilogue (cleanup) SourceWrite(string.Format("{0} ret = ", BulletParser.GetTypeRefName(method.ReturnType))); } else { // Return immediately SourceWrite("return "); } SourceWrite(BulletParser.GetTypeMarshalConstructorStart(method)); } else if (method.Property != null && method.Equals(method.Property.Getter)) { SourceWrite(BulletParser.GetTypeMarshalConstructorStart(method)); } // Native is defined as static_cast<className*>(_native) string nativePointer = (parentClass.BaseClass != null) ? "Native" : "_native"; if (method.Field != null) { var property = method.Property; if (method.Equals(property.Getter)) { CachedProperty cachedProperty; if (method.Parent.CachedProperties.TryGetValue(property.Name, out cachedProperty)) { SourceWrite(cachedProperty.CacheFieldName); } else { SourceWrite(string.Format("{0}->{1}", nativePointer, method.Field.Name)); } } else if (property.Setter != null && method.Equals(property.Setter)) { var param = method.Parameters[0]; var fieldSet = BulletParser.GetTypeMarshalFieldSetCppCli(method.Field, param, nativePointer); if (!string.IsNullOrEmpty(fieldSet)) { SourceWrite(fieldSet); } else { SourceWrite(string.Format("{0}->{1} = ", nativePointer, method.Field.Name)); if (param.Type.IsPointer || param.Type.IsReference) { if (param.Type.IsReference) { // Dereference SourceWrite('*'); } if (param.Type.Referenced.Target != null && param.Type.Referenced.Target.BaseClass != null) { // Cast native pointer from base class SourceWrite(string.Format("({0}*)", param.Type.Referenced.FullName)); } } SourceWrite(param.ManagedName); if (!param.Type.IsBasic) { SourceWrite("->_native"); } } } } else { if (method.IsConstructor) { } else if (method.IsStatic) { SourceWrite(parentClass.FullyQualifiedName + "::"); } else { SourceWrite(nativePointer + "->"); } OutputMethodMarshal(method, numParameters); } if (!method.IsConstructor && !method.IsVoid) { SourceWrite(BulletParser.GetTypeMarshalConstructorEnd(method)); } SourceWriteLine(';'); // Write type marshalling epilogue if (needTypeMarshalEpilogue) { foreach (var param in method.Parameters) { string epilogue = BulletParser.GetTypeMarshalEpilogueCppCli(param); if (!string.IsNullOrEmpty(epilogue)) { WriteTabs(1, true); SourceWriteLine(epilogue); } } if (!method.IsVoid) { WriteTabs(1, true); SourceWriteLine("return ret;"); } } }