Exemple #1
0
        private static bool IsStructType(Type type, out bool isValueTuple)
        {
            isValueTuple = false;
            if (type.GetTypeInfo().IsEnum)
            {
                return(false);
            }
            if (s_mappedTypes.Contains(type))
            {
                return(false);
            }

            if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType: true))
            {
                return(false);
            }
            Type elementType;

            if (ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType: true)
                != ArgTypeInspector.EnumerableType.NotEnumerable)
            {
                return(false);
            }

            return(ArgTypeInspector.IsStructType(type, out isValueTuple));
        }
Exemple #2
0
        private static void InspectParameterType(Type parameterType, ParameterInfo parameter, out Signature?signature, out IList <ArgumentDescription> arguments)
        {
            var  argumentAttribute = parameter.GetCustomAttribute <ArgumentAttribute>(false);
            bool isValueTuple;

            arguments = new List <ArgumentDescription>();
            if (argumentAttribute != null)
            {
                signature = Signature.GetSig(parameterType, isCompileTimeType: true);
                arguments.Add(new ArgumentDescription(argumentAttribute.Name, signature.Value, parameterType));
            }
            else if (IsStructType(parameterType, out isValueTuple))
            {
                signature = null;
                var            fields            = ArgTypeInspector.GetStructFields(parameterType, isValueTuple);
                IList <string> tupleElementNames = null;
                if (isValueTuple)
                {
                    var tupleElementNamesAttribute = parameter.GetCustomAttribute <TupleElementNamesAttribute>(false);
                    if (tupleElementNamesAttribute != null)
                    {
                        tupleElementNames = tupleElementNamesAttribute.TransformNames;
                    }
                }
                int nameIdx = 0;
                for (int i = 0; i < fields.Length;)
                {
                    var field     = fields[i];
                    var fieldType = field.FieldType;
                    if (i == 7 && isValueTuple)
                    {
                        fields = ArgTypeInspector.GetStructFields(fieldType, isValueTuple);
                        i      = 0;
                    }
                    else
                    {
                        var argumentSignature = Signature.GetSig(fieldType, isCompileTimeType: true);
                        var name = tupleElementNames != null && tupleElementNames.Count > nameIdx ? tupleElementNames[nameIdx] : field.Name;
                        arguments.Add(new ArgumentDescription(name, argumentSignature, fieldType));
                        if (signature == null)
                        {
                            signature = argumentSignature;
                        }
                        else
                        {
                            signature = Signature.Concat(signature.Value, argumentSignature);
                        }
                        i++;
                        nameIdx++;
                    }
                }
            }
            else
            {
                signature = Signature.GetSig(parameterType, isCompileTimeType: true);
                arguments.Add(new ArgumentDescription("value", signature.Value, parameterType));
            }
        }
        public static MethodInfo CreateReadMethodForType(Type type) // Type Read(MessageReader)
        {
            if (type.GetTypeInfo().IsEnum)
            {
                return(s_messageReaderReadEnum.MakeGenericMethod(new[] { type }));
            }

            if (type == typeof(bool))
            {
                return(s_messageReaderReadBoolean);
            }
            else if (type == typeof(byte))
            {
                return(s_messageReaderReadByte);
            }
            else if (type == typeof(double))
            {
                return(s_messageReaderReadDouble);
            }
            else if (type == typeof(short))
            {
                return(s_messageReaderReadInt16);
            }
            else if (type == typeof(int))
            {
                return(s_messageReaderReadInt32);
            }
            else if (type == typeof(long))
            {
                return(s_messageReaderReadInt64);
            }
            else if (type == typeof(ObjectPath))
            {
                return(s_messageReaderReadObjectPath);
            }
            else if (type == typeof(Signature))
            {
                return(s_messageReaderReadSignature);
            }
            else if (type == typeof(string))
            {
                return(s_messageReaderReadString);
            }
            else if (type == typeof(float))
            {
                return(s_messageReaderReadSingle);
            }
            else if (type == typeof(ushort))
            {
                return(s_messageReaderReadUInt16);
            }
            else if (type == typeof(uint))
            {
                return(s_messageReaderReadUInt32);
            }
            else if (type == typeof(ulong))
            {
                return(s_messageReaderReadUInt64);
            }
            else if (type == typeof(object))
            {
                return(s_messageReaderReadVariant);
            }
            else if (type == typeof(IDBusObject))
            {
                return(s_messageReaderReadBusObject);
            }

            if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType: true))
            {
                return(s_messageReaderReadDBusInterface.MakeGenericMethod(new[] { type }));
            }

            Type elementType;
            var  enumerableType = ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType: true);

            if (enumerableType != ArgTypeInspector.EnumerableType.NotEnumerable)
            {
                if (enumerableType == ArgTypeInspector.EnumerableType.GenericDictionary)
                {
                    TypeInfo elementTypeInfo = elementType.GetTypeInfo();
                    Type     keyType         = elementTypeInfo.GenericTypeArguments[0];
                    Type     valueType       = elementTypeInfo.GenericTypeArguments[1];
                    return(s_messageReaderReadDictionary.MakeGenericMethod(new[] { keyType, valueType }));
                }
                else if (enumerableType == ArgTypeInspector.EnumerableType.AttributeDictionary)
                {
                    return(s_messageReaderReadDictionaryObject.MakeGenericMethod(new[] { type }));
                }
                else // Enumerable, EnumerableKeyValuePair
                {
                    return(s_messageReaderReadArray.MakeGenericMethod(new[] { elementType }));
                }
            }

            bool isValueTuple;

            if (ArgTypeInspector.IsStructType(type, out isValueTuple))
            {
                if (isValueTuple)
                {
                    return(s_messageReaderReadValueTupleStruct.MakeGenericMethod(type));
                }
                else
                {
                    return(s_messageReaderReadStruct.MakeGenericMethod(type));
                }
            }

            if (ArgTypeInspector.IsSafeHandleType(type))
            {
                return(s_messageReaderReadSafeHandle.MakeGenericMethod(type));
            }

            throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'");
        }
