private MethodInfo GenSendSignal(SignalDescription signalDescription, bool isPropertiesChangedSignal)
        {
            var key    = $"{signalDescription.Interface.Name}.{signalDescription.Name}";
            var method = _typeBuilder.DefineMethod($"Emit{key}".Replace('.', '_'), MethodAttributes.Private, null,
                                                   signalDescription.SignalType == null ? Type.EmptyTypes : new[] { signalDescription.SignalType });

            var ilg = method.GetILGenerator();

            ilg.Emit(OpCodes.Ldarg_0);
            if (isPropertiesChangedSignal)
            {
                ilg.Emit(OpCodes.Ldstr, "org.freedesktop.DBus.Properties");
                ilg.Emit(OpCodes.Ldstr, "PropertiesChanged");
            }
            else
            {
                ilg.Emit(OpCodes.Ldstr, signalDescription.Interface.Name);
                ilg.Emit(OpCodes.Ldstr, signalDescription.Name);
            }

            if (signalDescription.SignalType == null)
            {
                ilg.Emit(OpCodes.Call, s_emitVoidSignal);
            }
            else
            {
                // Signature
                if (isPropertiesChangedSignal)
                {
                    ilg.Emit(OpCodes.Ldstr, "sa{sv}as");
                    ilg.Emit(OpCodes.Newobj, s_signatureConstructor);
                    ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor);
                }
                else if (signalDescription.SignalSignature.HasValue)
                {
                    ilg.Emit(OpCodes.Ldstr, signalDescription.SignalSignature.Value.Value);
                    ilg.Emit(OpCodes.Newobj, s_signatureConstructor);
                    ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor);
                }
                else
                {
                    LocalBuilder signature = ilg.DeclareLocal(s_nullableSignatureType);
                    ilg.Emit(OpCodes.Ldloca_S, signature);
                    ilg.Emit(OpCodes.Initobj, s_nullableSignatureType);
                    ilg.Emit(OpCodes.Ldloc, signature);
                }

                // Writer
                ilg.Emit(OpCodes.Newobj, s_messageWriterConstructor);

                if (isPropertiesChangedSignal)
                {
                    ilg.Emit(OpCodes.Dup);
                    ilg.Emit(OpCodes.Ldstr, signalDescription.Interface.Name);
                    ilg.Emit(OpCodes.Call, s_writerWriteString);
                    ilg.Emit(OpCodes.Dup);
                    ilg.Emit(OpCodes.Call, s_writerSetSkipNextStructPadding);
                }
                ilg.Emit(OpCodes.Dup);
                ilg.Emit(OpCodes.Ldarg_1);
                ilg.Emit(OpCodes.Call, WriteMethodFactory.CreateWriteMethodForType(signalDescription.SignalType, isCompileTimeType: true));

                ilg.Emit(OpCodes.Call, s_emitNonVoidSignal);
            }

            ilg.Emit(OpCodes.Ret);

            return(method);
        }
        private MethodInfo GenMethodHandler(string key, MethodDescription dbusMethod, bool propertyMethod)
        {
            // Task<Message> MethodCall(object o(Ldarg_1), Message methodCall(Ldarg_2), IProxyFactory(Ldarg_3));

            string methodName = $"Handle{key}".Replace('.', '_');
            var    method     = _typeBuilder.DefineMethod(methodName, MethodAttributes.Private, s_taskOfMessageType, s_methodHandlerParameterTypes);

            var ilg = method.GetILGenerator();

            // call CreateReply

            // this
            ilg.Emit(OpCodes.Ldarg_0);
            // Message
            ilg.Emit(OpCodes.Ldarg_2);
            // Task = (IDbusInterface)object.CallMethod(arguments)
            {
                // (IIinterface)object
                ilg.Emit(OpCodes.Ldarg_1);
                ilg.Emit(OpCodes.Castclass, dbusMethod.Interface.Type);

                // Arguments
                if (dbusMethod.InArguments.Count != 0)
                {
                    // create reader for reading the arguments
                    ilg.Emit(OpCodes.Ldarg_2);                            // message
                    ilg.Emit(OpCodes.Ldarg_3);                            // IProxyFactory
                    LocalBuilder reader = ilg.DeclareLocal(s_messageReaderType);
                    ilg.Emit(OpCodes.Newobj, s_messageReaderConstructor); // new MessageReader(message, proxyFactory)
                    ilg.Emit(OpCodes.Stloc, reader);

                    if (propertyMethod)
                    {
                        ilg.Emit(OpCodes.Ldloc, reader);
                        ilg.Emit(OpCodes.Call, s_readerSkipString);
                    }

                    foreach (var argument in dbusMethod.InArguments)
                    {
                        Type parameterType = argument.Type;
                        ilg.Emit(OpCodes.Ldloc, reader);
                        ilg.Emit(OpCodes.Call, ReadMethodFactory.CreateReadMethodForType(parameterType));
                    }
                }

                // Call method
                ilg.Emit(OpCodes.Callvirt, dbusMethod.MethodInfo);
            }

            if (dbusMethod.OutType != null)
            {
                //  Action<MessageWriter, T>
                ilg.Emit(OpCodes.Ldnull);
                ilg.Emit(OpCodes.Ldftn, WriteMethodFactory.CreateWriteMethodForType(dbusMethod.OutType, isCompileTimeType: true));
                var actionConstructor = s_action2GenericType.MakeGenericType(new[] { s_messageWriterType, dbusMethod.OutType }).GetConstructors()[0];
                ilg.Emit(OpCodes.Newobj, actionConstructor);

                // signature
                if (dbusMethod.OutSignature.HasValue)
                {
                    ilg.Emit(OpCodes.Ldstr, dbusMethod.OutSignature.Value.Value);
                    ilg.Emit(OpCodes.Newobj, s_signatureConstructor);
                    ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor);
                }
                else
                {
                    LocalBuilder signature = ilg.DeclareLocal(s_nullableSignatureType);
                    ilg.Emit(OpCodes.Ldloca_S, signature);
                    ilg.Emit(OpCodes.Initobj, s_nullableSignatureType);
                    ilg.Emit(OpCodes.Ldloc, signature);
                }

                // CreateReply
                ilg.Emit(OpCodes.Call, s_createNonVoidReply.MakeGenericMethod(new[] { dbusMethod.OutType }));
            }
            else
            {
                // CreateReply
                ilg.Emit(OpCodes.Call, s_createVoidReply);
            }

            ilg.Emit(OpCodes.Ret);

            return(method);
        }
        private void ImplementMethod(MethodDescription methodDescription, bool propertyMethod)
        {
            var method = _typeBuilder.ImplementInterfaceMethod(methodDescription.MethodInfo);

            ILGenerator ilg = method.GetILGenerator();

            //CallMethod(...)

            // BusObject (this)
            ilg.Emit(OpCodes.Ldarg_0);
            ilg.Emit(OpCodes.Castclass, s_dbusObjectProxyType);

            // Interface
            if (propertyMethod)
            {
                ilg.Emit(OpCodes.Ldstr, "org.freedesktop.DBus.Properties");
            }
            else
            {
                ilg.Emit(OpCodes.Ldstr, methodDescription.Interface.Name);
            }

            // Member
            ilg.Emit(OpCodes.Ldstr, methodDescription.Name);

            // Signature
            if (methodDescription.InSignature.HasValue || propertyMethod)
            {
                string inSig = methodDescription.InSignature?.Value ?? string.Empty;
                if (propertyMethod)
                {
                    inSig = "s" + inSig;
                }
                ilg.Emit(OpCodes.Ldstr, inSig);
                ilg.Emit(OpCodes.Newobj, s_signatureConstructor);
                ilg.Emit(OpCodes.Newobj, s_nullableSignatureConstructor);
            }
            else
            {
                LocalBuilder signature = ilg.DeclareLocal(s_nullableSignatureType);
                ilg.Emit(OpCodes.Ldloca_S, signature);
                ilg.Emit(OpCodes.Initobj, s_nullableSignatureType);
                ilg.Emit(OpCodes.Ldloc, signature);
            }

            // MessageWriter
            var argumentOffset = 1; //offset by one to account for "this"

            if (methodDescription.InArguments.Count != 0 || propertyMethod)
            {
                LocalBuilder writer = ilg.DeclareLocal(s_messageWriterType);
                ilg.Emit(OpCodes.Newobj, s_messageWriterConstructor);
                ilg.Emit(OpCodes.Stloc, writer);

                if (propertyMethod)
                {
                    // Write parameter
                    Type parameterType = typeof(string);
                    ilg.Emit(OpCodes.Ldloc, writer);
                    ilg.Emit(OpCodes.Ldstr, methodDescription.Interface.Name);
                    ilg.Emit(OpCodes.Call, WriteMethodFactory.CreateWriteMethodForType(parameterType, isCompileTimeType: true));
                }

                foreach (var argument in methodDescription.InArguments)
                {
                    // Write parameter
                    Type parameterType = argument.Type;
                    ilg.Emit(OpCodes.Ldloc, writer);
                    ilg.Emit(OpCodes.Ldarg, argumentOffset);
                    ilg.Emit(OpCodes.Call, WriteMethodFactory.CreateWriteMethodForType(parameterType, isCompileTimeType: true));

                    argumentOffset++;
                }

                ilg.Emit(OpCodes.Ldloc, writer);
            }
            else
            {
                ilg.Emit(OpCodes.Ldnull);
            }

            if (methodDescription.OutType != null)
            {
                // CallMethod
                if (methodDescription.IsGenericOut)
                {
                    Type genericParameter = method.GetGenericArguments()[0];
                    ilg.Emit(OpCodes.Call, s_callGenericOutMethod.MakeGenericMethod(new[] { genericParameter }));
                }
                else
                {
                    // ReadMethodDelegate
                    ilg.Emit(OpCodes.Ldnull);
                    ilg.Emit(OpCodes.Ldftn, ReadMethodFactory.CreateReadMethodForType(methodDescription.OutType));
                    var readDelegateConstructor = s_readMethodDelegateGenericType.MakeGenericType(new[] { methodDescription.OutType }).GetConstructors()[0];
                    ilg.Emit(OpCodes.Newobj, readDelegateConstructor);

                    ilg.Emit(OpCodes.Call, s_callNonVoidMethod.MakeGenericMethod(new[] { methodDescription.OutType }));
                }
            }
            else
            {
                // CallMethod
                ilg.Emit(OpCodes.Call, s_callVoidMethod);
            }

            ilg.Emit(OpCodes.Ret);
        }