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);
                }
            });
        }
Beispiel #2
0
        private void BuildInterfaceByRefAccessor(
            TypeDefinition interfaceDef, string propName,
            ConcurrentDictionary <string, GenericInstanceType> splitPointerDefs,
            LinkedListNode <ParameterInfo> fieldInfo32,
            LinkedListNode <ParameterInfo> fieldInfo64,
            out PropertyDefinition interfacePropDef,
            out MethodDefinition interfaceMethodDef
            )
        {
            var module = interfaceDef.Module;

            var fieldType32 = fieldInfo32.Value.Type;
            var fieldType64 = fieldInfo64.Value.Type;

            interfacePropDef   = null;
            interfaceMethodDef = null;

            var intType  = Module.TypeSystem.Int32;
            var longType = Module.TypeSystem.Int64;

            if (fieldType32.Is(intType) && fieldType64.Is(longType))
            {
                // IntPtr
                var propType    = Module.TypeSystem.IntPtr;
                var propRefType = propType.MakeByReferenceType();

                interfacePropDef =
                    DefineInterfaceGetProperty(interfaceDef, propRefType, propName);
                return;
            }
            var uIntType  = Module.TypeSystem.UInt32;
            var uLongType = Module.TypeSystem.UInt64;

            if (fieldType32.Is(uIntType) && fieldType64.Is(uLongType))
            {
                // UIntPtr
                var propType    = Module.TypeSystem.UIntPtr;
                var propRefType = propType.MakeByReferenceType();

                interfacePropDef =
                    DefineInterfaceGetProperty(interfaceDef, propRefType, propName);
                return;
            }

            if (fieldType32.Is(fieldType64) && fieldType32.IsDirect())
            {
                // same type
                var propType    = fieldType32;
                var propRefType = propType.MakeByReferenceType();

                interfacePropDef =
                    DefineInterfaceGetProperty(interfaceDef, propRefType, propName);
                return;
            }


            var fieldElemType32 = fieldType32.GetTypePointedTo(out LinkedList <CecilExtensions.TypeReferenceTransform> transforms32);
            var fieldElemType64 = fieldType64.GetTypePointedTo(out LinkedList <CecilExtensions.TypeReferenceTransform> transforms64);

            var pointerDepth32 = transforms32.Count;
            var pointerDepth64 = transforms64.Count;

            if (Math.Abs(pointerDepth32 - pointerDepth64) > 1)
            {
                throw new NotSupportedException();
            }

            if (fieldType32.IsPointer && fieldType64.Is(intType))
            {
                // 32-bit handle
                var handleType = HandleInt32Gtd.MakeGenericInstanceType(fieldElemType32);

                fieldInfo32.Value.Type = handleType;
                fieldInfo64.Value.Type = handleType;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName);
                return;
            }
            if (fieldType32.IsPointer && fieldType64.Is(uIntType))
            {
                // 32-bit handle
                var handleType = HandleUInt32Gtd.MakeGenericInstanceType(fieldElemType32);

                fieldInfo32.Value.Type = handleType;
                fieldInfo64.Value.Type = handleType;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName);
                return;
            }
            if (fieldType32.Is(longType) && fieldType64.IsPointer)
            {
                // 64-bit handle
                var handleType = HandleInt64Gtd.MakeGenericInstanceType(fieldElemType64);

                fieldInfo32.Value.Type = handleType;
                fieldInfo64.Value.Type = handleType;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName);
                return;
            }
            if (fieldType32.Is(uLongType) && fieldType64.IsPointer)
            {
                // 64-bit handle
                var handleType = HandleUInt64Gtd.MakeGenericInstanceType(fieldElemType64);

                fieldInfo32.Value.Type = handleType;
                fieldInfo64.Value.Type = handleType;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName);
                return;
            }

            if (fieldElemType64.Is(intType) && IsHandleType(fieldElemType32))
            {
                // 32-bit handle
                TypeReference handleType = HandleInt32Gtd.MakeGenericInstanceType(fieldElemType32);

                var updatedFieldType32 = handleType;
                foreach (var transform in transforms32)
                {
                    transform(ref updatedFieldType32);
                }

                fieldInfo32.Value.Type = updatedFieldType32;

                var updatedFieldType64 = handleType;
                foreach (var transform in transforms64)
                {
                    transform(ref updatedFieldType64);
                }

                fieldInfo64.Value.Type = updatedFieldType64;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName, transforms32.Skip(1));
                return;
            }
            if (fieldElemType64.Is(uIntType) && IsHandleType(fieldElemType32))
            {
                // 32-bit handle
                TypeReference handleType = HandleUInt32Gtd.MakeGenericInstanceType(fieldElemType32);

                var updatedFieldType32 = handleType;
                foreach (var transform in transforms32)
                {
                    transform(ref updatedFieldType32);
                }

                fieldInfo32.Value.Type = updatedFieldType32;

                var updatedFieldType64 = handleType;
                foreach (var transform in transforms64)
                {
                    transform(ref updatedFieldType64);
                }

                fieldInfo64.Value.Type = updatedFieldType64;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName, transforms32.Skip(1));
                return;
            }
            if (fieldElemType32.Is(longType) && IsHandleType(fieldElemType64))
            {
                // 64-bit handle
                TypeReference handleType = HandleInt64Gtd.MakeGenericInstanceType(fieldElemType64);

                var updatedFieldType32 = handleType;
                foreach (var transform in transforms32)
                {
                    transform(ref updatedFieldType32);
                }

                fieldInfo32.Value.Type = updatedFieldType32;

                var updatedFieldType64 = handleType;
                foreach (var transform in transforms64)
                {
                    transform(ref updatedFieldType64);
                }

                fieldInfo32.Value.Type = updatedFieldType32;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName, transforms64.Skip(1));
                return;
            }
            if (fieldElemType32.Is(uLongType) && IsHandleType(fieldElemType64))
            {
                // 64-bit handle
                TypeReference handleType = HandleUInt64Gtd.MakeGenericInstanceType(fieldElemType64);

                var updatedFieldType32 = handleType;
                foreach (var transform in transforms32)
                {
                    transform(ref updatedFieldType32);
                }

                fieldInfo32.Value.Type = updatedFieldType32;

                var updatedFieldType64 = handleType;
                foreach (var transform in transforms64)
                {
                    transform(ref updatedFieldType64);
                }

                fieldInfo32.Value.Type = updatedFieldType32;

                interfacePropDef =
                    DefineInterfaceGetHandleProperty(interfaceDef, handleType, propName, transforms64.Skip(1));
                return;
            }

            if (fieldType32.IsPointer && fieldType64.IsPointer && fieldElemType32.Is(fieldElemType64))
            {
                if (fieldElemType32.SizeOf() == 0)
                {
                    throw new NotImplementedException();
                }

                TypeReference propRefType = fieldElemType32;
                foreach (var transform in transforms32)
                {
                    transform(ref propRefType);
                }
                propRefType = propRefType.MakeByReferenceType();

                interfacePropDef =
                    DefineInterfaceGetProperty(interfaceDef, propRefType, propName);
                return;
            }

            var interiorType32 = fieldElemType32.FindInteriorType(ref transforms32);
            var interiorType64 = fieldElemType64.FindInteriorType(ref transforms64);

            if (!transforms32.SequenceEqual(transforms64))
            {
                throw new NotImplementedException();
            }

            if (fieldType32.Is(fieldType64) && fieldType32.IsArray)
            {
                // ref index implementation

                if (interiorType32.SizeOf() == 0)
                {
                    throw new NotImplementedException();
                }

                var fieldElemType = fieldType64.DescendElementType();

                if (IsHandleType(interiorType64))
                {
                    var           handleType     = HandleUIntPtrGtd.MakeGenericInstanceType(interiorType64);
                    TypeReference handleElemType = handleType;
                    foreach (var transform in transforms64.Take(transforms64.Count - 2))
                    {
                        transform(ref handleElemType);
                    }
                    var handleElemRefType = handleElemType.MakeByReferenceType();
                    interfaceMethodDef =
                        DefineInterfaceGetByIndexMethod(interfaceDef, handleElemRefType, propName);
                    return;
                }


                var propElemRefType = fieldElemType.MakeByReferenceType();

                interfaceMethodDef =
                    DefineInterfaceGetByIndexMethod(interfaceDef, propElemRefType, propName);
                return;
            }
            if (fieldType32.IsArray || fieldType64.IsArray)
            {
                // TODO: ...
                throw new NotImplementedException();
            }
            var fieldInterfaces32 = interiorType32.GetInterfaces();
            var fieldInterfaces64 = interiorType64.GetInterfaces();
            var commonInterfaces  = fieldInterfaces32.Intersect(fieldInterfaces64).ToArray();

            if (commonInterfaces.Length == 0)
            {
                // object, boxing reference
                throw new NotImplementedException();
                //DefineInterfaceGetSetProperty(interfaceDef, propType, propName);
            }
            if (commonInterfaces.Length > 1)
            {
                // TODO: multiple common interface, boxing reference
                throw new NotImplementedException();
            }

            /* commonInterfaces.Length == 1 */

            if (transforms32.First() == CecilExtensions.MakePointerType)
            {
                // common interface, boxing reference
                var commonInterface = commonInterfaces.First();
                splitPointerDefs.TryGetValue(commonInterface.FullName, out var splitPointerDef);
                var propType = splitPointerDef
                               ?? SplitPointerGtd.MakeGenericInstanceType(commonInterface, interiorType32, interiorType64).Import(module);
                foreach (var transform in transforms32.Skip(1))
                {
                    transform(ref propType);
                }
                var propRefType = propType.MakeByReferenceType();
                interfacePropDef = DefineInterfaceGetProperty(interfaceDef, propRefType, propName);
                return;
            }
            throw new NotImplementedException();
        }
        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);
        }