Exemple #4
0
        private static void AddInterfaceDescription(Type type, DBusInterfaceAttribute interfaceAttribute, List <InterfaceDescription> interfaces)
        {
            if (interfaces.Any(interf => interf.Name == interfaceAttribute.Name))
            {
                throw new ArgumentException($"DBus interface {interfaceAttribute.Name} is inherited multiple times");
            }

            IList <MethodDescription>   methods                 = null;
            IList <SignalDescription>   signals                 = null;
            IList <PropertyDescription> properties              = null;
            MethodDescription           propertyGetMethod       = null;
            MethodDescription           propertySetMethod       = null;
            MethodDescription           propertyGetAllMethod    = null;
            SignalDescription           propertiesChangedSignal = null;
            Type propertyType = interfaceAttribute.PropertyType;
            Type elementType;

            if (propertyType != null && ArgTypeInspector.InspectEnumerableType(propertyType, out elementType, isCompileTimeType: true) != ArgTypeInspector.EnumerableType.AttributeDictionary)
            {
                throw new ArgumentException($"Property type '{propertyType.FullName}' does not have the '{typeof(DictionaryAttribute).FullName}' attribute");
            }

            foreach (var member in type.GetMethods())
            {
                string memberName = member.ToString();
                if (!member.Name.EndsWith("Async", StringComparison.Ordinal))
                {
                    throw new ArgumentException($"{memberName} does not end with 'Async'");
                }
                var isSignal = member.Name.StartsWith("Watch", StringComparison.Ordinal);
                if (isSignal)
                {
                    if (member.ReturnType != s_signalReturnType)
                    {
                        throw new ArgumentException($"Signal {memberName} does not return 'Task<IDisposable>'");
                    }

                    var name = member.Name.Substring(5, member.Name.Length - 10);
                    if (name.Length == 0)
                    {
                        throw new ArgumentException($"Signal {memberName} has an empty name");
                    }

                    Signature?parameterSignature          = null;
                    IList <ArgumentDescription> arguments = null;
                    var  parameters           = member.GetParameters();
                    var  actionParameter      = parameters.Length > 0 ? parameters[0] : null;
                    Type parameterType        = null;
                    bool validActionParameter = false;
                    if (actionParameter != null)
                    {
                        if (actionParameter.ParameterType == s_exceptionActionType)
                        {
                            // actionParameter is missing
                        }
                        else if (actionParameter.ParameterType == s_emptyActionType)
                        {
                            validActionParameter = true;
                        }
                        else if (actionParameter.ParameterType.GetTypeInfo().IsGenericType &&
                                 actionParameter.ParameterType.GetGenericTypeDefinition() == s_singleParameterActionType)
                        {
                            validActionParameter = true;
                            parameterType        = actionParameter.ParameterType.GetGenericArguments()[0];
                            InspectParameterType(parameterType, actionParameter, out parameterSignature, out arguments);
                        }
                    }
                    if (parameters.Length > 0 && parameters[parameters.Length - 1].ParameterType == s_cancellationTokenType)
                    {
                        throw new NotSupportedException($"Signal {memberName} does not support cancellation. See https://github.com/tmds/Tmds.DBus/issues/15.");
                    }
                    var  lastParameter = parameters.Length > 0 ? parameters[parameters.Length - 1] : null;
                    bool hasOnError    = lastParameter?.ParameterType == s_exceptionActionType;
                    if (!validActionParameter || parameters.Length != 1 + (hasOnError ? 1 : 0))
                    {
                        throw new ArgumentException($"Signal {memberName} must accept an argument of Type 'Action'/'Action<>' and optional argument of Type 'Action<Exception>'");
                    }

                    var signal = new SignalDescription(member, name, actionParameter.ParameterType, parameterType, parameterSignature, arguments, hasOnError);
                    if (member.Name == interfaceAttribute.WatchPropertiesMethod)
                    {
                        if (propertiesChangedSignal != null)
                        {
                            throw new ArgumentException($"Multiple property changes signals are declared: {memberName}, {propertyGetMethod.MethodInfo.ToString()}");
                        }
                        propertiesChangedSignal = signal;
                        if (propertiesChangedSignal.SignalSignature != s_propertiesChangedSignature)
                        {
                            throw new ArgumentException($"PropertiesChanged signal {memberName} must accept an Action<T> where T is a struct with an IDictionary<string, object> and an string[] field");
                        }
                    }
                    else
                    {
                        signals = signals ?? new List <SignalDescription>();
                        signals.Add(signal);
                    }
                }
                else
                {
                    var name = member.Name.Substring(0, member.Name.Length - 5);
                    if (name.Length == 0)
                    {
                        throw new ArgumentException($"DBus Method {memberName} has an empty name");
                    }

                    IList <ArgumentDescription> outArguments = null;
                    Signature?outSignature  = null;
                    var       taskParameter = member.ReturnType;
                    Type      outType       = null;
                    bool      valid         = false;
                    bool      isGenericOut  = false;
                    if (taskParameter != null)
                    {
                        if (taskParameter == s_emptyTaskType)
                        {
                            valid   = true;
                            outType = null;
                        }
                        else if (taskParameter.GetTypeInfo().IsGenericType &&
                                 taskParameter.GetGenericTypeDefinition() == s_parameterTaskType)
                        {
                            valid   = true;
                            outType = taskParameter.GetGenericArguments()[0];
                            if (outType.IsGenericParameter)
                            {
                                outType      = s_objectType;
                                isGenericOut = true;
                            }
                            InspectParameterType(outType, member.ReturnParameter, out outSignature, out outArguments);
                        }
                    }
                    if (!valid)
                    {
                        throw new ArgumentException($"DBus Method {memberName} does not return 'Task'/'Task<>'");
                    }

                    IList <ArgumentDescription> inArguments = null;
                    Signature?inSignature = null;
                    var       parameters  = member.GetParameters();
                    if (parameters.Length > 0 && parameters[parameters.Length - 1].ParameterType == s_cancellationTokenType)
                    {
                        throw new NotSupportedException($"DBus Method {memberName} does not support cancellation. See https://github.com/tmds/Tmds.DBus/issues/15.");
                    }

                    for (int i = 0; i < parameters.Length; i++)
                    {
                        var param          = parameters[i];
                        var parameterType  = param.ParameterType;
                        var paramSignature = Signature.GetSig(parameterType, isCompileTimeType: true);
                        if (inSignature == null)
                        {
                            inSignature = paramSignature;
                        }
                        else
                        {
                            inSignature = Signature.Concat(inSignature.Value, paramSignature);
                        }
                        inArguments = inArguments ?? new List <ArgumentDescription>();
                        var argumentAttribute = param.GetCustomAttribute <ArgumentAttribute>(false);
                        var argName           = argumentAttribute != null ? argumentAttribute.Name : param.Name;
                        inArguments.Add(new ArgumentDescription(argName, paramSignature, parameterType));
                    }

                    var methodDescription = new MethodDescription(member, name, inArguments, inSignature, outType, isGenericOut, outSignature, outArguments);
                    if (member.Name == interfaceAttribute.GetPropertyMethod)
                    {
                        if (propertyGetMethod != null)
                        {
                            throw new ArgumentException($"Multiple property Get methods are declared: {memberName}, {propertyGetMethod.MethodInfo.ToString()}");
                        }
                        propertyGetMethod = methodDescription;
                        if ((propertyGetMethod.InSignature != Signature.StringSig) ||
                            (propertyGetMethod.OutSignature != Signature.VariantSig))
                        {
                            throw new ArgumentException($"Property Get method {memberName} must accept a 'string' parameter and return 'Task<object>'");
                        }
                    }
                    else if (member.Name == interfaceAttribute.GetAllPropertiesMethod)
                    {
                        if (propertyGetAllMethod != null)
                        {
                            throw new ArgumentException($"Multiple property GetAll are declared: {memberName}, {propertyGetAllMethod.MethodInfo.ToString()}");
                        }
                        propertyGetAllMethod = methodDescription;
                        if ((propertyGetAllMethod.InArguments.Count != 0) ||
                            (propertyGetAllMethod.OutSignature != s_getAllOutSignature))
                        {
                            throw new ArgumentException($"Property GetAll method {memberName} must accept no parameters and return 'Task<IDictionary<string, object>>'");
                        }
                        if (propertyType == null)
                        {
                            if (ArgTypeInspector.InspectEnumerableType(methodDescription.OutType, out elementType, isCompileTimeType: true) == ArgTypeInspector.EnumerableType.AttributeDictionary)
                            {
                                propertyType = methodDescription.OutType;
                            }
                        }
                    }
                    else if (member.Name == interfaceAttribute.SetPropertyMethod)
                    {
                        if (propertySetMethod != null)
                        {
                            throw new ArgumentException($"Multiple property Set are declared: {memberName}, {propertySetMethod.MethodInfo.ToString()}");
                        }
                        propertySetMethod = methodDescription;
                        if ((propertySetMethod.InArguments?.Count != 2 || propertySetMethod.InArguments[0].Type != s_stringType || propertySetMethod.InArguments[1].Type != s_objectType) ||
                            (propertySetMethod.OutArguments.Count != 0))
                        {
                            throw new ArgumentException($"Property Set method {memberName} must accept a 'string' and 'object' parameter and return 'Task'");
                        }
                    }
                    else
                    {
                        methods = methods ?? new List <MethodDescription>();
                        methods.Add(methodDescription);
                    }
                }
            }
            if (propertyType != null)
            {
                var fields = propertyType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
                foreach (var field in fields)
                {
                    string propertyName;
                    Type   fieldType;
                    PropertyTypeInspector.InspectField(field, out propertyName, out fieldType);
                    var propertySignature = Signature.GetSig(fieldType, isCompileTimeType: true);
                    var propertyAccess    = field.GetCustomAttribute <PropertyAttribute>()?.Access ?? PropertyAccess.ReadWrite;
                    var description       = new PropertyDescription(propertyName, propertySignature, propertyAccess);
                    properties = properties ?? new List <PropertyDescription>();
                    properties.Add(description);
                }
            }
            interfaces.Add(new InterfaceDescription(type, interfaceAttribute.Name, methods, signals, properties,
                                                    propertyGetMethod, propertyGetAllMethod, propertySetMethod, propertiesChangedSignal));
        }
        public static MethodInfo CreateWriteMethodForType(Type type, bool isCompileTimeType) // void Write(MessageWriter, T)
        {
            if (type.GetTypeInfo().IsEnum)
            {
                type = Enum.GetUnderlyingType(type);
            }

            if (type == typeof(bool))
            {
                return(s_messageWriterWriteBoolean);
            }
            else if (type == typeof(byte))
            {
                return(s_messageWriterWriteByte);
            }
            else if (type == typeof(double))
            {
                return(s_messageWriterWriteDouble);
            }
            else if (type == typeof(short))
            {
                return(s_messageWriterWriteInt16);
            }
            else if (type == typeof(int))
            {
                return(s_messageWriterWriteInt32);
            }
            else if (type == typeof(long))
            {
                return(s_messageWriterWriteInt64);
            }
            else if (type == typeof(ObjectPath))
            {
                return(s_messageWriterWriteObjectPath);
            }
            else if (type == typeof(Signature))
            {
                return(s_messageWriterWriteSignature);
            }
            else if (type == typeof(string))
            {
                return(s_messageWriterWriteString);
            }
            else if (type == typeof(float))
            {
                return(s_messageWriterWriteSingle);
            }
            else if (type == typeof(ushort))
            {
                return(s_messageWriterWriteUInt16);
            }
            else if (type == typeof(uint))
            {
                return(s_messageWriterWriteUInt32);
            }
            else if (type == typeof(ulong))
            {
                return(s_messageWriterWriteUInt64);
            }
            else if (type == typeof(object))
            {
                return(s_messageWriterWriteVariant);
            }
            else if (type == typeof(IDBusObject))
            {
                return(s_messageWriterWriteBusObject);
            }

            if (ArgTypeInspector.IsDBusObjectType(type, isCompileTimeType))
            {
                return(s_messageWriterWriteBusObject);
            }

            Type elementType;
            var  enumerableType = ArgTypeInspector.InspectEnumerableType(type, out elementType, isCompileTimeType);

            if (enumerableType != ArgTypeInspector.EnumerableType.NotEnumerable)
            {
                if ((enumerableType == ArgTypeInspector.EnumerableType.EnumerableKeyValuePair) ||
                    (enumerableType == ArgTypeInspector.EnumerableType.GenericDictionary))
                {
                    return(s_messageWriterWriteDict.MakeGenericMethod(elementType.GenericTypeArguments));
                }
                else if (enumerableType == ArgTypeInspector.EnumerableType.AttributeDictionary)
                {
                    return(s_messageWriterWriteDictionaryObject.MakeGenericMethod(type));
                }
                else // Enumerable
                {
                    return(s_messageWriterWriteArray.MakeGenericMethod(new[] { elementType }));
                }
            }

            bool isValueTuple;

            if (ArgTypeInspector.IsStructType(type, out isValueTuple))
            {
                if (isValueTuple)
                {
                    return(s_messageWriterWriteValueTupleStruct.MakeGenericMethod(type));
                }
                else
                {
                    return(s_messageWriterWriteStruct.MakeGenericMethod(type));
                }
            }

            if (ArgTypeInspector.IsSafeHandleType(type))
            {
                return(s_messageWriterWriteSafeHandle);
            }

            throw new ArgumentException($"Cannot (de)serialize Type '{type.FullName}'");
        }