private Func <TypeDefinition[]> DefineClrHandleStructInternal(ClangStructInfo structInfo, int?bits = null)
        {
            if (structInfo.Size > 0)
            {
                throw new NotImplementedException();
            }

            var structName = structInfo.Name;

            if (TypeRedirects.TryGetValue(structName, out var rename))
            {
                structName = rename;
            }
            if (Module.GetType(structName)?.Resolve() != null)
            {
                return(null);
            }

            // handle type
            var handleDef = Module.DefineType(structName,
                                              PublicSealedStructTypeAttributes, size: 0);

            handleDef.SetCustomAttribute(() => new BinderGeneratedAttribute());

            //handleDef.SetCustomAttribute(StructLayoutSequentialAttributeInfo);
            var handleInterface = IHandleGtd.MakeGenericInstanceType(handleDef);

            handleDef.AddInterfaceImplementation(handleInterface);
            var handlePointerType = handleDef.MakePointerType();
            var inputTypes        = bits == null
                                ? TypeArrayOfSingularVoidPointer
                                : bits == 64
                                        ? TypeArrayOfSingularULong
                                        : TypeArrayOfSingularUInt;
            var castMethod = handleDef.DefineMethod("Cast", PublicStaticMethodAttributes,
                                                    handlePointerType, inputTypes);

            SetMethodInliningAttributes(castMethod);
            castMethod.GenerateIL(il => {
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ret);
            });
            var handleType = handleDef.CreateType();

            return(() => new[] { handleType });
        }
