/// <summary> /// Generates <see cref="GenericMethodInvoker"/> fields for the generic methods in <paramref name="grainType"/>. /// </summary> /// <param name="grainType">The grain type.</param> /// <returns>The generated fields.</returns> private static MemberDeclarationSyntax[] GenerateGenericInvokerFields(Type grainType) { var methods = GrainInterfaceUtils.GetMethods(grainType); var result = new List <MemberDeclarationSyntax>(); foreach (var method in methods) { if (!method.IsGenericMethodDefinition) { continue; } result.Add(GenerateGenericInvokerField(method)); } return(result.ToArray()); }
/// <summary> /// Generates invoker methods. /// </summary> /// <param name="grainType">The grain type.</param> /// <param name="onEncounteredType"> /// The callback which is invoked when a type is encountered. /// </param> /// <returns>Invoker methods for the provided grain type.</returns> private static MemberDeclarationSyntax[] GenerateInvokeMethods(Type grainType, Action <Type> onEncounteredType) { var baseReference = SF.BaseExpression(); var methods = GrainInterfaceUtils.GetMethods(grainType); var members = new List <MemberDeclarationSyntax>(); foreach (var method in methods) { onEncounteredType(method.ReturnType); var methodId = GrainInterfaceUtils.ComputeMethodId(method); var methodIdArgument = SF.Argument(SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(methodId))); // Construct a new object array from all method arguments. var parameters = method.GetParameters(); var body = new List <StatementSyntax>(); foreach (var parameter in parameters) { onEncounteredType(parameter.ParameterType); if (typeof(IGrainObserver).GetTypeInfo().IsAssignableFrom(parameter.ParameterType)) { body.Add( SF.ExpressionStatement( CheckGrainObserverParamInternalExpression.Invoke() .AddArgumentListArguments(SF.Argument(parameter.Name.ToIdentifierName())))); } } // Get the parameters argument value. ExpressionSyntax args; if (parameters.Length == 0) { args = SF.LiteralExpression(SyntaxKind.NullLiteralExpression); } else { args = SF.ArrayCreationExpression(typeof(object).GetArrayTypeSyntax()) .WithInitializer( SF.InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(parameters.Select(GetParameterForInvocation).ToArray())); } var options = GetInvokeOptions(method); // Construct the invocation call. if (method.ReturnType == typeof(void)) { var invocation = SF.InvocationExpression(baseReference.Member("InvokeOneWayMethod")) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(SF.ExpressionStatement(invocation)); } else { var returnType = method.ReturnType == typeof(Task) ? typeof(object) : method.ReturnType.GenericTypeArguments[0]; var invocation = SF.InvocationExpression(baseReference.Member("InvokeMethodAsync", returnType)) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(SF.ReturnStatement(invocation)); } members.Add(method.GetDeclarationSyntax().AddBodyStatements(body.ToArray())); } return(members.ToArray()); }
/// <summary> /// Generates invoker methods. /// </summary> /// <param name="grainType">The grain type.</param> /// <param name="onEncounteredType"> /// The callback which is invoked when a type is encountered. /// </param> /// <returns>Invoker methods for the provided grain type.</returns> private static MemberDeclarationSyntax[] GenerateInvokeMethods(Type grainType, Action <Type> onEncounteredType) { var baseReference = SF.BaseExpression(); var methods = GrainInterfaceUtils.GetMethods(grainType); var members = new List <MemberDeclarationSyntax>(); foreach (var method in methods) { onEncounteredType(method.ReturnType); var methodId = GrainInterfaceUtils.ComputeMethodId(method); var methodIdArgument = SF.Argument(SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(methodId))); // Construct a new object array from all method arguments. var parameters = method.GetParameters(); var body = new List <StatementSyntax>(); foreach (var parameter in parameters) { onEncounteredType(parameter.ParameterType); if (typeof(IGrainObserver).IsAssignableFrom(parameter.ParameterType)) { body.Add( SF.ExpressionStatement( CheckGrainObserverParamInternalExpression.Invoke() .AddArgumentListArguments(SF.Argument(parameter.Name.ToIdentifierName())))); } } // Get the parameters argument value. ExpressionSyntax args; if (method.IsGenericMethodDefinition) { // Create an arguments array which includes the method's type parameters followed by the method's parameter list. var allParameters = new List <ExpressionSyntax>(); foreach (var typeParameter in method.GetGenericArguments()) { allParameters.Add(SF.TypeOfExpression(typeParameter.GetTypeSyntax())); } allParameters.AddRange(parameters.Select(GetParameterForInvocation)); args = SF.ArrayCreationExpression(typeof(object).GetArrayTypeSyntax()) .WithInitializer( SF.InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(allParameters.ToArray())); } else if (parameters.Length == 0) { args = SF.LiteralExpression(SyntaxKind.NullLiteralExpression); } else { args = SF.ArrayCreationExpression(typeof(object).GetArrayTypeSyntax()) .WithInitializer( SF.InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(parameters.Select(GetParameterForInvocation).ToArray())); } var options = GetInvokeOptions(method); // Construct the invocation call. var isOneWayTask = method.GetCustomAttribute <OneWayAttribute>() != null; if (method.ReturnType == typeof(void) || isOneWayTask) { var invocation = SF.InvocationExpression(baseReference.Member("InvokeOneWayMethod")) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(SF.ExpressionStatement(invocation)); if (isOneWayTask) { if (method.ReturnType != typeof(Task)) { throw new CodeGenerationException( $"Method {grainType.GetParseableName()}.{method.Name} is marked with [{nameof(OneWayAttribute)}], " + $"but has a return type which is not assignable from {typeof(Task)}"); } var done = typeof(Task).GetNameSyntax(true).Member((object _) => Task.CompletedTask); body.Add(SF.ReturnStatement(done)); } } else { var returnType = method.ReturnType == typeof(Task) ? typeof(object) : method.ReturnType.GenericTypeArguments[0]; var invocation = SF.InvocationExpression(baseReference.Member("InvokeMethodAsync", returnType)) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(SF.Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } ExpressionSyntax returnContent = invocation; if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition().FullName == "System.Threading.Tasks.ValueTask`1") { // Wrapping invocation expression with initialization of ValueTask (e.g. new ValueTask<int>(base.InvokeMethod())) returnContent = SF.ObjectCreationExpression(method.ReturnType.GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(SF.ExpressionStatement(invocation).Expression)); } body.Add(SF.ReturnStatement(returnContent)); } members.Add(method.GetDeclarationSyntax().AddBodyStatements(body.ToArray())); } return(members.ToArray()); }
/// <summary> /// Generates switch cases for the provided grain type. /// </summary> /// <param name="grainType"> /// The grain type. /// </param> /// <param name="methodIdArgument"> /// The method id argument, which is used to select the correct switch label. /// </param> /// <param name="generateMethodHandler"> /// The function used to generate switch block statements for each method. /// </param> /// <returns> /// The switch cases for the provided grain type. /// </returns> public static SwitchSectionSyntax[] GenerateGrainInterfaceAndMethodSwitch( Type grainType, ExpressionSyntax methodIdArgument, Func <MethodInfo, StatementSyntax[]> generateMethodHandler) { var interfaces = GrainInterfaceUtils.GetRemoteInterfaces(grainType); interfaces[GrainInterfaceUtils.GetGrainInterfaceId(grainType)] = grainType; // Switch on interface id. var interfaceCases = new List <SwitchSectionSyntax>(); foreach (var @interface in interfaces) { var interfaceType = @interface.Value; var interfaceId = @interface.Key; var methods = GrainInterfaceUtils.GetMethods(interfaceType); var methodCases = new List <SwitchSectionSyntax>(); // Switch on method id. foreach (var method in methods) { // Generate switch case. var methodId = GrainInterfaceUtils.ComputeMethodId(method); var methodType = method; // Generate the switch label for this interface id. var methodIdSwitchLabel = SF.CaseSwitchLabel( SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(methodId))); // Generate the switch body. var methodInvokeStatement = generateMethodHandler(methodType); methodCases.Add( SF.SwitchSection().AddLabels(methodIdSwitchLabel).AddStatements(methodInvokeStatement)); } // Generate the switch label for this interface id. var interfaceIdSwitchLabel = SF.CaseSwitchLabel( SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId))); // Generate the default case, which will throw a NotImplementedException. var errorMessage = SF.BinaryExpression( SyntaxKind.AddExpression, "interfaceId=".GetLiteralExpression(), SF.BinaryExpression( SyntaxKind.AddExpression, SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId)), SF.BinaryExpression( SyntaxKind.AddExpression, ",methodId=".GetLiteralExpression(), methodIdArgument))); var throwStatement = SF.ThrowStatement( SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(errorMessage))); var defaultCase = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement); // Generate switch statements for the methods in this interface. var methodSwitchStatements = SF.SwitchStatement(methodIdArgument).AddSections(methodCases.ToArray()).AddSections(defaultCase); // Generate the switch section for this interface. interfaceCases.Add( SF.SwitchSection().AddLabels(interfaceIdSwitchLabel).AddStatements(methodSwitchStatements)); } return(interfaceCases.ToArray()); }