// Property from getter method public PropertyDefinition(MethodDefinition getter) { Getter = getter; Parent = getter.Parent; Parent.Properties.Add(this); getter.Property = this; string name = getter.Name; // one_two_three -> oneTwoThree while (name.Contains("_")) { int pos = name.IndexOf('_'); name = name.Substring(0, pos) + name.Substring(pos + 1, 1).ToUpper() + name.Substring(pos + 2); } if (name.StartsWith("get", StringComparison.InvariantCultureIgnoreCase)) { Name = name.Substring(3); } else if (name.StartsWith("is", StringComparison.InvariantCultureIgnoreCase)) { Name = name[0].ToString().ToUpper() + name.Substring(1); } else if (name.StartsWith("has", StringComparison.InvariantCultureIgnoreCase)) { Name = name[0].ToString().ToUpper() + name.Substring(1); } else { throw new NotImplementedException(); } }
private void WriteMethodDeclaration(MethodDefinition method, int numParameters, int level, int overloadIndex) { Write(1, "EXPORT ", WriteTo.Header); // Return type if (method.IsConstructor) { Write($"{GetFullNameC(method.Parent)}* ", WriteTo.Header | WriteTo.Source); } else if (method.OutValueParameter != null) { Write("void ", WriteTo.Header | WriteTo.Source); } else { Write($"{GetTypeNameC(method.ReturnType)} ", WriteTo.Header | WriteTo.Source); } // Name string methodName = method.IsConstructor ? "new" : method.Name; if (overloadIndex != 0) { methodName += (overloadIndex + 1).ToString(); } Write($"{GetFullNameC(method.Parent)}_{methodName}(", WriteTo.Header | WriteTo.Source); // Parameters var parameters = method.Parameters.Take(numParameters); if (method.OutValueParameter != null) parameters = parameters.Concat(new[] { method.OutValueParameter }); var parametersCpp = parameters.Select(p => $"{GetTypeNameC(p.Type)} {p.Name}"); // The first parameter is the instance pointer (if not constructor or static method) if (!method.IsConstructor && !method.IsStatic) { parametersCpp = new[] { $"{GetFullNameC(method.Parent)}* obj" }.Concat(parametersCpp); } WriteLine($"{string.Join(", ", parametersCpp)});", WriteTo.Header); WriteLine($"{ListToLines(parametersCpp, WriteTo.Source)})", WriteTo.Source); }
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); } }
bool MethodNeedsExtensions(MethodDefinition method) { // Extension constructors & static extension methods not supported if (method.IsConstructor || method.IsStatic) { return false; } foreach (var param in method.Parameters) { if (_extensionClassesInternal.ContainsKey(param.Type.ManagedName)) { return true; } } return false; }
public MethodDefinition Copy(ClassDefinition parent = null) { var m = new MethodDefinition(Name, parent ?? Parent, Parameters.Length) { Access = Access, Field = Field, IsAbstract = IsAbstract, IsConstructor = IsConstructor, IsExcluded = IsExcluded, IsStatic = IsStatic, OutValueParameter = OutValueParameter?.Copy(), ReturnType = ReturnType.Copy() }; for (int i = 0; i < Parameters.Length; i++) { m.Parameters[i] = Parameters[i].Copy(); } return m; }
void OutputClass(ClassDefinition c, int level) { EnsureHeaderWhiteSpace(); EnsureSourceWhiteSpace(); // Write access modifier WriteTabs(level); if (level == 1) { HeaderWrite("public "); } // Write class definition HeaderWrite("ref class "); HeaderWrite(c.ManagedName); if (c.IsAbstract) { HeaderWrite(" abstract"); } if (c.BaseClass != null) { HeaderWrite(" : "); HeaderWriteLine(c.BaseClass.ManagedName); } else { if (c.IsTrackingDisposable) { HeaderWriteLine(" : ITrackingDisposable"); } else { // In C++/CLI, IDisposable is inherited automatically if the destructor and finalizer are defined //HeaderWriteLine(" : IDisposable"); if (c.IsStaticClass) { HeaderWrite(" sealed"); } HeaderWriteLine(); } } WriteTabs(level); HeaderWriteLine("{"); //hasHeaderWhiteSpace = false; // Default access for ref class var currentAccess = RefAccessSpecifier.Private; // Write child classes if (c.Classes.Count != 0) { OutputClasses(c.Classes, ref currentAccess, level); currentAccess = RefAccessSpecifier.Public; SourceWriteLine(); } // Classes without instances if (c.IsStaticClass) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteTabs(level + 1); HeaderWriteLine(string.Format("{0}() {{}}", c.ManagedName)); } // Downcast native pointer if any methods in a derived class use it if (c.BaseClass != null && c.Methods.Any(m => !m.IsConstructor && !m.IsStatic)) { EnsureSourceWhiteSpace(); SourceWriteLine(string.Format("#define Native static_cast<{0}*>(_native)", c.FullName)); hasSourceWhiteSpace = false; } // Write the unmanaged pointer to the base class if (c.BaseClass == null && !c.IsStaticClass) { if (c.Classes.Count != 0) { HeaderWriteLine(); } if (c.IsTrackingDisposable) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteTabs(level + 1); HeaderWriteLine("virtual event EventHandler^ OnDisposing;"); WriteTabs(level + 1); HeaderWriteLine("virtual event EventHandler^ OnDisposed;"); hasHeaderWhiteSpace = false; } EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal); WriteTabs(level + 1); HeaderWrite(c.FullName); HeaderWriteLine("* _native;"); hasHeaderWhiteSpace = false; } EnsureHeaderWhiteSpace(); EnsureSourceWhiteSpace(); // Private fields // _isDisposed flag if (c.IsTrackingDisposable) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteTabs(level + 1); HeaderWriteLine("bool _isDisposed;"); hasHeaderWhiteSpace = false; } // _preventDelete flag if (c.HasPreventDelete) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Private); WriteTabs(level + 1); HeaderWriteLine("bool _preventDelete;"); hasHeaderWhiteSpace = false; } // Write unmanaged constructor // TODO: Write constructor from unmanaged pointer only if the class is ever instantiated in this way. if (!c.NoInternalConstructor && !c.IsStaticClass) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Internal); WriteTabs(level + 1); SourceWrite(c.FullNameManaged); SourceWrite("::"); Write(c.ManagedName); Write('('); Write(c.FullName); Write("* native)"); HeaderWriteLine(';'); SourceWriteLine(); if (c.BaseClass != null) { WriteTabs(1, true); SourceWrite(": "); SourceWrite(c.BaseClass.ManagedName); SourceWriteLine("(native)"); } SourceWriteLine('{'); if (c.BaseClass == null) { WriteTabs(1, true); SourceWriteLine("_native = native;"); } SourceWriteLine('}'); hasHeaderWhiteSpace = false; hasSourceWhiteSpace = false; } // Write destructor & finalizer if (c.BaseClass == null && !c.IsStaticClass) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); WriteTabs(level + 1); HeaderWriteLine(string.Format("!{0}();", c.ManagedName)); EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Protected); WriteTabs(level + 1); HeaderWriteLine(string.Format("~{0}();", c.ManagedName)); hasHeaderWhiteSpace = false; EnsureSourceWhiteSpace(); SourceWriteLine(string.Format("{0}::~{1}()", c.FullNameManaged, c.ManagedName)); SourceWriteLine('{'); SourceWriteLine(string.Format("\tthis->!{0}();", c.ManagedName)); SourceWriteLine('}'); SourceWriteLine(); SourceWriteLine(string.Format("{0}::!{1}()", c.FullNameManaged, c.ManagedName)); SourceWriteLine('{'); if (c.IsTrackingDisposable) { SourceWriteLine("\tif (this->IsDisposed)"); SourceWriteLine("\t\treturn;"); SourceWriteLine(); SourceWriteLine("\tOnDisposing(this, nullptr);"); SourceWriteLine(); } if (c.HasPreventDelete) { SourceWriteLine("\tif (!_preventDelete)"); SourceWriteLine("\t{"); SourceWriteLine("\t\tdelete _native;"); SourceWriteLine("\t}"); } else { SourceWriteLine("\tdelete _native;"); } if (c.IsTrackingDisposable) { SourceWriteLine("\t_isDisposed = true;"); SourceWriteLine(); SourceWriteLine("\tOnDisposed(this, nullptr);"); } else { SourceWriteLine("\t_native = NULL;"); } SourceWriteLine('}'); hasSourceWhiteSpace = false; } // Write constructors int constructorCount = 0; foreach (MethodDefinition method in c.Methods.Where(m => m.IsConstructor)) { if (!c.HidePublicConstructors) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); OutputMethod(method, level); } constructorCount++; } // Write default constructor if (constructorCount == 0 && !c.IsAbstract && !c.HidePublicConstructors && !c.IsStaticClass) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); MethodDefinition constructor = new MethodDefinition(c.Name, c, 0); constructor.IsConstructor = true; OutputMethod(constructor, level); constructorCount++; } // Write methods if (c.Methods.Count - constructorCount != 0) { EnsureHeaderWhiteSpace(); foreach (MethodDefinition method in c.Methods) { if (!method.IsConstructor && method.Property == null) { EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); OutputMethod(method, level); } } } // Write properties (includes unmanaged fields and getters/setters) foreach (PropertyDefinition prop in c.Properties) { string typeConditional = GetTypeConditional(prop.Type, c.Header); if (typeConditional != null) { Write("#ifndef "); WriteLine(typeConditional); hasSourceWhiteSpace = true; } else { EnsureHeaderWhiteSpace(); } EnsureAccess(level, ref currentAccess, RefAccessSpecifier.Public); string typeRefName = BulletParser.GetTypeRefName(prop.Type); WriteTabs(level + 1); HeaderWrite("property "); HeaderWrite(typeRefName); HeaderWrite(" "); HeaderWriteLine(prop.Name); WriteTabs(level + 1); HeaderWriteLine("{"); // Getter/Setter OutputMethod(prop.Getter, level + 1); if (prop.Setter != null) { OutputMethod(prop.Setter, level + 1); } WriteTabs(level + 1); HeaderWriteLine("}"); if (typeConditional != null) { WriteLine("#endif"); hasSourceWhiteSpace = false; } hasHeaderWhiteSpace = false; } WriteTabs(level); HeaderWriteLine("};"); hasHeaderWhiteSpace = false; }
public MethodDefinition SpecializeTemplateMethod(ClassDefinition thisClass, MethodDefinition templateMethod, IDictionary<string, string> templateParams) { var methodSpec = templateMethod.Copy(thisClass); // Replace template parameters with concrete types methodSpec.ReturnType = CopyTypeTemplated(methodSpec.ReturnType, templateParams); foreach (var param in methodSpec.Parameters) { param.Type = CopyTypeTemplated(param.Type, templateParams); } return methodSpec; }
void WriteMethod(MethodDefinition method, int level, ref int overloadIndex, int numOptionalParams = 0, MethodDefinition returnParamMethod = null) { // Can't return whole structures, so append an output parameter // referencing the struct that will hold the return value. // convert "v method(param)" to "void method(param, &v)" if (!method.IsConstructor && !method.ReturnType.IsBasic && !method.ReturnType.IsPointer && BulletParser.MarshalStructByValue(method.ReturnType)) { var method2 = method.Copy(); var paras = method2.Parameters; Array.Resize<ParameterDefinition>(ref paras, paras.Length + 1); string paramName; if (method.Property != null && method.Property.Setter != null) { // Borrow out parameter name from setter paramName = method.Property.Setter.Parameters[0].Name; } else { paramName = "value"; } var valueType = new TypeRefDefinition() { IsBasic = false, IsPointer = true, Referenced = method2.ReturnType }; paras[paras.Length - 1] = new ParameterDefinition(paramName, valueType); paras[paras.Length - 1].ManagedName = paramName; method2.Parameters = paras; method2.ReturnType = new TypeRefDefinition(); WriteMethod(method2, level, ref overloadIndex, numOptionalParams, method); return; } EnsureWhiteSpace(WriteTo.Source | WriteTo.CS); // Skip methods wrapped by C# properties WriteTo propertyTo = WriteTo.Header | WriteTo.Source | ((method.Property == null) ? WriteTo.CS : 0); int numOptionalParamsTotal = method.NumOptionalParameters; int numParameters = method.Parameters.Length - numOptionalParamsTotal + numOptionalParams; WriteMethodDeclaration(method, numParameters, level, overloadIndex, returnParamMethod); if (method.Name.Equals("delete")) { WriteLine('{', WriteTo.Source); WriteTabs(1, WriteTo.Source); WriteLine("delete obj;", WriteTo.Source); WriteLine('}', WriteTo.Source); hasSourceWhiteSpace = false; hasCSWhiteSpace = false; return; } // Constructor base call if (method.IsConstructor && method.Parent.BaseClass != null) { WriteTabs(level + 2, WriteTo.CS & propertyTo); Write(": base(", WriteTo.CS & propertyTo); } else { WriteTabs(level + 1, WriteTo.CS & propertyTo); WriteLine('{', WriteTo.CS & propertyTo); WriteTabs(level + 2, WriteTo.CS & propertyTo); } // Method body WriteLine('{', WriteTo.Source); WriteMethodDefinition(method, numParameters, overloadIndex, level, returnParamMethod); WriteTabs(level + 1, WriteTo.CS & propertyTo); WriteLine('}', WriteTo.Source | WriteTo.CS & propertyTo); hasSourceWhiteSpace = false; if (method.Property == null) { hasCSWhiteSpace = false; } // If there are optional parameters, then output all possible combinations of calls overloadIndex++; if (numOptionalParams < numOptionalParamsTotal) { WriteMethod(method, level, ref overloadIndex, numOptionalParams + 1, returnParamMethod); } }
void WriteClass(ClassDefinition c, int level) { if (c.IsExcluded || c.IsTypedef || c.IsPureEnum) { return; } if (wrapperHeaderGuards.ContainsKey(c.Name)) { WriteClassWrapper(c); } EnsureWhiteSpace(WriteTo.Source | WriteTo.CS); // Write class definition WriteTabs(level, WriteTo.CS); Write("public class ", WriteTo.CS); Write(c.ManagedName, WriteTo.CS); if (c.BaseClass != null) { Write(" : ", WriteTo.CS); WriteLine(c.BaseClass.Target.FullNameManaged.Replace("::", "."), WriteTo.CS); //WriteLine(c.BaseClass.ManagedNameCS, WriteTo.CS); } else { WriteLine(" : IDisposable", WriteTo.CS); } WriteTabs(level, WriteTo.CS); WriteLine("{", WriteTo.CS); hasCSWhiteSpace = true; // Write child classes foreach (ClassDefinition cl in c.Classes.OrderBy(x => x.FullNameManaged)) { WriteClass(cl, level + 1); } if (!hasClassSeparatingWhitespace) { WriteLine(WriteTo.Header | WriteTo.Source); hasClassSeparatingWhitespace = true; } // Write native pointer if (c.BaseClass == null) { EnsureWhiteSpace(WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine("internal IntPtr _native;", WriteTo.CS); if (c.HasPreventDelete) { WriteTabs(level + 1, WriteTo.CS); WriteLine("bool _preventDelete;", WriteTo.CS); hasCSWhiteSpace = false; } hasCSWhiteSpace = false; } // Write methods int overloadIndex = 0; bufferBuilder.Clear(); // Write C# internal constructor if (!c.NoInternalConstructor) { EnsureWhiteSpace(WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); Write("internal ", WriteTo.CS); Write(c.ManagedName, WriteTo.CS); Write("(IntPtr native", WriteTo.CS); if (c.HasPreventDelete) { Write(", bool preventDelete", WriteTo.CS); } WriteLine(')', WriteTo.CS); if (c.BaseClass != null) { WriteTabs(level + 2, WriteTo.CS); Write(": base(native", WriteTo.CS); if (c.HasPreventDelete) { if (!c.BaseClass.Target.HasPreventDelete) { // Base class should also have preventDelete //throw new NotImplementedException(); } Write(", preventDelete", WriteTo.CS); } else { if (c.BaseClass.Target.HasPreventDelete) { Write(", true", WriteTo.CS); } } WriteLine(')', WriteTo.CS); } WriteTabs(level + 1, WriteTo.CS); WriteLine('{', WriteTo.CS); if (c.BaseClass == null) { WriteTabs(level + 2, WriteTo.CS); WriteLine("_native = native;", WriteTo.CS); if (c.HasPreventDelete) { WriteTabs(level + 2, WriteTo.CS); WriteLine("_preventDelete = preventDelete;", WriteTo.CS); } } WriteTabs(level + 1, WriteTo.CS); WriteLine('}', WriteTo.CS); hasCSWhiteSpace = false; } // Write constructors bool hasConstructors = false; if (!c.IsAbstract) { foreach (MethodDefinition method in c.Methods.Where(m => m.IsConstructor)) { if (!c.HidePublicConstructors) { WriteMethod(method, level, ref overloadIndex); } hasConstructors = true; } // Write default constructor if (!hasConstructors && !c.IsAbstract && !c.HidePublicConstructors) { var constructor = new MethodDefinition(c.Name, c, 0); constructor.IsConstructor = true; WriteMethod(constructor, level, ref overloadIndex); } overloadIndex = 0; } // Write methods MethodDefinition previousMethod = null; foreach (MethodDefinition method in c.Methods.Where(m => !m.IsConstructor).OrderBy(m => m.Name)) { if (previousMethod != null && previousMethod.Name != method.Name) { overloadIndex = 0; } WriteMethod(method, level, ref overloadIndex); previousMethod = method; } overloadIndex = 0; // Write properties foreach (PropertyDefinition prop in c.Properties) { WriteProperty(prop, level); } // Write delete method if (c.BaseClass == null) { var del = new MethodDefinition("delete", c, 0); del.ReturnType = new TypeRefDefinition(); WriteMethod(del, level, ref overloadIndex); c.Methods.Remove(del); overloadIndex = 0; } // Write DllImport clauses if (bufferBuilder.Length != 0) { EnsureWhiteSpace(WriteTo.CS); Write(bufferBuilder.ToString(), WriteTo.CS); } WriteTabs(level, WriteTo.CS); WriteLine("}", WriteTo.CS); hasCSWhiteSpace = false; hasClassSeparatingWhitespace = false; }
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($"{BulletParser.GetTypeRefName(method.ReturnType)} 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) { 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($"{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($"({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;"); } } }
public static string GetTypeMarshalConstructorStartCS(MethodDefinition method) { switch (method.ReturnType.Name) { case "btBroadphaseProxy": return "BroadphaseProxy.GetManaged("; case "btCollisionObject": return "CollisionObject.GetManaged("; case "btCollisionShape": return "CollisionShape.GetManaged("; case "btOverlappingPairCache": return "OverlappingPairCache.GetManaged("; case "IDebugDraw": return "DebugDraw.GetManaged("; case "btCollisionObjectWrapper": return $"new CollisionObjectWrapper("; default: return ""; } }
public static string GetTypeMarshalConstructorEnd(MethodDefinition getter) { switch (getter.ReturnType.Name) { case "btCollisionShape": case "btIDebugDraw": case "btOverlappingPairCache": case "btQuaternion": case "btTransform": case "btVector4": return ")"; default: return string.Empty; } }
public static string GetTypeMarshalConstructorStart(MethodDefinition getter) { switch (getter.ReturnType.Name) { case "btCollisionShape": return "CollisionShape::GetManaged("; case "btIDebugDraw": return "DebugDraw::GetManaged("; case "btOverlappingPairCache": return "OverlappingPairCache::GetManaged("; case "btQuaternion": return "Math::BtQuatToQuaternion(&"; case "btTransform": return "Math::BtTransformToMatrix(&"; case "btVector4": return "Math::BtVector4ToVector4(&"; default: return string.Empty; } }
private void CreateFieldSetter(FieldDefinition field, ClassDefinition @class, string setterName) { var type = field.Type; var typeCanonical = type.Canonical; // Can't assign value to reference or constant array switch (typeCanonical.Kind) { case TypeKind.LValueReference: case TypeKind.ConstantArray: return; case TypeKind.Record: if (typeCanonical.Target == null) return; if (!typeCanonical.Target.MarshalAsStruct) return; type = new TypeRefDefinition { IsConst = true, Kind = TypeKind.Pointer, Referenced = type.Copy() }; break; default: type = type.Copy(); break; } var setter = new MethodDefinition(setterName, @class, 1) { Field = field, ReturnType = new TypeRefDefinition("void") }; setter.Parameters[0] = new ParameterDefinition("value", type) { MarshalDirection = MarshalDirection.In }; field.Setter = setter; }
private void CreateFieldGetter(FieldDefinition field, ClassDefinition @class, string getterName, MethodDefinition setter) { MethodDefinition getter; // Use getter with an out parameter for structs if (field.Type.Target != null && field.Type.Target.MarshalAsStruct) { getter = new MethodDefinition(getterName, @class, 1); getter.ReturnType = new TypeRefDefinition("void"); string paramName = setter != null ? setter.Parameters[0].Name : "value"; var paramType = new TypeRefDefinition(field.Type.Name) { Kind = TypeKind.Pointer, Referenced = field.Type.Copy() }; getter.Parameters[0] = new ParameterDefinition(paramName, paramType) { MarshalDirection = MarshalDirection.Out }; } else { TypeRefDefinition type; if (field.Type.Canonical.Kind == TypeKind.Record) { type = new TypeRefDefinition() { Kind = TypeKind.Pointer, Referenced = field.Type.Copy() }; } else if (field.Type.Canonical.Kind == TypeKind.Unexposed) { // TODO: type = field.Type; } else { type = field.Type; } getter = new MethodDefinition(getterName, @class, 0); getter.ReturnType = type; } getter.Field = field; field.Getter = getter; }
private void CreateDestructors() { foreach (var @class in Project.ClassDefinitions.Values) { if (@class.BaseClass != null) continue; if (@class.NoInternalConstructor && @class.HidePublicConstructors) continue; if (@class.IsStatic) continue; if (@class is EnumDefinition) continue; if (@class.IsPureEnum) continue; var overloadIndex = @class.Methods.Count(m => m.Name.Equals("delete")); string name = overloadIndex == 0 ? "delete" : $"delete{overloadIndex + 1}"; var deleteMethod = new MethodDefinition(name, @class, 0) { IsDestructor = true, ReturnType = new TypeRefDefinition("void") }; } }
// Create default constructor if no explicit C++ constructor exists. void CreateDefaultConstructors() { foreach (var @class in Project.ClassDefinitions.Values) { if (@class.HidePublicConstructors) continue; if (@class.IsStatic) continue; if (@class is EnumDefinition) continue; if (@class.IsPureEnum) continue; var constructors = @class.Methods.Where(m => m.IsConstructor); if (!constructors.Any()) { // Only possible if base class has a default constructor if (@class.BaseClass == null || (@class.BaseClass != null && @class.BaseClass.Methods.Any(m => m.IsConstructor && !m.Parameters.Any()))) { var constructor = new MethodDefinition(@class.Name, @class, 0) { IsConstructor = true, ReturnType = new TypeRefDefinition("void") }; //TODO: //constructor.ManagedName = GetManagedMethodName(constructor); } } } }
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); } }
private void WriteMethod(MethodDefinition method, int numOptionalParams = 0) { bool convertReturnType = _extensionClassesInternal.ContainsKey(method.ReturnType.ManagedName); bufferBuilder.Clear(); WriteTabs(2, WriteTo.Buffer); Write("public unsafe static ", WriteTo.Buffer); if (convertReturnType) { Write(_extensionClassesExternal[method.ReturnType.ManagedName], WriteTo.Buffer); } else { Write(method.ReturnType.ManagedName, WriteTo.Buffer); } Write(string.Format(" {0}(this {1} obj", method.ManagedName, method.Parent.ManagedName), WriteTo.Buffer); var extendedParams = new List<ParameterDefinition>(); int numParameters = method.Parameters.Length - numOptionalParams; for (int i = 0; i < numParameters; i++) { Write(", ", WriteTo.Buffer); var param = method.Parameters[i]; if (_extensionClassesInternal.ContainsKey(param.Type.ManagedName)) { Write(string.Format("ref {0} {1}", _extensionClassesExternal[param.Type.ManagedName], param.Name), WriteTo.Buffer); extendedParams.Add(param); } else { Write(string.Format("{0} {1}", param.Type.ManagedName, param.Name), WriteTo.Buffer); } } WriteLine(')', WriteTo.Buffer); WriteTabs(2, WriteTo.Buffer); WriteLine('{', WriteTo.Buffer); // Fix parameter pointers int tabs = 3; foreach (var param in extendedParams) { WriteTabs(tabs, WriteTo.Buffer); WriteLine(string.Format("fixed ({0}* {1}Ptr = &{2})", _extensionClassesExternal[param.Type.ManagedName], param.Name, param.Name), WriteTo.Buffer); WriteTabs(tabs, WriteTo.Buffer); WriteLine('{', WriteTo.Buffer); tabs++; } WriteTabs(tabs, WriteTo.Buffer); if (method.ReturnType.Name != "void") { Write("return ", WriteTo.Buffer); } Write(string.Format("obj.{0}(", method.ManagedName), WriteTo.Buffer); bool hasOptionalParam = false; for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; if (_extensionClassesInternal.ContainsKey(param.Type.ManagedName)) { Write(string.Format("ref *({0}*){1}Ptr", _extensionClassesInternal[param.Type.ManagedName], param.Name), WriteTo.Buffer); } else { Write(param.Name, WriteTo.Buffer); } if (param.IsOptional) { hasOptionalParam = true; } if (i != numParameters - 1) { Write(", ", WriteTo.Buffer); } } Write(')', WriteTo.Buffer); if (convertReturnType) { Write(_returnTypeConversion[method.ReturnType.ManagedName], WriteTo.Buffer); } WriteLine(';', WriteTo.Buffer); // Close fixed blocks while (tabs != 2) { tabs--; WriteTabs(tabs, WriteTo.Buffer); WriteLine('}', WriteTo.Buffer); } _extensionMethods.Add(new KeyValuePair<string, string>(method.Name, bufferBuilder.ToString())); if (hasOptionalParam) { WriteMethod(method, numOptionalParams + 1); } }
public static string GetTypeMarshalConstructorEndCS(MethodDefinition method) { if (GetTypeMarshalConstructorStartCS(method) != "") { return ")"; } return ""; }
void OutputMethodMarshal(MethodDefinition method, int numParameters) { if (method.IsConstructor) { SourceWrite(method.Parent.FullyQualifiedName); } 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"); } } // Insert comma if there are more parameters if (i != numParameters - 1) { if (_sourceLineLength >= LineBreakWidth) { SourceWriteLine(","); WriteTabs(2, true); } else { SourceWrite(", "); } } } SourceWrite(')'); }
private void WriteMethodDeclaration(MethodDefinition method, int numParameters, int level, int overloadIndex, MethodDefinition returnParamMethod = null) { // Do not write accessor methods of C# properties WriteTo cs = (method.Property == null) ? WriteTo.CS : WriteTo.None; // Cached properties that are initialized only once do not need a DllImport for the get method WriteTo dllImport; if (method.Property != null && method.Property.Setter == null && method.Parent.CachedProperties.ContainsKey(method.Property.Name)) { dllImport = WriteTo.None; } else { dllImport = WriteTo.Buffer; } // Skip delete methods in classes that can't be constructed (including all subclasses). if (method.Name.Equals("delete")) { if (method.Parent.HidePublicConstructors) { // TODO: Check all subclasses //return; } } WriteTabs(1); Write("EXPORT ", WriteTo.Header); WriteTabs(level + 1, cs); Write("public ", cs); // DllImport clause WriteTabs(level + 1, dllImport); WriteLine("[DllImport(Native.Dll, CallingConvention = Native.Conv), SuppressUnmanagedCodeSecurity]", dllImport); if (method.ReturnType != null && method.ReturnType.ManagedName.Equals("bool")) { WriteTabs(level + 1, dllImport); WriteLine("[return: MarshalAs(UnmanagedType.I1)]", dllImport); } WriteTabs(level + 1, dllImport); Write("static extern ", dllImport); // Return type if (method.IsConstructor) { Write(method.Parent.FullNameC, WriteTo.Header); Write(method.Parent.FullyQualifiedName, WriteTo.Source); Write("* ", WriteTo.Header | WriteTo.Source); Write("IntPtr ", dllImport); } else { if (method.IsStatic) { Write("static ", cs); } WriteType(method.ReturnType, WriteTo.Header | WriteTo.Source); if (cs != 0) { WriteTypeCS((returnParamMethod != null) ? returnParamMethod.ReturnType : method.ReturnType); } Write(' ', WriteTo.Header | WriteTo.Source | cs); if (method.ReturnType.IsBasic) { Write(method.ReturnType.ManagedNameCS, dllImport); } else if (method.ReturnType.HasTemplateTypeParameter) { Write(method.ReturnType.ManagedNameCS, dllImport); } else if (method.ReturnType.Referenced != null) { Write("IntPtr", dllImport); } else { // Return structures to an additional out parameter, not immediately Write("void", dllImport); } Write(' ', dllImport); } // Name string methodName = method.IsConstructor ? "new" : method.Name; Write($"{method.Parent.FullNameC}_{methodName}", WriteTo.Header | WriteTo.Source | dllImport); if (methodName.Equals("delete")) { WriteDeleteMethodCS(method, level); } else { string methodNameManaged = method.IsConstructor ? method.Parent.ManagedName : method.ManagedName; Write(methodNameManaged, cs); } // Index number for overloaded methods if (overloadIndex != 0) { Write((overloadIndex + 1).ToString(), WriteTo.Header | WriteTo.Source | dllImport); } // Parameters if (!method.Name.Equals("delete")) { Write('(', cs); } Write('(', WriteTo.Header | WriteTo.Source | dllImport); // The first parameter is the instance pointer (if not constructor or static method) if (!method.IsConstructor && !method.IsStatic) { Write($"{method.Parent.FullNameC}* obj", WriteTo.Header); Write($"{method.Parent.FullyQualifiedName}* obj", WriteTo.Source); Write("IntPtr obj", dllImport); if (numParameters != 0) { Write(", ", WriteTo.Header | WriteTo.Source | dllImport); } } for (int i = 0; i < numParameters; i++) { bool isFinalParameter = (i == numParameters - 1); bool isCsFinalParameter = returnParamMethod != null && isFinalParameter; var param = method.Parameters[i]; // Parameter type if (!isCsFinalParameter) { if (param.Type.Referenced != null && !(param.Type.IsConst || param.Type.Referenced.IsConst) && BulletParser.MarshalStructByValue(param.Type)) { Write("out ", cs); } } WriteType(param.Type, WriteTo.Header | WriteTo.Source); if (cs != 0 && !isCsFinalParameter) { WriteTypeCS(param.Type); } Write(BulletParser.GetTypeDllImport(param.Type), dllImport); // Parameter name if (!isCsFinalParameter) { Write($" {param.ManagedName}", cs); } Write($" {param.Name}", WriteTo.Header | WriteTo.Source | dllImport); if (!isFinalParameter) { Write(", ", WriteTo.Header | WriteTo.Source | dllImport); } if (!(isFinalParameter || (returnParamMethod != null && i == numParameters - 2))) { Write(", ", WriteTo.CS); } } WriteLine(");", WriteTo.Header | dllImport); if (!method.Name.Equals("delete")) { WriteLine(')', cs); } WriteLine(')', WriteTo.Source); }
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($"#ifndef {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 = $"{method.Property.Name}::{headerMethodName}"; } else { headerMethodName = method.ManagedName; sourceMethodName = headerMethodName; } HeaderWrite($"{headerMethodName}("); SourceWrite($"{parentClass.FullNameCppCli}::{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($"{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($": {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 = $"\t{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 WriteClass(ClassDefinition c, int level) { if (c.IsExcluded || c.IsTypedef || c.IsPureEnum || c is ClassTemplateDefinition || c is EnumDefinition) { return; } if (wrapperHeaderGuards.ContainsKey(c.Name)) { WriteClassWrapper(c); } // Write class definition EnsureWhiteSpace(WriteTo.CS); WriteTabs(level, WriteTo.CS); Write("public ", WriteTo.CS); if (c.IsAbstract) { Write("abstract ", WriteTo.CS); } Write($"class {c.ManagedName}", WriteTo.CS); if (c.BaseClass != null) { Write(" : ", WriteTo.CS); WriteLine(c.BaseClass.FullNameCppCli.Replace("::", "."), WriteTo.CS); } else if (c.IsStaticClass) { WriteLine(WriteTo.CS); } else { WriteLine(" : IDisposable", WriteTo.CS); } WriteTabs(level, WriteTo.CS); WriteLine("{", WriteTo.CS); hasCSWhiteSpace = true; // Write child classes foreach (var cl in c.Classes.OrderBy(x => x.FullNameCppCli)) { WriteClass(cl, level + 1); } // Write the native pointer to the base class if (c.BaseClass == null && !c.IsStaticClass) { EnsureWhiteSpace(WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine("internal IntPtr _native;", WriteTo.CS); if (c.HasPreventDelete) { WriteTabs(level + 1, WriteTo.CS); WriteLine("bool _preventDelete;", WriteTo.CS); hasCSWhiteSpace = false; } hasCSWhiteSpace = false; } // Write cached property fields if (c.CachedProperties.Any()) { EnsureWhiteSpace(WriteTo.CS); foreach (var cachedProperty in c.CachedProperties.OrderBy(p => p.Key)) { WriteTabs(level + 1, WriteTo.CS); string name = cachedProperty.Key; name = char.ToLower(name[0]) + name.Substring(1); WriteLine(string.Format("{0} {1} _{2};", cachedProperty.Value.Access.ToString().ToLower(), cachedProperty.Value.Property.Type.ManagedNameCS, name), WriteTo.CS); } hasCSWhiteSpace = false; } // Write methods bufferBuilder.Clear(); // Write constructors if (!c.IsStaticClass) { // Write C# internal constructor if (!c.NoInternalConstructor) { EnsureWhiteSpace(WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); Write($"internal {c.ManagedName}(IntPtr native", WriteTo.CS); if (c.HasPreventDelete) { Write(", bool preventDelete", WriteTo.CS); } WriteLine(')', WriteTo.CS); if (c.BaseClass != null) { WriteTabs(level + 2, WriteTo.CS); Write(": base(native", WriteTo.CS); if (c.HasPreventDelete) { if (!c.BaseClass.HasPreventDelete) { // Base class should also have preventDelete //throw new NotImplementedException(); } Write(", preventDelete", WriteTo.CS); } else { if (c.BaseClass.HasPreventDelete) { Write(", true", WriteTo.CS); } } WriteLine(')', WriteTo.CS); } WriteTabs(level + 1, WriteTo.CS); WriteLine('{', WriteTo.CS); if (c.BaseClass == null) { WriteTabs(level + 2, WriteTo.CS); WriteLine("_native = native;", WriteTo.CS); if (c.HasPreventDelete) { WriteTabs(level + 2, WriteTo.CS); WriteLine("_preventDelete = preventDelete;", WriteTo.CS); } } WriteTabs(level + 1, WriteTo.CS); WriteLine('}', WriteTo.CS); hasCSWhiteSpace = false; } // Write public constructors if (!c.HidePublicConstructors && !c.IsAbstract) { int overloadIndex = 0; var constructors = c.Methods.Where(m => m.IsConstructor && !m.IsExcluded); if (constructors.Any()) { foreach (var constructor in constructors) { WriteMethod(constructor, level, ref overloadIndex); } } } } // Write methods var methods = c.Methods.Where(m => !m.IsConstructor && !m.IsExcluded).OrderBy(m => m.Name); var methodsOverloads = methods.GroupBy(m => m.Name); foreach (var groupByName in methodsOverloads) { int overloadIndex = 0; foreach (var method in groupByName) { WriteMethod(method, level, ref overloadIndex); } } // Write properties foreach (var prop in c.Properties) { WriteProperty(prop, level); } // Write delete method if (c.BaseClass == null && !c.IsStaticClass) { int overloadIndex = 0; var del = new MethodDefinition("delete", c, 0); del.ReturnType = new TypeRefDefinition(); WriteMethod(del, level, ref overloadIndex); c.Methods.Remove(del); } // Write DllImport clauses if (bufferBuilder.Length != 0) { EnsureWhiteSpace(WriteTo.CS); Write(bufferBuilder.ToString(), WriteTo.CS); } WriteTabs(level, WriteTo.CS); WriteLine("}", WriteTo.CS); hasCSWhiteSpace = false; hasCppClassSeparatingWhitespace = false; }
void WriteMethodDefinition(MethodDefinition method, int numParameters, int overloadIndex, int level, MethodDefinition returnParamMethod) { // Skip methods wrapped by C# properties WriteTo cs = (method.Property == null) ? WriteTo.CS : WriteTo.None; // Field marshalling if (method.Field != null && method.Field.Type.Referenced == null && BulletParser.MarshalStructByValue(method.Field.Type)) { WriteTabs(1, WriteTo.Source); if (method.Property.Getter.Name == method.Name) { WriteLine(BulletParser.GetFieldGetterMarshal(method.Parameters[0], method.Field), WriteTo.Source); } else { WriteLine(BulletParser.GetFieldSetterMarshal(method.Parameters[0], method.Field), WriteTo.Source); } return; } int numParametersOriginal = numParameters; if (returnParamMethod != null) { numParametersOriginal--; } bool needTypeMarshalEpilogue = false; if (!(method.Property != null && BulletParser.MarshalStructByValue(method.Property.Type) && method.Property.Getter.Name == method.Name)) { // Type marshalling prologue for (int i = 0; i < numParametersOriginal; i++) { var param = method.Parameters[i]; string prologue = BulletParser.GetTypeMarshalPrologue(param, method); if (!string.IsNullOrEmpty(prologue)) { WriteTabs(1, WriteTo.Source); WriteLine(prologue, WriteTo.Source); } // Do we need a type marshalling epilogue? if (!needTypeMarshalEpilogue) { string epilogue = BulletParser.GetTypeMarshalEpilogue(param); if (!string.IsNullOrEmpty(epilogue)) { needTypeMarshalEpilogue = true; } } } } WriteTabs(1, WriteTo.Source); if (returnParamMethod != null) { // Temporary variable Write(BulletParser.GetTypeNameCS(returnParamMethod.ReturnType), cs); Write(' ', cs); Write(method.Parameters[numParametersOriginal].ManagedName, cs); WriteLine(';', cs); WriteTabs(level + 2, cs); Write(BulletParser.GetReturnValueMarshalStart(returnParamMethod.ReturnType), WriteTo.Source); } if (method.IsConstructor) { Write("return new ", WriteTo.Source); Write(method.Parent.FullName, WriteTo.Source); if (method.Parent.BaseClass == null) { Write("_native = ", WriteTo.CS); } Write(method.Parent.FullNameCS, WriteTo.CS); Write("_new", WriteTo.CS); } else { if (!method.IsVoid) { var returnType = method.ReturnType; if (needTypeMarshalEpilogue) { // Store return value in a temporary variable Write(BulletParser.GetTypeRefName(returnType), WriteTo.Source); Write(" ret = ", WriteTo.Source); } else { // Return immediately Write("return ", WriteTo.Source); } Write("return ", cs); Write(BulletParser.GetTypeMarshalConstructorStartCS(method), cs); if (!returnType.IsBasic && !returnType.IsPointer && !returnType.IsConstantArray) { if (!(returnType.Target != null && returnType.Target.IsPureEnum)) { Write('&', WriteTo.Source); } } } if (method.IsStatic) { Write(method.Parent.Name, WriteTo.Source); Write("::", WriteTo.Source); } else { Write("obj->", WriteTo.Source); } if (method.Field == null) { Write(method.Name, WriteTo.Source); } Write(method.Parent.FullNameCS, cs); Write('_', cs); Write(method.Name, cs); } if (overloadIndex != 0) { Write((overloadIndex + 1).ToString(), cs); } // Call parameters if (method.Field != null) { Write(method.Field.Name, WriteTo.Source); if (method.Property.Setter != null && method.Name.Equals(method.Property.Setter.Name)) { Write(" = value", WriteTo.Source); } WriteLine(';', WriteTo.Source); } else { Write('(', WriteTo.Source); for (int i = 0; i < numParametersOriginal; i++) { var param = method.Parameters[i]; string marshal = BulletParser.GetTypeMarshal(param); if (!string.IsNullOrEmpty(marshal)) { Write(marshal, WriteTo.Source); } else { if (!param.Type.IsBasic && !param.Type.IsPointer && !param.Type.IsConstantArray) { Write('*', WriteTo.Source); } Write(param.Name, WriteTo.Source); } if (i != numParametersOriginal - 1) { Write(", ", WriteTo.Source); } } if (returnParamMethod != null) { WriteLine(BulletParser.GetReturnValueMarshalEnd(method.Parameters[numParametersOriginal]), WriteTo.Source); } else { WriteLine(");", WriteTo.Source); } } if (cs != 0) { Write('(', WriteTo.CS); if (!method.IsConstructor && !method.IsStatic) { Write("_native", WriteTo.CS); if (numParametersOriginal != 0) { Write(", ", WriteTo.CS); } } for (int i = 0; i < numParameters; i++) { var param = method.Parameters[i]; Write(BulletParser.GetTypeCSMarshal(param), WriteTo.CS); if (i != numParameters - 1) { Write(", ", WriteTo.CS); } } if (method.IsConstructor && method.Parent.BaseClass != null) { Write(")", WriteTo.CS); if (method.Parent.BaseClass.Target.HasPreventDelete) { Write(", false"); } WriteLine(")", WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine('{', WriteTo.CS); } else { if (!method.IsConstructor && !method.IsVoid) { Write(BulletParser.GetTypeMarshalConstructorEndCS(method), cs); } WriteLine(");", WriteTo.CS); } } // Return temporary variable if (returnParamMethod != null) { WriteTabs(level + 2, cs); Write("return ", cs); Write(method.Parameters[numParametersOriginal].ManagedName, cs); WriteLine(';', cs); } // Write type marshalling epilogue if (needTypeMarshalEpilogue) { for (int i = 0; i < numParametersOriginal; i++) { var param = method.Parameters[i]; string epilogue = BulletParser.GetTypeMarshalEpilogue(param); if (!string.IsNullOrEmpty(epilogue)) { WriteTabs(1, WriteTo.Source); WriteLine(epilogue, WriteTo.Source); } } if (!method.IsVoid) { WriteTabs(1, WriteTo.Source); WriteLine("return ret;", WriteTo.Source); } } }
void WriteMethod(MethodDefinition method, int level, ref int overloadIndex, int numOptionalParams = 0) { EnsureWhiteSpace(WriteTo.Source); if (!_hasCppClassSeparatingWhitespace) { WriteLine(WriteTo.Header | WriteTo.Source); _hasCppClassSeparatingWhitespace = true; } EnsureWhiteSpace(WriteTo.Source); int numOptionalParamsTotal = method.NumOptionalParameters; int numParameters = method.Parameters.Length - numOptionalParamsTotal + numOptionalParams; WriteMethodDeclaration(method, numParameters, level, overloadIndex); // Method body WriteLine('{', WriteTo.Source); WriteMethodDefinition(method, numParameters, level, overloadIndex); WriteLine('}', WriteTo.Source); hasSourceWhiteSpace = false; // If there are optional parameters, then output all possible combinations of calls overloadIndex++; if (numOptionalParams < numOptionalParamsTotal) { WriteMethod(method, level, ref overloadIndex, numOptionalParams + 1); } }
void WriteDeleteMethod(MethodDefinition method, int level) { // public void Dispose() WriteLine("Dispose()", WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine('{', WriteTo.CS); WriteTabs(level + 2, WriteTo.CS); WriteLine("Dispose(true);", WriteTo.CS); WriteTabs(level + 2, WriteTo.CS); WriteLine("GC.SuppressFinalize(this);", WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine('}', WriteTo.CS); // protected virtual void Dispose(bool disposing) WriteLine(WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine("protected virtual void Dispose(bool disposing)", WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine("{", WriteTo.CS); WriteTabs(level + 2, WriteTo.CS); WriteLine("if (_native != IntPtr.Zero)", WriteTo.CS); WriteTabs(level + 2, WriteTo.CS); WriteLine('{', WriteTo.CS); if (method.Parent.HasPreventDelete) { WriteTabs(level + 3, WriteTo.CS); WriteLine("if (!_preventDelete)", WriteTo.CS); WriteTabs(level + 3, WriteTo.CS); WriteLine('{', WriteTo.CS); WriteTabs(level + 4, WriteTo.CS); Write(method.Parent.FullNameCS, WriteTo.CS); WriteLine("_delete(_native);", WriteTo.CS); WriteTabs(level + 3, WriteTo.CS); WriteLine('}', WriteTo.CS); } else { WriteTabs(level + 3, WriteTo.CS); Write(method.Parent.FullNameCS, WriteTo.CS); WriteLine("_delete(_native);", WriteTo.CS); } WriteTabs(level + 3, WriteTo.CS); WriteLine("_native = IntPtr.Zero;", WriteTo.CS); WriteTabs(level + 2, WriteTo.CS); WriteLine('}', WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine('}', WriteTo.CS); // C# Destructor WriteLine(WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); Write('~', WriteTo.CS); Write(method.Parent.ManagedName, WriteTo.CS); WriteLine("()", WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine('{', WriteTo.CS); WriteTabs(level + 2, WriteTo.CS); WriteLine("Dispose(false);", WriteTo.CS); WriteTabs(level + 1, WriteTo.CS); WriteLine('}', WriteTo.CS); }
void WriteMethodDefinition(MethodDefinition method, int numParameters, int level, int overloadIndex) { To = WriteTo.Source; if (method.IsDestructor) { WriteLine(1, "delete obj;", WriteTo.Source); return; } string qualifier = method.IsStatic ? $"{method.Parent.Name}::" : "obj->"; // Struct field marshalling var field = method.Field; if (field != null) { string fieldName = $"{qualifier}{field.Name}"; if (field.Type.Target != null && field.Type.Target.MarshalAsStruct) { string macroPrefix = field.Type.Name.ToUpper(); string paramName = (method.OutValueParameter != null ? method.OutValueParameter : method.Parameters[0]).Name; if (method == field.Getter) { WriteLine(1, $"{macroPrefix}_SET({paramName}, {fieldName});"); } else { WriteLine(1, $"{macroPrefix}_COPY(&{fieldName}, {paramName});"); } } else if (method == field.Getter) { if (field.Type.Kind == TypeKind.Record) { WriteLine(1, $"return &{qualifier}{field.Name};"); } else { WriteLine(1, $"return {qualifier}{field.Name};"); } } else { WriteLine(1, $"{qualifier}{field.Name} = value;"); } return; } // Parameter marshalling prologues bool needTypeMarshalEpilogue = false; foreach (var param in method.Parameters.Take(numParameters)) { if (param.Type.Kind == TypeKind.LValueReference && param.Type.Referenced.Target != null && param.Type.Referenced.Target.MarshalAsStruct) { string macroPrefix = param.Type.Referenced.Target.Name.ToUpper(); if (param.MarshalDirection == MarshalDirection.Out) { WriteLine(1, $"{macroPrefix}_DEF({param.Name});"); needTypeMarshalEpilogue = true; } else { WriteLine(1, $"{macroPrefix}_IN({param.Name});"); if (param.MarshalDirection == MarshalDirection.InOut) { needTypeMarshalEpilogue = true; } } } } WriteTabs(1); if (method.IsConstructor) { Write($"return new {method.Parent.FullyQualifiedName}"); } else { if (!method.IsVoid) { var returnType = method.ReturnType.Canonical; if (method.OutValueParameter != null) { string macroPrefix = method.OutValueParameter.Type.Referenced.Name.ToUpper(); if (returnType.Kind == TypeKind.LValueReference) { Write($"{macroPrefix}_COPY({method.OutValueParameter.Name}, &"); } else { Write($"{macroPrefix}_SET({method.OutValueParameter.Name}, "); } } else { if (needTypeMarshalEpilogue) { // Save the return value and return it after the epilogues Write($"{GetTypeNameC(method.ReturnType)} ret = "); } else { // Return immediately Write("return "); } switch (returnType.Kind) { case TypeKind.LValueReference: case TypeKind.Record: if (!(returnType.Target != null && returnType.Target is EnumDefinition)) { Write('&'); } break; } } } Write($"{qualifier}{method.Name}"); } // Call parameters var originalParams = method.Parameters.Take(numParameters) .Select(p => { if (p.Type.Target != null && p.Type.Target.MarshalAsStruct) { string macroPrefix = p.Type.Target.Name.ToUpper(); return $"{macroPrefix}_USE({p.Name})"; } if (p.Type.Kind == TypeKind.LValueReference && p.Type.Referenced.Target != null && p.Type.Referenced.Target.MarshalAsStruct) { string macroPrefix = p.Type.Referenced.Target.Name.ToUpper(); return $"{macroPrefix}_USE({p.Name})"; } var type = p.Type.Canonical; if (type.Kind == TypeKind.LValueReference) { return $"*{p.Name}"; } return p.Name; }); Write("("); Write($"{ListToLines(originalParams, WriteTo.Source, 1)})"); if (method.OutValueParameter != null) { Write(")"); } WriteLine(";"); // Write type marshalling epilogue if (needTypeMarshalEpilogue) { foreach (var param in method.Parameters.Take(numParameters) .Where(p => p.MarshalDirection == MarshalDirection.Out || p.MarshalDirection == MarshalDirection.InOut)) { if (param.Type.Kind == TypeKind.LValueReference && param.Type.Referenced.Target != null && param.Type.Referenced.Target.MarshalAsStruct) { string macroPrefix = param.Type.Referenced.Target.Name.ToUpper(); WriteLine(1, $"{macroPrefix}_DEF_OUT({param.Name});"); } } if (!method.IsVoid) { WriteLine(1, "return ret;"); } } }
void WriteMethodDeclaration(MethodDefinition method, int numParameters, int level, int overloadIndex, MethodDefinition returnParamMethod = null) { // Skip methods wrapped by C# properties WriteTo cs = (method.Property == null) ? WriteTo.CS : WriteTo.None; WriteTabs(1); Write("EXPORT ", WriteTo.Header); WriteTabs(level + 1, cs); Write("public ", cs); // DllImport clause WriteTabs(level + 1, WriteTo.Buffer); WriteLine("[DllImport(Native.Dll, CallingConvention = Native.Conv), SuppressUnmanagedCodeSecurity]", WriteTo.Buffer); if (method.ReturnType != null && method.ReturnType.ManagedName.Equals("bool")) { WriteTabs(level + 1, WriteTo.Buffer); WriteLine("[return: MarshalAs(UnmanagedType.I1)]", WriteTo.Buffer); } WriteTabs(level + 1, WriteTo.Buffer); Write("static extern ", WriteTo.Buffer); // Return type if (method.IsConstructor) { Write(method.Parent.FullNameCS, WriteTo.Header); Write(method.Parent.FullName, WriteTo.Source); Write("* ", WriteTo.Header | WriteTo.Source); Write("IntPtr ", WriteTo.Buffer); } else { if (method.IsStatic) { Write("static ", cs); } WriteType(method.ReturnType, WriteTo.Header | WriteTo.Source); if (cs != 0) { WriteTypeCS((returnParamMethod != null) ? returnParamMethod.ReturnType : method.ReturnType); } Write(' ', WriteTo.Header | WriteTo.Source | cs); if (method.ReturnType.IsBasic) { Write(method.ReturnType.ManagedNameCS, WriteTo.Buffer); } else if (method.ReturnType.HasTemplateTypeParameter) { Write(method.ReturnType.ManagedNameCS, WriteTo.Buffer); } else if (method.ReturnType.Referenced != null) { Write("IntPtr", WriteTo.Buffer); } else { // Return structures to an additional out parameter, not immediately Write("void", WriteTo.Buffer); } Write(' ', WriteTo.Buffer); } // Name Write(method.Parent.FullNameCS, WriteTo.Header | WriteTo.Source | WriteTo.Buffer); Write('_', WriteTo.Header | WriteTo.Source | WriteTo.Buffer); if (method.IsConstructor) { Write("new", WriteTo.Header | WriteTo.Source | WriteTo.Buffer); Write(method.Parent.ManagedName, WriteTo.CS); } else { Write(method.Name, WriteTo.Header | WriteTo.Source | WriteTo.Buffer); if (method.Name.Equals("delete")) { WriteDeleteMethod(method, level); } else { Write(method.ManagedName, cs); } } // Index number for overloaded methods if (overloadIndex != 0) { Write((overloadIndex + 1).ToString(), WriteTo.Header | WriteTo.Source | WriteTo.Buffer); } // Parameters if (!method.Name.Equals("delete")) { Write('(', cs); } Write('(', WriteTo.Header | WriteTo.Source | WriteTo.Buffer); // The first parameter is the instance pointer (if not constructor or static method) if (!method.IsConstructor && !method.IsStatic) { Write(method.Parent.FullNameCS, WriteTo.Header); Write(method.Parent.FullName, WriteTo.Source); Write("* obj", WriteTo.Header | WriteTo.Source); Write("IntPtr obj", WriteTo.Buffer); if (numParameters != 0) { Write(", ", WriteTo.Header | WriteTo.Source | WriteTo.Buffer); } } for (int i = 0; i < numParameters; i++) { bool isFinalParameter = (i == numParameters - 1); bool isCsFinalParameter = (returnParamMethod != null) ? isFinalParameter : false; var param = method.Parameters[i]; // Parameter type if (!isCsFinalParameter) { if (param.Type.Referenced != null && !(param.Type.IsConst || param.Type.Referenced.IsConst) && BulletParser.MarshalStructByValue(param.Type)) { Write("out ", cs); } } WriteType(param.Type, WriteTo.Header | WriteTo.Source); if (cs != 0 && !isCsFinalParameter) { WriteTypeCS(param.Type); } Write(BulletParser.GetTypeDllImport(param.Type), WriteTo.Buffer); // Parameter name if (!isCsFinalParameter) { Write(' ', cs); Write(param.ManagedName, cs); } Write(' ', WriteTo.Header | WriteTo.Source | WriteTo.Buffer); Write(param.Name, WriteTo.Header | WriteTo.Source | WriteTo.Buffer); if (!isFinalParameter) { Write(", ", WriteTo.Header | WriteTo.Source | WriteTo.Buffer); } if (!(isFinalParameter || (returnParamMethod != null && i == numParameters - 2))) { Write(", ", WriteTo.CS); } } WriteLine(");", WriteTo.Header | WriteTo.Buffer); if (!method.Name.Equals("delete")) { WriteLine(')', cs); } WriteLine(')', WriteTo.Source); }
public static string GetTypeMarshalPrologue(ParameterDefinition parameter, MethodDefinition method) { if (method.Field != null) { return null; } switch (parameter.Type.ManagedName) { case "Quaternion": return "QUATERNION_CONV(" + parameter.Name + ");"; case "Matrix3x3": return "MATRIX3X3_CONV(" + parameter.Name + ");"; case "Transform": return "TRANSFORM_CONV(" + parameter.Name + ");"; case "Vector3": return "VECTOR3_CONV(" + parameter.Name + ");"; case "Vector4": return "VECTOR4_CONV(" + parameter.Name + ");"; default: return null; } }