Example #2
0
        private Func <TypeDefinition[]> DefineClrType(ClangEnumInfo enumInfo)
        {
            var underlyingTypeInfo = ResolveParameter(enumInfo.UnderlyingType);
            var underlyingType     = underlyingTypeInfo.Type;

            var name = enumInfo.Name;

            Debug.WriteLine($"Defining enumeration {name}");


            if (TypeRedirects.TryGetValue(name, out var renamed))
            {
                name = renamed;
            }

            var enumTypeDef = Module.GetType(name);

            if (enumTypeDef == null)
            {
                enumTypeDef = Module.DefineEnum(name, TypeAttributes.Public, underlyingType);
                enumTypeDef.SetCustomAttribute(() => new BinderGeneratedAttribute());
            }
            else
            {
                enumTypeDef.ChangeUnderlyingType(underlyingType);
            }
            //enumTypeDef.SetCustomAttribute(FlagsAttributeInfo);

            foreach (var enumDef in enumInfo.Definitions)
            {
                enumTypeDef.DefineLiteral(enumDef.Name, Convert.ChangeType(enumDef.Value, underlyingType.GetRuntimeType()));
            }

            var enumType = enumTypeDef.CreateType();

            return(() => new[] { enumType });
        }
        private ParameterInfo ResolveParameter(CXType originalType, string name = null, int index = 0)
        {
            string renamed;
            var    type = originalType;

            if (type.kind == CXTypeKind.CXType_FunctionProto)
            {
                throw new NotImplementedException();
            }
            if (type.kind == CXTypeKind.CXType_FunctionNoProto)
            {
                throw new NotImplementedException();
            }
            var typeKind = CanonizeType(ref type, out var typeDeclCursor);

            if (typeKind == CXTypeKind.CXType_Pointer)
            {
                var pointeeType = clang.getPointeeType(type);
                if (clang.getFunctionTypeCallingConv(pointeeType) != CXCallingConv.CXCallingConv_Invalid)
                {
                    var delegateTypeName     = originalType.ToString();
                    var possibleDelegateType = Module.GetType(delegateTypeName);
                    if (possibleDelegateType != null)
                    {
                        return(new ParameterInfo(name, possibleDelegateType, index));
                    }

                    return(new ParameterInfo(name,
                                             IncompleteTypeReference.Get(Module, null, delegateTypeName), index));
                }
                var resolvedParameter = ResolveParameter(pointeeType);
                return(new ParameterInfo(name,
                                         resolvedParameter.Type.MakePointerType(), index));
            }
            if (typeKind == CXTypeKind.CXType_DependentSizedArray)
            {
                throw new NotImplementedException();
            }
            if (typeKind == CXTypeKind.CXType_ConstantArray)
            {
                var arraySize         = (int)clang.getArraySize(type);
                var elementType       = clang.getArrayElementType(type);
                var resolvedParameter = ResolveParameter(elementType, name);
                var clrElementType    = resolvedParameter.Type;
                if (clrElementType.IsPointer)
                {
                    clrElementType = Module.TypeSystem.IntPtr;
                }
                var arrayType = resolvedParameter.Type.MakeArrayType();

                if (!PrimitiveUnmanagedTypeMap.TryGetValue(clrElementType.GetRuntimeType(), out var unmanagedType))
                {
                    throw new NotImplementedException();
                }

                return(new ParameterInfo(name,
                                         arrayType, index, ParameterAttributes.None, arraySize));
            }
            if (PrimitiveTypeMap.TryGetValue(typeKind, out var primitiveType))
            {
                if (primitiveType == null)
                {
                    throw new NotImplementedException();
                }
                var originalTypeName = originalType.ToString();
                var typeName         = originalTypeName;

                if (TypeRedirects.TryGetValue(originalTypeName, out renamed))
                {
                    typeName = renamed;
                }
                if (originalType.kind == CXTypeKind.CXType_Typedef)
                {
                    if (KnownTypes.ContainsKey(typeName))
                    {
                        var knownType = Module.GetType(typeName)
                                        ?? Module.GetType(originalTypeName)
                                        ?? throw new NotImplementedException();
                        return(new ParameterInfo(name, knownType, index));
                    }
                }
                else
                {
                    var found = Module.GetType(typeName);
                    if (found != null)
                    {
                        return(new ParameterInfo(name, found, index));
                    }
                }

                return(new ParameterInfo(name, primitiveType.Import(Module), index));
            }

            var typeDeclName = typeDeclCursor.ToString();

            if (TypeRedirects.TryGetValue(typeDeclName, out renamed))
            {
                typeDeclName = renamed;
            }

            var possibleType = Module.GetType(typeDeclName);

            if (possibleType != null)
            {
                return(new ParameterInfo(name, possibleType, index));
            }

            return(new ParameterInfo(name,
                                     IncompleteTypeReference.Get(Module, null, typeDeclName), index));
        }
        private IClangType ParseTypeDef(CXCursor cursor)
        {
            var originalType   = clang.getCursorType(cursor);
            var canonType      = clang.getCanonicalType(originalType);
            var typeDeclCursor = clang.getTypeDeclaration(canonType);

            if (IsCursorInSystemHeader(typeDeclCursor))
            {
                return(null);
            }

            var name = cursor.ToString();

            if (typeDeclCursor.kind == CXCursorKind.CXCursor_NoDeclFound)
            {
                if (canonType.kind != CXTypeKind.CXType_Pointer)
                {
                    // likely simple type alias
                    if (TypeRedirects.TryGetValue(name, out var renamed))
                    {
                        name = renamed;
                    }
                    if (KnownTypes.TryGetValue(name, out var knownType))
                    {
                        if (PrimitiveTypeMap.TryGetValue(canonType.kind, out var primitiveType))
                        {
                            var existingType = Module.GetType(name);
                            if (existingType == null)
                            {
                                throw new NotImplementedException();
                            }

                            switch (knownType)
                            {
                            case KnownType.Bitmask:
                            case KnownType.Enum: {
                                existingType.ChangeUnderlyingType(primitiveType.Import(Module));
                                break;
                            }

                            default:
                                break;
                            }

                            IncrementStatistic("typedefs");
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }
                    }

                    return(null);
                }

                var pointeeType = clang.getPointeeType(canonType);
                var callConv    = clang.getFunctionTypeCallingConv(pointeeType);
                if (callConv == CXCallingConv.CXCallingConv_Invalid)
                {
                    // likely a pointer type alias
                    return(null);
                }

                return(ParseDelegate(cursor, callConv));
            }

            switch (typeDeclCursor.kind)
            {
            case CXCursorKind.CXCursor_UnionDecl:
            case CXCursorKind.CXCursor_StructDecl: {
                var typeName = typeDeclCursor.ToString();
                if (name == typeName)
                {
                    return(null);
                }

                throw new NotImplementedException();
            }

            case CXCursorKind.CXCursor_EnumDecl: {
                if (TypeRedirects.TryGetValue(name, out var renamed))
                {
                    name = renamed;
                }
                if (KnownTypes.TryGetValue(name, out var knownType))
                {
                    var existingType = Module.GetType(name);
                    if (existingType != null)
                    {
                        return(null);
                    }

                    switch (knownType)
                    {
                    case KnownType.Enum: {
                        throw new NotImplementedException();
                    }

                    case KnownType.Bitmask: {
                        throw new NotImplementedException();
                    }

                    default:
                        throw new NotImplementedException();
                    }
                }

                throw new NotImplementedException();
            }
            }

            IncrementStatistic("typedefs");
            throw new NotImplementedException();
        }
        private Func <TypeDefinition[]> DefineClrType(ClangFunctionInfoBase funcInfo)
        {
            var funcName = funcInfo.Name;

            Debug.WriteLine($"Defining function {funcName}");

            if (TypeRedirects.TryGetValue(funcName, out var rename))
            {
                funcName = rename;
            }

            var funcRef = Module.GetType(funcName, true);
            var funcDef = funcRef.Resolve();

            if (funcDef != null)
            {
                return(null);
            }

            funcDef = Module.DefineType(funcName,
                                        DelegateTypeAttributes,
                                        MulticastDelegateType);
            funcDef.SetCustomAttribute(() => new BinderGeneratedAttribute());

            var umfpDef = new TypeDefinition(funcDef.Namespace, funcDef.Name + "Unmanaged",
                                             TypeAttributes.Sealed
                                             | TypeAttributes.BeforeFieldInit
                                             | TypeAttributes.SequentialLayout
                                             | TypeAttributes.Public,
                                             Module.TypeSystem.ValueType);

            Module.Types.Add(umfpDef);

            var umfpRef  = Module.ImportReference(umfpDef);
            var iumfpGtd = IUnmanagedFunctionPointerGtd.MakeGenericInstanceType(funcDef);
            var iumfpRef = iumfpGtd.Import(Module);

            umfpDef.AddInterfaceImplementation(iumfpRef);

            // todo: add implicit conversion ops using Marshal

            var retParam = ResolveParameter(funcInfo.ReturnType);

            /* todo: figure out why the attribute is jacked up
             * if (!CallingConventionMap.TryGetValue(funcInfo.CallConvention, out var callConv))
             *      throw new NotImplementedException();
             * if (!ClrCallingConventionAttributeMap.TryGetValue(callConv, out var callConvAttr))
             *      throw new NotImplementedException();
             */
            var argParams = new LinkedList <ParameterInfo>(funcInfo.Parameters.Select(p => ResolveParameter(p.Type, p.Name, (int)p.Index)));

            return(() => {
                retParam.Complete(TypeRedirects, true);

                var retType = retParam.Type;

                foreach (var argParam in argParams)
                {
                    argParam.Complete(TypeRedirects, true);
                }

                Debug.WriteLine($"Completed dependencies for function {funcName}");

                var umfpValue = umfpDef.DefineField("Value", Module.TypeSystem.IntPtr,
                                                    FieldAttributes.Public | FieldAttributes.InitOnly);
                var umfpValueRef = Module.ImportReference(umfpValue);


                var getDelegateForFpMethodDef = Module.ImportReference(
                    new GenericInstanceMethod(GetDelegateForFpMethodGtd)
                {
                    GenericArguments = { funcDef }
                });

                var getFpForDelegateMethodDef = Module.ImportReference(
                    new GenericInstanceMethod(GetFpForDelegateMethodGtd)
                {
                    GenericArguments = { funcDef }
                });

                var umfpDlgt = umfpDef.DefineMethod("op_Implicit",
                                                    PublicStaticMethodAttributes | MethodAttributes.SpecialName,
                                                    funcDef, umfpRef);

                umfpDlgt.GenerateIL(il => {
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldfld, umfpValueRef);
                    il.Emit(OpCodes.Call, getDelegateForFpMethodDef);
                    il.Emit(OpCodes.Ret);
                });

                var dlgtUmfp = umfpDef.DefineMethod("op_Implicit",
                                                    PublicStaticMethodAttributes | MethodAttributes.SpecialName,
                                                    umfpRef, funcDef);

                var dlgtUmfpV0 = new VariableDefinition(umfpRef);
                dlgtUmfp.Body.Variables.Add(dlgtUmfpV0);
                dlgtUmfp.Body.InitLocals = true;

                dlgtUmfp.GenerateIL(il => {
                    il.Emit(OpCodes.Ldloca_S, dlgtUmfpV0);
                    il.Emit(OpCodes.Initobj, umfpRef);

                    il.Emit(OpCodes.Ldloca_S, dlgtUmfpV0);

                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Call, getFpForDelegateMethodDef);
                    il.Emit(OpCodes.Stfld, umfpValueRef);

                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ret);
                });

                var umfpPtr = umfpDef.DefineMethod("op_Implicit",
                                                   PublicStaticMethodAttributes | MethodAttributes.SpecialName,
                                                   Module.TypeSystem.IntPtr, umfpRef);

                umfpPtr.GenerateIL(il => {
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldfld, umfpValueRef);
                    il.Emit(OpCodes.Ret);
                });

                var ptrUmfp = umfpDef.DefineMethod("op_Implicit",
                                                   PublicStaticMethodAttributes | MethodAttributes.SpecialName,
                                                   umfpRef, Module.TypeSystem.IntPtr);

                var ptrUmfpV0 = new VariableDefinition(umfpRef);
                ptrUmfp.Body.Variables.Add(ptrUmfpV0);
                ptrUmfp.Body.InitLocals = true;

                ptrUmfp.GenerateIL(il => {
                    var argNull = default(CecilLabel);
                    if (EmitNullChecks)
                    {
                        argNull = il.DefineLabel();
                    }
                    il.Emit(OpCodes.Ldloca_S, ptrUmfpV0);
                    il.Emit(OpCodes.Initobj, umfpRef);

                    il.Emit(OpCodes.Ldloca_S, ptrUmfpV0);
                    il.Emit(OpCodes.Ldarg_0);

                    if (EmitNullChecks)
                    {
                        il.Emit(OpCodes.Dup);
                        il.Emit(OpCodes.Brfalse, argNull);
                    }
                    il.Emit(OpCodes.Stfld, umfpValueRef);

                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ret);

                    if (EmitNullChecks)
                    {
                        il.MarkLabel(argNull);
                        il.Emit(OpCodes.Newobj, ArgumentNullCtor);
                        il.Emit(OpCodes.Throw);

                        // ReSharper disable once PossibleNullReferenceException
                        argNull.Cleanup();
                    }
                });


                var argTypes = argParams.Select(p => p.Type).ToArray();


                var retTypeDef = retType.Resolve();
                if (retTypeDef.BaseType != null && retTypeDef.BaseType.Is(MulticastDelegateType))
                {
                    // todo: marshalas umfp marshaller
                    var ufpSpecTypeRef = Module.GetType(retTypeDef.FullName + "Unmanaged");
                    Debug.Assert(ufpSpecTypeRef != null);
                    retType = ufpSpecTypeRef;
                }

                for (var i = 0; i < argTypes.Length; i++)
                {
                    var argType = argTypes[i];
                    var argTypeDef = argType.Resolve();
                    if (argType.IsPointer)
                    {
                        var interiorArgType = argType.GetInteriorType(out var transforms);
                        if (interiorArgType.Resolve().IsInterface)
                        {
                            if (!argType.Name.StartsWith("I"))
                            {
                                throw new NotImplementedException();
                            }
                            var argTypeNameBase = interiorArgType.Name.Substring(1);
                            if (!MarshallableSplitPointers.TryGetValue(argTypeNameBase, out var marshallable))
                            {
                                TypeReference splitPtrTypeRef;
                                if (_splitPointerDefs.TryGetValue(interiorArgType.FullName, out var splitPtrType))
                                {
                                    splitPtrTypeRef = splitPtrType;
                                }
                                else
                                {
                                    var argType32 = Module.GetType(interiorArgType.Namespace, argTypeNameBase + "32");
                                    Debug.Assert(argType32 != null);
                                    var argType64 = Module.GetType(interiorArgType.Namespace, argTypeNameBase + "64");
                                    Debug.Assert(argType64 != null);
                                    splitPtrType = SplitPointerGtd.MakeGenericInstanceType(interiorArgType, argType32, argType64);
                                    if (!_splitPointerDefs.TryAdd(interiorArgType.FullName, splitPtrType))
                                    {
                                        throw new NotImplementedException();
                                    }
                                    splitPtrTypeRef = splitPtrType.Import(Module);
                                }

                                marshallable = Module.DefineType(argTypeNameBase + "Ptr", PublicSealedStructTypeAttributes);
                                var valueField = marshallable.DefineField("Value", splitPtrTypeRef,
                                                                          FieldAttributes.Public | FieldAttributes.InitOnly);

                                // implicit conversion from split pointer to marshallable for use as params
                                var asSplitPtrOp = marshallable.DefineMethod("op_Implicit",
                                                                             PublicStaticMethodAttributes | MethodAttributes.SpecialName,
                                                                             splitPtrTypeRef, marshallable);
                                asSplitPtrOp.GenerateIL(il => {
                                    il.Emit(OpCodes.Ldarg_0);
                                    il.Emit(OpCodes.Ldfld, valueField);
                                    il.Emit(OpCodes.Ret);
                                });

                                // implicit conversion from marshallable to split pointer for use as returns
                                var toSplitPtrOp = marshallable.DefineMethod("op_Implicit",
                                                                             PublicStaticMethodAttributes | MethodAttributes.SpecialName,
                                                                             marshallable, splitPtrTypeRef);
                                var toSplitPtrOpV0 = new VariableDefinition(marshallable);
                                toSplitPtrOp.Body.Variables.Add(toSplitPtrOpV0);
                                toSplitPtrOp.Body.InitLocals = true;
                                toSplitPtrOp.GenerateIL(il => {
                                    il.Emit(OpCodes.Ldloca_S, ptrUmfpV0);
                                    il.Emit(OpCodes.Initobj, marshallable);
                                    il.Emit(OpCodes.Ldloca_S, ptrUmfpV0);
                                    il.Emit(OpCodes.Ldarg_0);
                                    il.Emit(OpCodes.Stfld, valueField);
                                    il.Emit(OpCodes.Ldloc_0);
                                    il.Emit(OpCodes.Ret);
                                });

                                if (!MarshallableSplitPointers.TryAdd(argTypeNameBase, marshallable))
                                {
                                    throw new NotImplementedException();
                                }
                            }
                            Debug.Assert(marshallable != null);
                            argTypes[i] = marshallable.Import(Module)
                                          .ApplyTransforms(transforms.Skip(1));



                            continue;
                        }
                    }
                    if (argTypeDef.BaseType == null)
                    {
                        continue;
                    }
                    if (!argTypeDef.BaseType.Is(MulticastDelegateType))
                    {
                        continue;
                    }

                    // todo: marshalas umfp marshaller
                    var ufpSpecTypeRef = Module.GetType(argTypeDef.FullName + "Unmanaged");
                    Debug.Assert(ufpSpecTypeRef != null);
                    argTypes[i] = ufpSpecTypeRef;
                }


                try {
                    var ctor = funcDef.DefineConstructor(DelegateConstructorAttributes,
                                                         Module.TypeSystem.Object, Module.TypeSystem.IntPtr);
                    ctor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);

                    var method = funcDef.DefineMethod("Invoke",
                                                      DelegateInvokeMethodAttributes,
                                                      retType, argTypes);

                    method.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);

                    // todo: figure out why the attribute is jacked up
                    //method.SetCustomAttribute(callConvAttr);


                    argParams.ConsumeLinkedList((argParam, i) => {
                        var param = method.DefineParameter(i + 1, argParam.Attributes, argParam.Name);
                    });

                    return new[] { funcDef.CreateType() };
                }
                catch (Exception ex) {
                    throw new InvalidProgramException("Critical function type definition failure.", ex);
                }
            });
        }
        private Func <TypeDefinition[]> DefineClrType(ClangUnionInfo unionInfo)
        {
            if (unionInfo.Size == 0)
            {
                throw new NotImplementedException();
            }
            var unionName = unionInfo.Name;

            Debug.WriteLine($"Defining union {unionName}");
            if (TypeRedirects.TryGetValue(unionName, out var rename))
            {
                unionName = rename;
            }
            if (Module.GetType(unionName)?.Resolve() != null)
            {
                return(null);
            }
            var unionDef = Module.DefineType(unionName,
                                             PublicSealedUnionTypeAttributes, null,
                                             (int)unionInfo.Alignment,
                                             (int)unionInfo.Size);

            unionDef.SetCustomAttribute(() => new BinderGeneratedAttribute());
            //unionDef.SetCustomAttribute(StructLayoutExplicitAttributeInfo);
            var fieldParams = new LinkedList <ParameterInfo>(unionInfo.Fields.Select(f => ResolveField(f.Type, f.Name, (int)f.Offset)));

            return(() => {
                foreach (var fieldParam in fieldParams)
                {
                    fieldParam.Complete(TypeRedirects, true);
                }

                Debug.WriteLine($"Completed dependencies for union {unionName}");

                fieldParams.ConsumeLinkedList(fieldParam => {
                    var fieldName = fieldParam.Name;
                    var fieldType = fieldParam.Type;
                    if (!fieldType.IsArray)
                    {
                        var fieldDef = unionDef.DefineField(fieldName, fieldType, FieldAttributes.Public);
                        //fieldDef.SetCustomAttribute(AttributeInfo.Create(
                        //	() => new FieldOffsetAttribute(fieldParam.Position)), Module);
                        fieldDef.Offset = fieldParam.Position;
                    }
                    else
                    {
                        var arraySize = fieldParam.ArraySize;
                        fieldType = fieldType.DescendElementType();
                        var offsetPer = fieldType.SizeOf();
                        if (offsetPer == -1)
                        {
                            throw new NotImplementedException();
                        }
                        var fieldDef = unionDef.DefineField($"{fieldName}[0]",
                                                            fieldType, FieldAttributes.Private);
                        fieldDef.Offset = fieldParam.Position;
                        for (var i = 1; i < arraySize; ++i)
                        {
                            unionDef.DefineField($"{fieldName}[{i}]",
                                                 fieldType, FieldAttributes.Private)
                            .Offset = fieldParam.Position + i * offsetPer;
                        }

                        var fieldRefType = fieldType.MakeByReferenceType().Import(Module);

                        var unionGetter = unionDef.DefineMethod(fieldName,
                                                                PublicHideBySigMethodAttributes,
                                                                fieldRefType, Module.TypeSystem.Int32);

                        unionGetter.DefineParameter(1, ParameterAttributes.In, "index");
                        SetMethodInliningAttributes(unionGetter);
                        unionGetter.GenerateIL(il => {
                            var argOutOfRange = default(CecilLabel);

                            if (EmitBoundsChecks)
                            {
                                argOutOfRange = il.DefineLabel();
                                il.Emit(OpCodes.Ldarg_1);                            // index
                                il.EmitPushConst(0);                                 // underflow
                                il.Emit(OpCodes.Blt, argOutOfRange);

                                il.Emit(OpCodes.Ldarg_1);                                 // index
                                il.EmitPushConst(arraySize);                              // overflow
                                il.Emit(OpCodes.Bge, argOutOfRange);
                            }

                            il.Emit(OpCodes.Ldarg_0);                             // this
                            il.Emit(OpCodes.Ldflda, fieldDef);
                            il.Emit(OpCodes.Ldarg_1);                             // index

                            il.Emit(OpCodes.Sizeof, fieldType);
                            il.Emit(OpCodes.Mul);
                            il.Emit(OpCodes.Add);
                            if (fieldType.Resolve().IsInterface)
                            {
                                il.Emit(OpCodes.Box, fieldType);
                            }
                            il.Emit(OpCodes.Ret);

                            if (EmitBoundsChecks)
                            {
                                il.MarkLabel(argOutOfRange);
                                il.Emit(OpCodes.Newobj, ArgumentOutOfRangeCtor);
                                il.Emit(OpCodes.Throw);
                                // ReSharper disable once PossibleNullReferenceException
                                argOutOfRange.Cleanup();
                            }
                        });
                    }
                });

                return new[] { unionDef.CreateType() };
            });
        }
        private Func <TypeDefinition[]> DefineClrStructInternal(ClangStructInfo structInfo32, ClangStructInfo structInfo64)
        {
            if (structInfo32.Size == 0 && structInfo64.Size == 0)
            {
                return(DefineClrHandleStructInternal(structInfo64));
            }

            var structName = structInfo32.Name;

            Debug.WriteLine($"Defining interface and structure {structName}");

            if (TypeRedirects.TryGetValue(structName, out var rename))
            {
                structName = rename;
            }

            var interfaceName = "I" + structName;

            if (Module.GetType(interfaceName)?.Resolve() != null)
            {
                return(null);
            }

            var interfaceDef = Module.DefineType(interfaceName,
                                                 PublicInterfaceTypeAttributes);

            interfaceDef.SetCustomAttribute(() => new BinderGeneratedAttribute());

            var structDef32 = Module.DefineType(structName + "32",
                                                PublicSealedStructTypeAttributes, null,
                                                (int)structInfo32.Alignment,
                                                (int)structInfo32.Size);

            structDef32.SetCustomAttribute(() => new BinderGeneratedAttribute());

            /*
             * structDef32.SetCustomAttribute(AttributeInfo.Create(
             *      () => new StructLayoutAttribute(LayoutKind.Sequential) {
             *              Pack = (int) structInfo32.Alignment,
             *              Size = (int) structInfo32.Size
             *      }));
             */
            var structDef64 = Module.DefineType(structName + "64",
                                                PublicSealedStructTypeAttributes, null,
                                                (int)structInfo64.Alignment,
                                                (int)structInfo64.Size);

            structDef64.SetCustomAttribute(() => new BinderGeneratedAttribute());

            /*
             * structDef64.SetCustomAttribute(AttributeInfo.Create(
             *      () => new StructLayoutAttribute(LayoutKind.Sequential) {
             *              Pack = (int) structInfo64.Alignment,
             *              Size = (int) structInfo64.Size
             *      }));
             */
            _splitPointerDefs[interfaceDef.FullName] = SplitPointerGtd
                                                       .MakeGenericInstanceType(interfaceDef, structDef32, structDef64);


            if (!structDef32.Interfaces.Contains(interfaceDef))
            {
                structDef32.AddInterfaceImplementation(interfaceDef);
            }

            if (!structDef64.Interfaces.Contains(interfaceDef))
            {
                structDef64.AddInterfaceImplementation(interfaceDef);
            }

            var interfacePropDefs   = new ConcurrentDictionary <string, PropertyDefinition>();
            var interfaceMethodDefs = new ConcurrentDictionary <string, MethodDefinition>();


            var fieldParams32 = new LinkedList <ParameterInfo>(
                structInfo32.Fields.Select(f => ResolveField(f.Type, f.Name, (int)f.Offset)));
            var fieldParams64 = new LinkedList <ParameterInfo>(
                structInfo64.Fields.Select(f => ResolveField(f.Type, f.Name, (int)f.Offset)));

            var interfacePropNames = new LinkedList <string>(structInfo32.Fields
                                                             .Select(f => f.Name)
                                                             .Union(structInfo64.Fields
                                                                    .Select(f => f.Name)));

            TypeDefinition[] BuildInterfacePropsAndStructFields()
            {
                foreach (var fieldParam in fieldParams32)
                {
                    fieldParam.Complete(TypeRedirects, "32");
                }
                foreach (var fieldParam in fieldParams64)
                {
                    fieldParam.Complete(TypeRedirects, "64");
                }

                Debug.WriteLine($"Completed dependencies for interface and structure {structName}");

                interfacePropNames.ConsumeLinkedList(propName => {
                    BuildInterfaceByRefAccessor(interfaceDef, propName, _splitPointerDefs,
                                                fieldParams32.Nodes().First(f => f.Value.Name == propName),
                                                fieldParams64.Nodes().First(f => f.Value.Name == propName),
                                                out var interfacePropDef, out var interfaceMethodDef);
                    if (interfacePropDef != null)
                    {
                        interfacePropDefs[propName] = interfacePropDef;
                    }
                    if (interfaceMethodDef != null)
                    {
                        interfaceMethodDefs[propName] = interfaceMethodDef;
                    }
                });
                var interfaceType = interfaceDef.IsCreated()
                                        ? Module.GetType(interfaceDef.FullName)
                                        : interfaceDef.CreateType();

                /*
                 * if (!structDef32.ImplementedInterfaces.Contains(interfaceType))
                 *      structDef32.AddInterfaceImplementation(interfaceType);
                 * if (!structDef64.ImplementedInterfaces.Contains(interfaceType))
                 *      structDef64.AddInterfaceImplementation(interfaceType);
                 */

                void BuildStructField(ParameterInfo fieldParam, TypeDefinition structDef, int bits)
                {
                    var ptrSizeForType = bits / 8;

                    var fieldName = fieldParam.Name;
                    var fieldType = fieldParam.Type;

                    var isArray = fieldType.IsArray;

                    // never define an array element on a struct
                    // there should be a fixed buffer attribute on the field
                    if (isArray)
                    {
                        fieldType = fieldType.DescendElementType();
                    }

                    var fieldDef = PrepareAndDefineField(
                        structDef, isArray,
                        fieldParam, ref fieldType,
                        out var fieldInteriorType,
                        out var fieldTransforms);

                    TypeReference fieldRefType;

                    if (isArray)
                    {
                        fieldRefType = fieldType.MakeByReferenceType();

                        var           intfMethodInfo = interfaceType.GetMethod(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                        TypeReference intfMethodType;
                        try {
                            intfMethodType = intfMethodInfo.ReturnType;
                        }
                        catch {
                            intfMethodType = interfaceMethodDefs[fieldName].ReturnType;
                        }
                        var intfMethodElemType     = intfMethodType.DescendElementType();
                        var intfMethodInteriorType = intfMethodType.GetInteriorType(out var methodRetTransforms);

                        if (fieldRefType.Is(intfMethodType) ||
                            fieldType.IsPointer && intfMethodElemType.IsPointer ||
                            IsIntPtrOrUIntPtr(intfMethodElemType) && fieldType.SizeOf(ptrSizeForType) == ptrSizeForType ||
                            IsTypedHandle(intfMethodElemType, fieldType) ||
                            fieldInteriorType.Is(intfMethodInteriorType) && fieldTransforms.SequenceEqual(methodRetTransforms.Skip(1)))
                        {
                            //var fixedBufAttrInfo = fieldParam.AttributeInfos.First(ai => ai.Type == FixedBufferAttributeType);
                            var fixedBufSize = fieldParam.ArraySize;
                            var structGetter = structDef.DefineMethod(fieldName,
                                                                      PublicInterfaceImplementationMethodAttributes,
                                                                      intfMethodType, Module.TypeSystem.Int32);
                            structDef.DefineMethodOverride(structGetter, intfMethodInfo);
                            structGetter.DefineParameter(1, ParameterAttributes.In, "index");
                            SetMethodInliningAttributes(structGetter);
                            structGetter.GenerateIL(il => {
                                var argOutOfRange = default(CecilLabel);

                                if (EmitBoundsChecks)
                                {
                                    argOutOfRange = il.DefineLabel();
                                    il.Emit(OpCodes.Ldarg_1);                                // index
                                    il.EmitPushConst(0);                                     // underflow
                                    il.Emit(OpCodes.Blt, argOutOfRange);

                                    il.Emit(OpCodes.Ldarg_1);                                     // index
                                    il.EmitPushConst(fixedBufSize);                               // overflow
                                    il.Emit(OpCodes.Bge, argOutOfRange);
                                }

                                il.Emit(OpCodes.Ldarg_0);                                 // this
                                il.Emit(OpCodes.Ldflda, fieldDef);
                                il.Emit(OpCodes.Ldarg_1);                                 // index

                                il.Emit(OpCodes.Sizeof, fieldType);
                                il.Emit(OpCodes.Mul);
                                il.Emit(OpCodes.Add);
                                if (intfMethodType.Resolve().IsInterface)
                                {
                                    il.Emit(OpCodes.Box, fieldType);
                                }
                                il.Emit(OpCodes.Ret);

                                if (EmitBoundsChecks)
                                {
                                    il.MarkLabel(argOutOfRange);
                                    il.Emit(OpCodes.Newobj, ArgumentOutOfRangeCtor);
                                    il.Emit(OpCodes.Throw);

                                    // ReSharper disable once PossibleNullReferenceException
                                    argOutOfRange.Cleanup();
                                }
                            });
                            return;
                        }

                        throw new NotImplementedException();
                    }

                    fieldRefType = fieldType.MakeByReferenceType();

                    var propInfo = interfaceType.GetProperty(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

                    if (propInfo == null)
                    {
                        throw new NotImplementedException();
                    }

                    TypeReference propType;

                    try {
                        propType = propInfo.PropertyType;
                    }
                    catch {
                        propType = interfacePropDefs[fieldName].PropertyType;
                    }
                    var propInteriorType = propType.GetInteriorType(out var propTransforms).Resolve();

                    if (propInteriorType == null)
                    {
                        throw new NotImplementedException();
                    }


                    var propElemType = propType.DescendElementType();

                    if (fieldRefType.Is(propType) ||
                        fieldType.IsPointer && propElemType.IsPointer ||
                        IsIntPtrOrUIntPtr(propElemType) && fieldType.SizeOf(ptrSizeForType) == ptrSizeForType ||
                        IsTypedHandle(propElemType, fieldType) ||
                        fieldInteriorType.Is(propInteriorType) && fieldTransforms.SequenceEqual(propTransforms.Skip(1)))
                    {
                        /*
                         * var structProp = structDef.DefineProperty(fieldName,
                         *      PropertyAttributes.SpecialName,
                         *      propType, Type.EmptyTypes);
                         */
                        var structGetter = structDef.DefineMethod("get_" + fieldName,
                                                                  HiddenPropertyMethodAttributes
                                                                  | MethodAttributes.Virtual, propType);
                        SetMethodInliningAttributes(structGetter);

                        //structProp.SetGetMethod(structGetter);
                        structDef.DefineMethodOverride(structGetter, propInfo.Resolve().GetMethod);
                        structGetter.GenerateIL(il => {
                            il.Emit(OpCodes.Ldarg_0);                             // this
                            il.Emit(OpCodes.Ldflda, fieldDef);
                            if (propInteriorType.IsInterface)
                            {
                                il.Emit(OpCodes.Box, fieldType);
                            }
                            il.Emit(OpCodes.Ret);
                        });
                        return;
                    }

                    if (propType.Is(Module.TypeSystem.Object))
                    {
                        // TODO: boxing reference
                        throw new NotImplementedException();
                    }
                    if (propType.IsAssignableFrom(fieldType))
                    {
                        // TODO: boxed interface
                        throw new NotImplementedException();
                    }

                    throw new NotImplementedException();
                }

                fieldParams32.ConsumeLinkedList(fieldParam => BuildStructField(fieldParam, structDef32, 32));

                var structType32 = structDef32;

                fieldParams64.ConsumeLinkedList(fieldParam => BuildStructField(fieldParam, structDef64, 64));

                var structType64 = structDef64;

                return(new[] { interfaceType, structType32, structType64 });
            }

            return(BuildInterfacePropsAndStructFields);
        }