private static string generatePropertyEncodeImplementation(Type type) { var propertyEncoder = new StringBuilder(); propertyEncoder.Append(@" public void EncodeProperties(global::Dbus.Encoder sendBody) { sendBody.AddArray(() => {"); foreach (var property in type.GetTypeInfo().GetProperties()) { propertyEncoder.Append(@" sendBody.StartCompoundValue(); sendBody.Add(""" + property.Name + @"""); Encode" + property.Name + @"(sendBody);"); } propertyEncoder.Append(@" }, storesCompoundValues: true); } public void EncodeProperty(global::Dbus.Encoder sendBody, string propertyName) { switch (propertyName) {"); foreach (var property in type.GetTypeInfo().GetProperties()) { propertyEncoder.Append(@" case """ + property.Name + @""": Encode" + property.Name + @"(sendBody); break;"); } propertyEncoder.Append(@" default: throw new global::Dbus.DbusException( global::Dbus.DbusException.CreateErrorName(""UnknownProperty""), ""No such Property: "" + propertyName ); } }"); foreach (var property in type.GetTypeInfo().GetProperties()) { var encoder = new EncoderGenerator("sendBody"); encoder.AddVariant("value", property.PropertyType); propertyEncoder.Append(@" private void Encode" + property.Name + @"(global::Dbus.Encoder sendBody) { var value = target." + property.Name + @"; " + encoder.Result + @" }"); } return(propertyEncoder.ToString()); }
private static string generateMethodImplementation(MethodInfo methodInfo, string interfaceName) { if (!methodInfo.Name.EndsWith("Async")) { throw new InvalidOperationException($"The method '{methodInfo.Name}' does not end with 'Async'"); } var callName = methodInfo.Name.Substring(0, methodInfo.Name.Length - "Async".Length); var returnType = methodInfo.ReturnType; var returnTypeString = BuildTypeString(returnType); if (returnTypeString != "global::System.Threading.Tasks.Task" && !returnTypeString.StartsWith("global::System.Threading.Tasks.Task<")) { throw new InvalidOperationException($"The method '{methodInfo.Name}' does not return a Task type"); } var isProperty = propertyName.IsMatch(callName); isProperty &= methodInfo.GetCustomAttribute <DbusMethodAttribute>() == null; var parameters = methodInfo.GetParameters(); if (isProperty) { if (callName.StartsWith("Get")) { if (parameters.Length == 0) { isProperty &= true; } else if (parameters[0].ParameterType == typeof(CancellationToken)) { isProperty &= true; } else { isProperty = false; } } else if (callName.StartsWith("Set")) { if (parameters.Length == 1 && parameters[0].ParameterType != typeof(CancellationToken)) { isProperty &= true; } else if (parameters.Length == 2 && parameters[1].ParameterType == typeof(CancellationToken)) { isProperty &= true; } else { isProperty = false; } } } var encoder = new EncoderGenerator("sendBody"); var cancellationTokenName = "default(global::System.Threading.CancellationToken)"; if (parameters.Length > 0 || isProperty) { if (isProperty) { encoder.Add($@"""{interfaceName}""", typeof(string)); encoder.Add($@"""{callName.Substring(3 /* "Get" or "Set" */)}""", typeof(string)); interfaceName = "org.freedesktop.DBus.Properties"; callName = callName.Substring(0, 3); // "Get" or "Set" foreach (var parameter in parameters) { if (parameter.ParameterType == typeof(CancellationToken)) { cancellationTokenName = parameter.Name !; } else { encoder.AddVariant(parameter.Name !, parameter.ParameterType); } } } else { foreach (var parameter in parameters) { if (parameter.ParameterType == typeof(CancellationToken)) { cancellationTokenName = parameter.Name !; } else { encoder.Add(parameter.Name !, parameter.ParameterType); } } } } string returnStatement; var decoder = new DecoderGenerator("decoder", "receivedMessage"); if (returnType == typeof(Task)) { returnStatement = "return;"; } else if (isProperty) { // must be "Get" decoder.Add("result", typeof(object), Indent + " "); returnStatement = "return (" + BuildTypeString(returnType.GenericTypeArguments[0]) + ")result;"; } else // Task<T> { decoder.Add("result", returnType.GenericTypeArguments[0], Indent + " "); returnStatement = "return result;"; } var createFunction = ""; if (returnType != typeof(Task)) { createFunction = @" " + BuildTypeString(methodInfo.ReturnType.GenericTypeArguments[0]) + @" decode(global::Dbus.Decoder decoder) { " + decoder.Result + @" " + returnStatement + @" } "; } return(@" public async " + returnTypeString + @" " + methodInfo.Name + @"(" + string.Join(", ", methodInfo.GetParameters().Select(x => BuildTypeString(x.ParameterType) + " " + x.Name)) + @") { var sendBody = new global::Dbus.Encoder(); " + encoder.Result + @" var receivedMessage = await connection.SendMethodCall( this.path, """ + interfaceName + @""", """ + callName + @""", this.destination, sendBody, """ + encoder.Signature + @""", " + cancellationTokenName + @" ).ConfigureAwait(false); " + (returnType == typeof(Task) ? "" : createFunction) + @" using (receivedMessage) { receivedMessage.AssertSignature(""" + decoder.Signature + @"""); " + (returnType == typeof(Task) ? "return;" : "return decode(receivedMessage.Decoder);") + @" } } "); }
public static (string name, string implementation) GenerateMethodProxy(MethodInfo method) { if (!method.Name.EndsWith("Async")) { throw new InvalidOperationException("Only method names ending in 'Async' are supported"); } if (method.ReturnType != typeof(Task) && method.ReturnType.GetGenericTypeDefinition() != typeof(Task <>)) { throw new InvalidOperationException("Only methods returning a Task type are supported"); } var methodName = method.Name.Substring(0, method.Name.Length - "Async".Length); var methodImplementation = new StringBuilder(); methodImplementation.Append(@" private async global::System.Threading.Tasks.Task handle" + method.Name + @"(global::Dbus.MethodCallOptions methodCallOptions, global::Dbus.ReceivedMessage receivedMessage, global::System.Threading.CancellationToken cancellationToken) { "); var decoder = new DecoderGenerator("receivedMessage.Decoder", "receivedMessage"); var methodParameters = new List <string>(); foreach (var parameter in method.GetParameters()) { if (parameter.ParameterType == typeof(CancellationToken)) { methodParameters.Add("cancellationToken"); } else { decoder.Add(parameter.Name !, parameter.ParameterType); methodParameters.Add(parameter.Name !); } } var encoder = new EncoderGenerator("sendBody"); if (method.ReturnType != typeof(Task)) // Task<T> { var returnType = method.ReturnType.GenericTypeArguments[0]; encoder.Add("methodResult", returnType, Indent); } methodImplementation.Append(Indent); methodImplementation.AppendLine(@"receivedMessage.AssertSignature(""" + decoder.Signature + @""");"); methodImplementation.Append(decoder.Result); methodImplementation.Append(Indent); if (encoder.Signature != "") { methodImplementation.Append("var methodResult = "); } methodImplementation.Append("await target." + method.Name + "("); methodImplementation.Append(string.Join(", ", methodParameters)); methodImplementation.AppendLine(");"); methodImplementation.Append(Indent); methodImplementation.AppendLine("if (!methodCallOptions.ShouldSendReply)"); methodImplementation.Append(Indent); methodImplementation.AppendLine(" return;"); methodImplementation.Append(Indent); methodImplementation.AppendLine("var sendBody = new global::Dbus.Encoder();"); if (encoder.Signature != "") { methodImplementation.Append(encoder.Result); } methodImplementation.Append(Indent); methodImplementation.AppendLine(@"await connection.SendMethodReturnAsync(methodCallOptions, sendBody, """ + encoder.Signature + @""", cancellationToken).ConfigureAwait(false);"); methodImplementation.Append(@" }"); return(methodName, methodImplementation.ToString()); }