SharpGen/Transform/MarshalledElementFactory.cs (206 lines of code) (raw):

using System; using System.Diagnostics; using System.Linq; using SharpGen.Config; using SharpGen.CppModel; using SharpGen.Logging; using SharpGen.Model; namespace SharpGen.Transform { public sealed class MarshalledElementFactory { private readonly Ioc ioc; private Logger Logger => ioc.Logger; private GlobalNamespaceProvider GlobalNamespace => ioc.GlobalNamespace; private TypeRegistry TypeRegistry => ioc.TypeRegistry; public MarshalledElementFactory(Ioc ioc) { this.ioc = ioc ?? throw new ArgumentNullException(nameof(ioc)); } public event Action<CsStruct> RequestStructProcessing; private void CreateCore(CsMarshalBase csMarshallable) { var marshallable = (CppMarshallable) csMarshallable.CppElement; CsTypeBase publicType = null; CsTypeBase marshalType = null; var mappingRule = marshallable.Rule; var publicTypeName = mappingRule is {MappingType: { } mapType} ? mapType : marshallable.TypeName; // If CppType is an array, try first to get the binding for this array if (csMarshallable.IsArray) { publicType = TypeRegistry.FindBoundType(publicTypeName + "[" + marshallable.ArrayDimension + "]"); if (publicType != null) csMarshallable.IsArray = false; } if (publicType == null) { void AssignStringTypes(CsFundamentalType charType) { publicType = csMarshallable.HasPointer || csMarshallable.IsArray ? TypeRegistry.String : charType; marshalType = csMarshallable.IsArray ? charType : null; } switch (publicTypeName) { case "char": AssignStringTypes(TypeRegistry.UInt8); break; case "wchar_t": csMarshallable.IsWideChar = true; AssignStringTypes(TypeRegistry.Char); break; default: // Try to get a declared type if (!TypeRegistry.FindBoundType(publicTypeName, out var boundType)) { Logger.Fatal("Unknown type found [{0}]", publicTypeName); return; } publicType = boundType.CSharpType; // By default, use the underlying native type as the marshal type // if it differs from the public type. marshalType = TypeRegistry.FindBoundType(marshallable.TypeName); if (publicType == marshalType) marshalType = null; // Otherwise, get the registered marshal type if one exists marshalType ??= boundType.MarshalType; break; } } switch (publicType) { case CsStruct csStruct: { if (!csStruct.IsFullyMapped) // If a structure was not already parsed, then parse it before going further RequestStructProcessing?.Invoke(csStruct); if (!csStruct.IsFullyMapped) // No one tried to map the struct so we can't continue. Logger.Fatal( $"No struct processor processed {csStruct.QualifiedName}. Cannot continue processing" ); if (csStruct.HasMarshalType && !csMarshallable.HasPointer) // If referenced structure has a specialized marshalling, then use the structure's built-in marshalling marshalType = publicType; break; } case CsEnum: // enums don't need a marshal type. They can always marshal as their underlying type. marshalType = null; break; } if (publicType.IsWellKnownType(GlobalNamespace, WellKnownName.PointerSize)) marshalType = PointerType(mappingRule); // Present void* elements as IntPtr. Marshal strings as IntPtr if (marshallable.HasPointer) { if (publicType == TypeRegistry.Void) publicType = marshalType = PointerType(mappingRule); else if (publicType == TypeRegistry.String) marshalType = TypeRegistry.IntPtr; } csMarshallable.PublicType = publicType; csMarshallable.MarshalType = marshalType; csMarshallable.Relations = RelationParser.ParseRelation(mappingRule.Relation, Logger); } private static CsFundamentalType PointerType(MappingRule mappingRule) => mappingRule.KeepPointers != true ? TypeRegistry.IntPtr : TypeRegistry.VoidPtr; public CsReturnValue Create(CppReturnValue cppReturnValue) { CsReturnValue retVal = new(ioc, cppReturnValue); CreateCore(retVal); MakeGeneralPointersBeIntPtr(retVal); if (retVal.PublicType is CsInterface {IsCallback: true} iface) { retVal.PublicType = iface.GetNativeImplementationOrThis(); } return retVal; } public CsField Create(CppField cppField, string name) { CsField field = new(ioc, cppField, name); CreateCore(field); MakeGeneralPointersBeIntPtr(field); return field; } private static void MakeGeneralPointersBeIntPtr(CsMarshalBase csMarshallable) { if (!csMarshallable.HasPointer) return; if (csMarshallable is CsReturnValue {PublicType: CsStruct} && !csMarshallable.IsArray) return; csMarshallable.MarshalType = TypeRegistry.IntPtr; if (csMarshallable.IsString || csMarshallable.IsInterface) return; csMarshallable.PublicType = TypeRegistry.IntPtr; } public CsParameter Create(CppParameter cppParameter, string name) { CsParameter param = new(ioc, cppParameter, name); CreateCore(param); if (cppParameter.IsAttributeRuleRedundant) Logger.Message("Parameter [{0}] has redundant attribute rule specification", cppParameter.FullName); static bool HasFlag(ParamAttribute value, ParamAttribute flag) => (value & flag) == flag; // -------------------------------------------------------------------------------- // Pointer - Handle special cases // -------------------------------------------------------------------------------- if (param.HasPointer) { var paramRule = cppParameter.Rule; var numIndirections = cppParameter.Pointer.Count(static p => p is '*' or '&'); bool isBuffer, isIn, isInOut, isOut; { var cppAttribute = cppParameter.Attribute; // Force Interface** to be ParamAttribute.Out if (param.PublicType is CsInterface && cppAttribute == ParamAttribute.In && numIndirections == 2) cppAttribute = ParamAttribute.Out; isBuffer = HasFlag(cppAttribute, ParamAttribute.Buffer); isIn = HasFlag(cppAttribute, ParamAttribute.In); isInOut = HasFlag(cppAttribute, ParamAttribute.InOut); isOut = HasFlag(cppAttribute, ParamAttribute.Out); } // Either In, InOut or Out is set Debug.Assert((isIn ? 1 : 0) + (isInOut ? 1 : 0) + (isOut ? 1 : 0) == 1); // -------------------------------------------------------------------------------- // Handling Parameter Interface // -------------------------------------------------------------------------------- if (param.PublicType is CsInterface) { // Simplify logic by assuming interface instance pointer is the interface itself. --numIndirections; if (isOut) param.Attribute = CsParameterAttribute.Out; } else if (isIn) { var publicType = param.PublicType; param.Attribute = publicType is CsFundamentalType {IsPointerSize: true} || publicType.IsWellKnownType(GlobalNamespace, WellKnownName.FunctionCallback) ? CsParameterAttribute.In : CsParameterAttribute.RefIn; } else if (isInOut) { if (param.IsOptional) { param.SetPublicResetMarshalType(PointerType(paramRule)); param.Attribute = CsParameterAttribute.In; } else { param.Attribute = CsParameterAttribute.Ref; } } else if (isOut) { param.Attribute = CsParameterAttribute.Out; } switch (param.PublicType) { // Handle void* with Buffer attribute case CsFundamentalType {IsUntypedPointer: true} when isBuffer: param.Attribute = CsParameterAttribute.In; param.IsArray = false; break; // Handle strings with Out attribute case CsFundamentalType {IsString: true} when isOut: param.Attribute = CsParameterAttribute.In; param.IsArray = false; param.SetPublicResetMarshalType(TypeRegistry.IntPtr); break; // There's no way to know how to deallocate native-allocated memory correctly // since we don't know what allocator the native memory uses, // so we treat any extra pointer indirections as IntPtr case not CsFundamentalType {IsUntypedPointer: true} when numIndirections > 1: param.IsArray = false; param.SetPublicResetMarshalType(TypeRegistry.IntPtr); break; } } if (param.Relations.OfType<StructSizeRelation>().Any()) Logger.Error( LoggingCodes.InvalidRelation, $"Parameter [{cppParameter}] marked with a struct-size relationship" ); return param; } } }