void FindForwardReferences(List <ManagedClass> forwardRefs, ManagedClass @class) { var header = @class.Header.Native; foreach (var prop in @class.Properties) { AddForwardReference(forwardRefs, prop.Type, header); } var methods = @class.Methods.Where(m => m.Native.Access == AccessSpecifier.Public && !m.Native.IsExcluded); if (@class.Native.HidePublicConstructors) { methods = methods.Where(m => !m.Native.IsConstructor); } foreach (var method in methods) { AddForwardReference(forwardRefs, method.Native.ReturnType, header); foreach (var param in method.Parameters) { AddForwardReference(forwardRefs, param.Native.Type, header); } } foreach (var cl in @class.NestedClasses) { FindForwardReferences(forwardRefs, cl); } }
private static string GetFullNameManaged(ManagedClass @class) { if (@class.Parent != null) { return($"{GetFullNameManaged(@class.Parent)}.{@class.Name}"); } return(@class.Name); }
public ManagedMethod Copy(ManagedClass parent) { var m = new ManagedMethod(Native, parent, Name) { Property = Property }; //TODO: params return m; }
public ManagedMethod Copy(ManagedClass parent) { var m = new ManagedMethod(Native, parent, Name) { Property = Property }; //TODO: params return(m); }
bool ClassNeedsExtensions(ManagedClass c) { foreach (var prop in c.Properties) { if (_extensionClassesInternal.ContainsKey(GetName(prop.Type))) { return true; } } return c.Methods.Any(MethodNeedsExtensions); }
bool ClassNeedsExtensions(ManagedClass c) { foreach (var prop in c.Properties) { if (_extensionClassesInternal.ContainsKey(GetName(prop.Type))) { return(true); } } return(c.Methods.Any(MethodNeedsExtensions)); }
} // property that wraps this get/set method public ManagedMethod(MethodDefinition nativeMethod, ManagedClass parent, string name) { Native = nativeMethod; Parent = parent; Name = name; Parameters = nativeMethod.Parameters.Select(p => new ManagedParameter(p)).ToArray(); if (parent != null) { parent.Methods.Add(this); } }
private ManagedClass GetManagedClass(ClassDefinition @class) { if (@class == null) { return(null); } ManagedClass managedClass; if (Classes.TryGetValue(@class.FullyQualifiedName, out managedClass)) { return(managedClass); } if (@class.IsExcluded) { return(null); } string managedName = GetManagedClassName(@class); var parent = GetManagedClass(@class.Parent); managedClass = new ManagedClass(@class, managedName, parent); managedClass.BaseClass = GetManagedClass(@class.BaseClass); Classes[@class.FullyQualifiedName] = managedClass; // Set parent header or class managedClass.Header = GetManagedHeader(@class.Header); if (parent != null) { parent.NestedClasses.Add(managedClass); } else { managedClass.Header.Classes.Add(managedClass); } // Managed methods foreach (var method in @class.Methods) { managedName = GetManagedMethodName(method); var managedMethod = new ManagedMethod(method, managedClass, managedName); foreach (var param in managedMethod.Parameters) { param.Name = GetManagedParameterName(param.Native); } } return(managedClass); }
public ManagedMethod(MethodDefinition nativeMethod, ManagedClass parent, string name) { Native = nativeMethod; Parent = parent; Name = name; Parameters = nativeMethod.Parameters.Select(p => new ManagedParameter(p)).ToArray(); if (nativeMethod.OutValueParameter != null) { OutValueParameter = new ManagedParameter(nativeMethod.OutValueParameter); } if (parent != null) { parent.Methods.Add(this); } }
// Accepts a ClassDefinition for recursion void WriteEnumClass(ManagedClass @class, int level) { var @enum = @class.Native as EnumDefinition; if (@enum == null) { foreach (var childClass in @class.NestedClasses) { WriteEnumClass(childClass, level); } return; } EnsureWhiteSpace(); if (@enum.IsFlags) { WriteLine(level, "[Flags]"); } WriteLine(level, $"public enum {@class.Name}"); WriteLine(level, "{"); for (int i = 0; i < @enum.EnumConstants.Count; i++) { var constant = @enum.EnumConstants[i]; if (constant.Value.Equals("")) { Write(level + 1, constant.Constant); } else { Write(level + 1, $"{constant.Constant} = {constant.Value}"); } if (i < @enum.EnumConstants.Count - 1) { Write(','); } WriteLine(); } WriteLine(level, "}"); hasCSWhiteSpace = false; }
private ManagedClass GetManagedClass(ClassDefinition @class) { if (@class == null) return null; ManagedClass managedClass; if (Classes.TryGetValue(@class.FullyQualifiedName, out managedClass)) { return managedClass; } if (@class.IsExcluded) return null; string managedName = GetManagedClassName(@class); var parent = GetManagedClass(@class.Parent); managedClass = new ManagedClass(@class, managedName, parent); managedClass.BaseClass = GetManagedClass(@class.BaseClass); Classes[@class.FullyQualifiedName] = managedClass; // Set parent header or class managedClass.Header = GetManagedHeader(@class.Header); if (parent != null) { parent.NestedClasses.Add(managedClass); } else { managedClass.Header.Classes.Add(managedClass); } // Managed methods foreach (var method in @class.Methods) { managedName = GetManagedMethodName(method); var managedMethod = new ManagedMethod(method, managedClass, managedName); foreach (var param in managedMethod.Parameters) { param.Name = GetManagedParameterName(param.Native); } } return managedClass; }
void WriteClass(ManagedClass @class, int level) { var nativeClass = @class.Native; EnsureHeaderWhiteSpace(); EnsureSourceWhiteSpace(); var prevTo = To; To = WriteTo.Header; WriteTabs(level); // Access modifier if (level == 1) { Write("public "); } // Class definition Write($"ref class {@class.Name}"); // abstract/sealed keywords if (nativeClass.IsAbstract) { Write(" abstract"); } else if (nativeClass.IsStatic) { Write(" sealed"); } // Inheritance if (@class.BaseClass != null) { WriteLine($" : {@class.BaseClass.Name}"); } else if (nativeClass.IsTrackingDisposable) { WriteLine(" : ITrackingDisposable"); } else { WriteLine(); } // Class body start WriteLine(level, "{"); // Default access for ref class var currentAccess = RefAccessSpecifier.Private; // Nested classes if ([email protected](IsExcludedClass)) { WriteClasses(@class.NestedClasses, ref currentAccess, level); currentAccess = RefAccessSpecifier.Public; WriteLine(WriteTo.Source); } // Private constructor for classes that aren't instanced if (nativeClass.IsStatic) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteLine(level + 1, $"{@class.Name}() {{}}"); hasHeaderWhiteSpace = false; } // Downcast native pointer if any methods in a derived class use it if (@class.BaseClass != null && @class.Methods.Select(m => m.Native) .Any(m => !m.IsConstructor && !m.IsStatic && !m.IsExcluded && m.Access == AccessSpecifier.Public)) { EnsureSourceWhiteSpace(); WriteLine($"#define Native static_cast<{nativeClass.FullyQualifiedName}*>(_native)", WriteTo.Source); hasSourceWhiteSpace = false; } // Write the native pointer to the base class if (@class.BaseClass == null && !nativeClass.IsStatic) { if (@class.NestedClasses.Any(c => !IsExcludedClass(c))) { WriteLine(); } if (nativeClass.IsTrackingDisposable) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteLine(level + 1, "virtual event EventHandler^ OnDisposing;"); WriteLine(level + 1, "virtual event EventHandler^ OnDisposed;"); hasHeaderWhiteSpace = false; } EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal); Write(level + 1, nativeClass.FullyQualifiedName); WriteLine("* _native;"); hasHeaderWhiteSpace = false; } EnsureHeaderWhiteSpace(); EnsureSourceWhiteSpace(); // Private fields // _isDisposed flag if (nativeClass.IsTrackingDisposable) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteLine(level + 1, "bool _isDisposed;"); hasHeaderWhiteSpace = false; } // _preventDelete flag if (nativeClass.HasPreventDelete) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteLine(level + 1, "bool _preventDelete;"); hasHeaderWhiteSpace = false; } // Cached property fields foreach (var cachedProperty in @class.CachedProperties.OrderBy(p => p.Key)) { EnsureAccess(level, ref currentAccess, cachedProperty.Value.Access); string typename = GetTypeName(cachedProperty.Value.Property.Type); string fieldName = cachedProperty.Key; fieldName = char.ToLower(fieldName[0]) + fieldName.Substring(1); WriteLine(level + 1, $"{typename} _{fieldName};"); hasHeaderWhiteSpace = false; } // Constructors and destructors if (!nativeClass.IsStatic) { // Write unmanaged constructor // TODO: Write constructor from unmanaged pointer only if the class is ever instantiated in this way. if (!nativeClass.NoInternalConstructor) { // Declaration To = WriteTo.Header; EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal); WriteLine(level + 1, $"{@class.Name}({nativeClass.FullyQualifiedName}* native);"); // Definition To = WriteTo.Source; // TODO: use full name once class hierarchy flattening is implemented //WriteLine($"{@class.FullNameCppCli}::{@class.ManagedName}({@class.FullyQualifiedName}* native)"); WriteLine($"{@class.Name}::{@class.Name}({nativeClass.FullyQualifiedName}* native)"); if (@class.BaseClass != null) { WriteLine(1, $": {@class.BaseClass.Name}(native)"); } // Body WriteLine('{'); if (@class.BaseClass == null) { WriteLine(1, "_native = native;"); } WriteLine('}'); hasHeaderWhiteSpace = false; hasSourceWhiteSpace = false; } // Write destructor & finalizer if (@class.BaseClass == null) { To = WriteTo.Header; // ECMA-372 19.13.1: "The access-specifier of a destructor in a ref class is ignored." // ECMA-372 19.13.2: "The access-specifier of a finalizer in a ref class is ignored." WriteLine(level + 1, $"~{@class.Name}();"); WriteLine(level + 1, $"!{@class.Name}();"); hasHeaderWhiteSpace = false; To = WriteTo.Source; EnsureSourceWhiteSpace(); WriteLine($"{GetFullNameManaged(@class)}::~{@class.Name}()"); WriteLine('{'); WriteLine(1, $"this->!{@class.Name}();"); WriteLine('}'); WriteLine(); WriteLine($"{GetFullNameManaged(@class)}::!{@class.Name}()"); WriteLine('{'); if (nativeClass.IsTrackingDisposable) { WriteLine(1, "if (this->IsDisposed)"); WriteLine(2, "return;"); WriteLine(); WriteLine(1, "OnDisposing(this, nullptr);"); WriteLine(); } if (nativeClass.HasPreventDelete) { WriteLine(1, "if (!_preventDelete)"); WriteLine(1, "{"); WriteLine(2, "delete _native;"); WriteLine(1, "}"); } else { WriteLine(1, "delete _native;"); } if (nativeClass.IsTrackingDisposable) { WriteLine(1, "_isDisposed = true;"); WriteLine(); WriteLine(1, "OnDisposed(this, nullptr);"); } else { WriteLine(1, "_native = NULL;"); } WriteLine('}'); hasSourceWhiteSpace = false; } // Write public constructors if (!nativeClass.HidePublicConstructors && !nativeClass.IsAbstract) { var constructors = @class.Methods.Where(m => m.Native.IsConstructor && m.Native.Access == AccessSpecifier.Public); if (constructors.Any()) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); foreach (var constructor in constructors) { WriteMethod(constructor, level); } } } } // Write non-constructor methods var methods = @class.Methods .Where(m => !m.Native.IsConstructor && !m.Native.IsExcluded && m.Native.Access == AccessSpecifier.Public && m.Property == null); if (methods.Any()) { EnsureHeaderWhiteSpace(); foreach (var method in methods) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteMethod(method, level); } } // Write properties (includes unmanaged fields and getters/setters) To = WriteTo.Header; foreach (PropertyDefinition prop in @class.Properties) { string typeConditional = GetTypeConditional(prop.Type, @class.Header); if (typeConditional != null) { WriteLine($"#ifndef {typeConditional}", WriteTo.Header | WriteTo.Source); hasSourceWhiteSpace = true; } else { EnsureHeaderWhiteSpace(); } EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteLine(level + 1, $"property {GetTypeName(prop.Type)} {prop.Name}"); WriteLine(level + 1, "{"); // Getter/Setter WriteMethod(prop.Getter, level + 1); if (prop.Setter != null) { WriteMethod(prop.Setter, level + 1); } WriteLine(level + 1, "}"); if (typeConditional != null) { WriteLine("#endif", WriteTo.Header | WriteTo.Source); hasSourceWhiteSpace = false; } hasHeaderWhiteSpace = false; } WriteLine(level, "};"); hasHeaderWhiteSpace = false; To = prevTo; }
private void WriteClass(ManagedClass @class) { foreach (var child in @class.NestedClasses) { WriteClass(child); } if (!ClassNeedsExtensions(@class)) { return; } _extensionMethods.Clear(); To = WriteTo.CS; EnsureWhiteSpace(); Write(1, "[EditorBrowsable(EditorBrowsableState.Never)]"); WriteLine(1, $"public static class {@class.Name}Extensions"); WriteLine(1, "{"); To = WriteTo.Buffer; foreach (var prop in @class.Properties) { if (_extensionClassesInternal.ContainsKey(GetName(prop.Type))) { string typeName = _extensionClassesExternal[GetName(prop.Type)]; // Getter with out parameter ClearBuffer(); WriteLine(2, string.Format("public unsafe static void Get{0}(this {1} obj, out {2} value)", prop.Name, @class.Name, typeName)); WriteLine(2, "{"); WriteLine(3, $"fixed ({typeName}* valuePtr = &value)"); WriteLine(3, "{"); Write(4, $"*({_extensionClassesInternal[GetName(prop.Type)]}"); WriteLine(string.Format("*({0}*)valuePtr = obj.{1};", _extensionClassesInternal[GetName(prop.Type)], prop.Name)); WriteLine(3, "}"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair<string, string>("Get" + prop.Name, GetBufferString())); // Getter with return value ClearBuffer(); WriteLine(2, string.Format("public static {0} Get{1}(this {1} obj)", typeName, prop.Name, @class.Name)); WriteLine(2, "{"); WriteLine(3, $"{typeName} value;"); WriteLine(3, $"Get{prop.Name}(obj, out value)"); WriteLine(3, "return value;"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair<string, string>("Get" + prop.Name, GetBufferString())); if (prop.Setter == null) { continue; } // Setter with ref parameter ClearBuffer(); WriteLine(2, string.Format("public unsafe static void Set{0}(this {1} obj, ref {2} value)", prop.Name, @class.Name, typeName)); WriteLine(2, "{"); WriteLine(3, $"fixed ({typeName}* valuePtr = &value)"); WriteLine(3, "{"); WriteLine(4, string.Format("obj.{0} = *({1}*)valuePtr;", prop.Name, _extensionClassesInternal[GetName(prop.Type)])); WriteLine(3, "}"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair<string, string>("Set" + prop.Name, GetBufferString())); // Setter with non-ref parameter ClearBuffer(); WriteLine(2, string.Format("public static void Set{0}(this {1} obj, {2} value)", prop.Name, @class.Name, typeName)); WriteLine(2, "{"); WriteLine(3, $"Set{prop.Name}(obj, ref value);"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair<string, string>("Set" + prop.Name, GetBufferString())); } } foreach (var method in @class.Methods) { if (!MethodNeedsExtensions(method)) { continue; } WriteMethod(method); } foreach (var method in _extensionMethods.OrderBy(key => key.Key)) { EnsureWhiteSpace(WriteTo.CS); Write(method.Value, WriteTo.CS); hasCSWhiteSpace = false; } WriteLine(1, "}", WriteTo.CS); hasCSWhiteSpace = false; }
private static string GetFullNameManaged(ManagedClass @class) { if (@class.Parent != null) { return $"{GetFullNameManaged(@class.Parent)}.{@class.Name}"; } return @class.Name; }
void WriteClass(ManagedClass @class, int level) { var nativeClass = @class.Native; // Write class definition EnsureWhiteSpace(); Write(level, "public "); if (nativeClass.IsStatic) Write("static "); if (@class.Parent != null && @class.Parent.BaseClass != null) { if (@class.Parent.BaseClass.NestedClasses .Any(c => c != @class && c.Name.Equals(@class.Name))) { Write("new "); } } if (nativeClass.IsAbstract) Write("abstract "); Write($"class {@class.Name}"); if (@class.BaseClass != null) { WriteLine($" : {GetFullNameManaged(@class.BaseClass)}"); } else if (nativeClass.IsStatic) { WriteLine(); } else { WriteLine(" : IDisposable"); } WriteLine(level, "{"); hasCSWhiteSpace = true; // Write child classes foreach (var c in @class.NestedClasses .Where(c => !IsExcludedClass(c)) .OrderBy(GetFullNameManaged)) { WriteClass(c, level + 1); } // Write the native pointer to the base class if (@class.BaseClass == null && !nativeClass.IsStatic) { EnsureWhiteSpace(); WriteLine(level + 1, "internal IntPtr _native;"); if (nativeClass.HasPreventDelete) { WriteLine(level + 1, "bool _preventDelete;"); } hasCSWhiteSpace = false; } // Write cached property fields if (@class.CachedProperties.Any()) { EnsureWhiteSpace(); foreach (var cachedProperty in @class.CachedProperties.OrderBy(p => p.Key)) { string fieldName = cachedProperty.Key; fieldName = char.ToLower(fieldName[0]) + fieldName.Substring(1); WriteLine(level + 1, string.Format("{0} {1} _{2};", cachedProperty.Value.Access.ToString().ToLower(), GetTypeNameCS(cachedProperty.Value.Property.Type), fieldName)); } hasCSWhiteSpace = false; } // Write methods ClearBuffer(); // Write constructors if (!nativeClass.IsStatic) { // Write C# internal constructor if (!nativeClass.NoInternalConstructor) { EnsureWhiteSpace(); Write(level + 1, $"internal {@class.Name}(IntPtr native"); if (nativeClass.HasPreventDelete) { Write(", bool preventDelete"); } WriteLine(')'); if (@class.BaseClass != null) { Write(level + 2, ": base(native"); if (nativeClass.HasPreventDelete) { if ([email protected]) { // Base class should also have preventDelete //throw new NotImplementedException(); } Write(", preventDelete"); } else if (@class.BaseClass.Native.HasPreventDelete) { Write(", true"); } WriteLine(')'); } WriteLine(level + 1, "{"); if (@class.BaseClass == null) { WriteLine(level + 2, "_native = native;"); if (nativeClass.HasPreventDelete) { WriteLine(level + 2, "_preventDelete = preventDelete;"); } } WriteLine(level + 1, "}"); hasCSWhiteSpace = false; } // Write public constructors if (!nativeClass.HidePublicConstructors && !nativeClass.IsAbstract) { int overloadIndex = 0; var constructors = @class.Methods.Where(m => m.Native.IsConstructor && !m.Native.IsExcluded); if (constructors.Any()) { foreach (var constructor in constructors) { WriteMethod(constructor, level, ref overloadIndex); } } } } // Write methods var methods = @class.Methods.Where(m => !m.Native.IsConstructor && !m.Native.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 @class.Properties) { WriteProperty(prop, level); } // Write delete method // TODO: skip delete methods in classes that can't be constructed. /* if (@class.BaseClass == null && !nativeClass.IsStatic) { int overloadIndex = 0; var del = new MethodDefinition("delete", @class, 0); del.ReturnType = new TypeRefDefinition("void"); WriteMethod(del, level, ref overloadIndex); @class.Methods.Remove(del); } */ // Write DllImport clauses if (GetBufferString().Length != 0) { EnsureWhiteSpace(WriteTo.CS); Write(GetBufferString(), WriteTo.CS); } WriteLine(level, "}", WriteTo.CS); hasCSWhiteSpace = false; }
// Accepts a ClassDefinition for recursion void WriteEnumClass(ManagedClass @class, int level) { var @enum = @class.Native as EnumDefinition; if (@enum == null) { foreach (var childClass in @class.NestedClasses) { WriteEnumClass(childClass, level); } return; } EnsureWhiteSpace(); if (@enum.IsFlags) WriteLine(level, "[Flags]"); WriteLine(level, $"public enum {@class.Name}"); WriteLine(level, "{"); for (int i = 0; i < @enum.EnumConstants.Count; i++) { var constant = @enum.EnumConstants[i]; if (constant.Value.Equals("")) { Write(level + 1, constant.Constant); } else { Write(level + 1, $"{constant.Constant} = {constant.Value}"); } if (i < @enum.EnumConstants.Count - 1) { Write(','); } WriteLine(); } WriteLine(level, "}"); hasCSWhiteSpace = false; }
public ManagedClass(ClassDefinition nativeClass, string managedName, ManagedClass parent) { Native = nativeClass; Name = managedName; Parent = parent; }
void WriteClass(ManagedClass @class, int level) { var nativeClass = @class.Native; // Write class definition EnsureWhiteSpace(); Write(level, "public "); if (nativeClass.IsStatic) { Write("static "); } if (@class.Parent != null && @class.Parent.BaseClass != null) { if (@class.Parent.BaseClass.NestedClasses .Any(c => c != @class && c.Name.Equals(@class.Name))) { Write("new "); } } if (nativeClass.IsAbstract) { Write("abstract "); } Write($"class {@class.Name}"); if (@class.BaseClass != null) { WriteLine($" : {GetFullNameManaged(@class.BaseClass)}"); } else if (nativeClass.IsStatic) { WriteLine(); } else { WriteLine(" : IDisposable"); } WriteLine(level, "{"); hasCSWhiteSpace = true; // Write child classes foreach (var c in @class.NestedClasses .Where(c => !IsExcludedClass(c)) .OrderBy(GetFullNameManaged)) { WriteClass(c, level + 1); } // Write the native pointer to the base class if (@class.BaseClass == null && !nativeClass.IsStatic) { EnsureWhiteSpace(); WriteLine(level + 1, "internal IntPtr _native;"); if (nativeClass.HasPreventDelete) { WriteLine(level + 1, "bool _preventDelete;"); } hasCSWhiteSpace = false; } // Write cached property fields if (@class.CachedProperties.Any()) { EnsureWhiteSpace(); foreach (var cachedProperty in @class.CachedProperties.OrderBy(p => p.Key)) { string fieldName = cachedProperty.Key; fieldName = char.ToLower(fieldName[0]) + fieldName.Substring(1); WriteLine(level + 1, string.Format("{0} {1} _{2};", cachedProperty.Value.Access.ToString().ToLower(), GetTypeNameCS(cachedProperty.Value.Property.Type), fieldName)); } hasCSWhiteSpace = false; } // Write methods ClearBuffer(); // Write constructors if (!nativeClass.IsStatic) { // Write C# internal constructor if (!nativeClass.NoInternalConstructor) { EnsureWhiteSpace(); Write(level + 1, $"internal {@class.Name}(IntPtr native"); if (nativeClass.HasPreventDelete) { Write(", bool preventDelete"); } WriteLine(')'); if (@class.BaseClass != null) { Write(level + 2, ": base(native"); if (nativeClass.HasPreventDelete) { if ([email protected]) { // Base class should also have preventDelete //throw new NotImplementedException(); } Write(", preventDelete"); } else if (@class.BaseClass.Native.HasPreventDelete) { Write(", true"); } WriteLine(')'); } WriteLine(level + 1, "{"); if (@class.BaseClass == null) { WriteLine(level + 2, "_native = native;"); if (nativeClass.HasPreventDelete) { WriteLine(level + 2, "_preventDelete = preventDelete;"); } } WriteLine(level + 1, "}"); hasCSWhiteSpace = false; } // Write public constructors if (!nativeClass.HidePublicConstructors && !nativeClass.IsAbstract) { int overloadIndex = 0; var constructors = @class.Methods.Where(m => m.Native.IsConstructor && !m.Native.IsExcluded); if (constructors.Any()) { foreach (var constructor in constructors) { WriteMethod(constructor, level, ref overloadIndex); } } } } // Write methods var methods = @class.Methods.Where(m => !m.Native.IsConstructor && !m.Native.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 @class.Properties) { WriteProperty(prop, level); } // Write delete method // TODO: skip delete methods in classes that can't be constructed. /* * if (@class.BaseClass == null && !nativeClass.IsStatic) * { * int overloadIndex = 0; * var del = new MethodDefinition("delete", @class, 0); * del.ReturnType = new TypeRefDefinition("void"); * WriteMethod(del, level, ref overloadIndex); * @class.Methods.Remove(del); * } */ // Write DllImport clauses if (GetBufferString().Length != 0) { EnsureWhiteSpace(WriteTo.CS); Write(GetBufferString(), WriteTo.CS); } WriteLine(level, "}", WriteTo.CS); hasCSWhiteSpace = false; }
void FindForwardReferences(List<ManagedClass> forwardRefs, ManagedClass @class) { var header = @class.Header.Native; foreach (var prop in @class.Properties) { AddForwardReference(forwardRefs, prop.Type, header); } var methods = @class.Methods.Where(m => m.Native.Access == AccessSpecifier.Public && !m.Native.IsExcluded); if (@class.Native.HidePublicConstructors) methods = methods.Where(m => !m.Native.IsConstructor); foreach (var method in methods) { AddForwardReference(forwardRefs, method.Native.ReturnType, header); foreach (var param in method.Parameters) { AddForwardReference(forwardRefs, param.Native.Type, header); } } foreach (var cl in @class.NestedClasses) { FindForwardReferences(forwardRefs, cl); } }
void WriteClass(ManagedClass @class, int level) { var nativeClass = @class.Native; EnsureHeaderWhiteSpace(); EnsureSourceWhiteSpace(); var prevTo = To; To = WriteTo.Header; WriteTabs(level); // Access modifier if (level == 1) Write("public "); // Class definition Write($"ref class {@class.Name}"); // abstract/sealed keywords if (nativeClass.IsAbstract) Write(" abstract"); else if (nativeClass.IsStatic) Write(" sealed"); // Inheritance if (@class.BaseClass != null) { WriteLine($" : {@class.BaseClass.Name}"); } else if (nativeClass.IsTrackingDisposable) WriteLine(" : ITrackingDisposable"); else WriteLine(); // Class body start WriteLine(level, "{"); // Default access for ref class var currentAccess = RefAccessSpecifier.Private; // Nested classes if ([email protected](IsExcludedClass)) { WriteClasses(@class.NestedClasses, ref currentAccess, level); currentAccess = RefAccessSpecifier.Public; WriteLine(WriteTo.Source); } // Private constructor for classes that aren't instanced if (nativeClass.IsStatic) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteLine(level + 1, $"{@class.Name}() {{}}"); hasHeaderWhiteSpace = false; } // Downcast native pointer if any methods in a derived class use it if (@class.BaseClass != null && @class.Methods.Select(m => m.Native) .Any(m => !m.IsConstructor && !m.IsStatic && !m.IsExcluded && m.Access == AccessSpecifier.Public)) { EnsureSourceWhiteSpace(); WriteLine($"#define Native static_cast<{nativeClass.FullyQualifiedName}*>(_native)", WriteTo.Source); hasSourceWhiteSpace = false; } // Write the native pointer to the base class if (@class.BaseClass == null && !nativeClass.IsStatic) { if (@class.NestedClasses.Any(c => !IsExcludedClass(c))) { WriteLine(); } if (nativeClass.IsTrackingDisposable) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteLine(level + 1, "virtual event EventHandler^ OnDisposing;"); WriteLine(level + 1, "virtual event EventHandler^ OnDisposed;"); hasHeaderWhiteSpace = false; } EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal); Write(level + 1, nativeClass.FullyQualifiedName); WriteLine("* _native;"); hasHeaderWhiteSpace = false; } EnsureHeaderWhiteSpace(); EnsureSourceWhiteSpace(); // Private fields // _isDisposed flag if (nativeClass.IsTrackingDisposable) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteLine(level + 1, "bool _isDisposed;"); hasHeaderWhiteSpace = false; } // _preventDelete flag if (nativeClass.HasPreventDelete) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteLine(level + 1, "bool _preventDelete;"); hasHeaderWhiteSpace = false; } // Cached property fields foreach (var cachedProperty in @class.CachedProperties.OrderBy(p => p.Key)) { EnsureAccess(level, ref currentAccess, cachedProperty.Value.Access); string typename = GetTypeName(cachedProperty.Value.Property.Type); string fieldName = cachedProperty.Key; fieldName = char.ToLower(fieldName[0]) + fieldName.Substring(1); WriteLine(level + 1, $"{typename} _{fieldName};"); hasHeaderWhiteSpace = false; } // Constructors and destructors if (!nativeClass.IsStatic) { // Write unmanaged constructor // TODO: Write constructor from unmanaged pointer only if the class is ever instantiated in this way. if (!nativeClass.NoInternalConstructor) { // Declaration To = WriteTo.Header; EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal); WriteLine(level + 1, $"{@class.Name}({nativeClass.FullyQualifiedName}* native);"); // Definition To = WriteTo.Source; // TODO: use full name once class hierarchy flattening is implemented //WriteLine($"{@class.FullNameCppCli}::{@class.ManagedName}({@class.FullyQualifiedName}* native)"); WriteLine($"{@class.Name}::{@class.Name}({nativeClass.FullyQualifiedName}* native)"); if (@class.BaseClass != null) { WriteLine(1, $": {@class.BaseClass.Name}(native)"); } // Body WriteLine('{'); if (@class.BaseClass == null) WriteLine(1, "_native = native;"); WriteLine('}'); hasHeaderWhiteSpace = false; hasSourceWhiteSpace = false; } // Write destructor & finalizer if (@class.BaseClass == null) { To = WriteTo.Header; // ECMA-372 19.13.1: "The access-specifier of a destructor in a ref class is ignored." // ECMA-372 19.13.2: "The access-specifier of a finalizer in a ref class is ignored." WriteLine(level + 1, $"~{@class.Name}();"); WriteLine(level + 1, $"!{@class.Name}();"); hasHeaderWhiteSpace = false; To = WriteTo.Source; EnsureSourceWhiteSpace(); WriteLine($"{GetFullNameManaged(@class)}::~{@class.Name}()"); WriteLine('{'); WriteLine(1, $"this->!{@class.Name}();"); WriteLine('}'); WriteLine(); WriteLine($"{GetFullNameManaged(@class)}::!{@class.Name}()"); WriteLine('{'); if (nativeClass.IsTrackingDisposable) { WriteLine(1, "if (this->IsDisposed)"); WriteLine(2, "return;"); WriteLine(); WriteLine(1, "OnDisposing(this, nullptr);"); WriteLine(); } if (nativeClass.HasPreventDelete) { WriteLine(1, "if (!_preventDelete)"); WriteLine(1, "{"); WriteLine(2, "delete _native;"); WriteLine(1, "}"); } else { WriteLine(1, "delete _native;"); } if (nativeClass.IsTrackingDisposable) { WriteLine(1, "_isDisposed = true;"); WriteLine(); WriteLine(1, "OnDisposed(this, nullptr);"); } else { WriteLine(1, "_native = NULL;"); } WriteLine('}'); hasSourceWhiteSpace = false; } // Write public constructors if (!nativeClass.HidePublicConstructors && !nativeClass.IsAbstract) { var constructors = @class.Methods.Where(m => m.Native.IsConstructor && m.Native.Access == AccessSpecifier.Public); if (constructors.Any()) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); foreach (var constructor in constructors) { WriteMethod(constructor, level); } } } } // Write non-constructor methods var methods = @class.Methods .Where(m => !m.Native.IsConstructor && !m.Native.IsExcluded && m.Native.Access == AccessSpecifier.Public && m.Property == null); if (methods.Any()) { EnsureHeaderWhiteSpace(); foreach (var method in methods) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteMethod(method, level); } } // Write properties (includes unmanaged fields and getters/setters) To = WriteTo.Header; foreach (ManagedProperty prop in @class.Properties) { string typeConditional = GetTypeConditional(prop.Type, @class.Header); if (typeConditional != null) { WriteLine($"#ifndef {typeConditional}", WriteTo.Header | WriteTo.Source); hasSourceWhiteSpace = true; } else { EnsureHeaderWhiteSpace(); } EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteLine(level + 1, $"property {GetTypeName(prop.Type)} {prop.Name}"); WriteLine(level + 1, "{"); // Getter/Setter WriteMethod(prop.Getter, level + 1); if (prop.Setter != null) { WriteMethod(prop.Setter, level + 1); } WriteLine(level + 1, "}"); if (typeConditional != null) { WriteLine("#endif", WriteTo.Header | WriteTo.Source); hasSourceWhiteSpace = false; } hasHeaderWhiteSpace = false; } WriteLine(level, "};"); hasHeaderWhiteSpace = false; To = prevTo; }
private void WriteClass(ManagedClass @class) { foreach (var child in @class.NestedClasses) { WriteClass(child); } if (!ClassNeedsExtensions(@class)) { return; } _extensionMethods.Clear(); To = WriteTo.CS; EnsureWhiteSpace(); Write(1, "[EditorBrowsable(EditorBrowsableState.Never)]"); WriteLine(1, $"public static class {@class.Name}Extensions"); WriteLine(1, "{"); To = WriteTo.Buffer; foreach (var prop in @class.Properties) { if (_extensionClassesInternal.ContainsKey(GetName(prop.Type))) { string typeName = _extensionClassesExternal[GetName(prop.Type)]; // Getter with out parameter ClearBuffer(); WriteLine(2, string.Format("public unsafe static void Get{0}(this {1} obj, out {2} value)", prop.Name, @class.Name, typeName)); WriteLine(2, "{"); WriteLine(3, $"fixed ({typeName}* valuePtr = &value)"); WriteLine(3, "{"); Write(4, $"*({_extensionClassesInternal[GetName(prop.Type)]}"); WriteLine(string.Format("*({0}*)valuePtr = obj.{1};", _extensionClassesInternal[GetName(prop.Type)], prop.Name)); WriteLine(3, "}"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair <string, string>("Get" + prop.Name, GetBufferString())); // Getter with return value ClearBuffer(); WriteLine(2, string.Format("public static {0} Get{1}(this {1} obj)", typeName, prop.Name, @class.Name)); WriteLine(2, "{"); WriteLine(3, $"{typeName} value;"); WriteLine(3, $"Get{prop.Name}(obj, out value)"); WriteLine(3, "return value;"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair <string, string>("Get" + prop.Name, GetBufferString())); if (prop.Setter == null) { continue; } // Setter with ref parameter ClearBuffer(); WriteLine(2, string.Format("public unsafe static void Set{0}(this {1} obj, ref {2} value)", prop.Name, @class.Name, typeName)); WriteLine(2, "{"); WriteLine(3, $"fixed ({typeName}* valuePtr = &value)"); WriteLine(3, "{"); WriteLine(4, string.Format("obj.{0} = *({1}*)valuePtr;", prop.Name, _extensionClassesInternal[GetName(prop.Type)])); WriteLine(3, "}"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair <string, string>("Set" + prop.Name, GetBufferString())); // Setter with non-ref parameter ClearBuffer(); WriteLine(2, string.Format("public static void Set{0}(this {1} obj, {2} value)", prop.Name, @class.Name, typeName)); WriteLine(2, "{"); WriteLine(3, $"Set{prop.Name}(obj, ref value);"); WriteLine(2, "}"); _extensionMethods.Add(new KeyValuePair <string, string>("Set" + prop.Name, GetBufferString())); } } foreach (var method in @class.Methods) { if (!MethodNeedsExtensions(method)) { continue; } WriteMethod(method); } foreach (var method in _extensionMethods.OrderBy(key => key.Key)) { EnsureWhiteSpace(WriteTo.CS); Write(method.Value, WriteTo.CS); hasCSWhiteSpace = false; } WriteLine(1, "}", WriteTo.CS); hasCSWhiteSpace = false; }