/// <summary> /// Generates the class for the provided grain types. /// </summary> internal TypeDeclarationSyntax GenerateClass(GrainInterfaceDescription description) { var generatedTypeName = description.ReferenceTypeName; var grainType = description.Type; var genericTypes = grainType.GetHierarchyTypeParameters() .Select(_ => TypeParameter(_.ToString())) .ToArray(); // Create the special marker attribute. var grainTypeArgument = TypeOfExpression(grainType.WithoutTypeParameters().ToTypeSyntax()); var attributes = AttributeList() .AddAttributes( GeneratedCodeAttributeGenerator.GetGeneratedCodeAttributeSyntax(wellKnownTypes), Attribute(wellKnownTypes.SerializableAttribute.ToNameSyntax()), Attribute(wellKnownTypes.ExcludeFromCodeCoverageAttribute.ToNameSyntax()), Attribute(wellKnownTypes.GrainReferenceAttribute.ToNameSyntax()) .AddArgumentListArguments(AttributeArgument(grainTypeArgument))); var classDeclaration = ClassDeclaration(generatedTypeName) .AddModifiers(Token(SyntaxKind.InternalKeyword)) .AddBaseListTypes( SimpleBaseType(wellKnownTypes.GrainReference.ToTypeSyntax()), SimpleBaseType(grainType.ToTypeSyntax())) .AddConstraintClauses(grainType.GetTypeConstraintSyntax()) .AddMembers(GenerateConstructors(generatedTypeName)) .AddMembers( GrainInterfaceCommon.GenerateInterfaceIdProperty(this.wellKnownTypes, description).AddModifiers(Token(SyntaxKind.OverrideKeyword)), GrainInterfaceCommon.GenerateInterfaceVersionProperty(this.wellKnownTypes, description).AddModifiers(Token(SyntaxKind.OverrideKeyword)), GenerateInterfaceNameProperty(description), GenerateIsCompatibleMethod(description), GenerateGetMethodNameMethod(description)) .AddMembers(GenerateInvokeMethods(description)) .AddAttributeLists(attributes); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } if (this.options.DebuggerStepThrough) { var debuggerStepThroughAttribute = Attribute(this.wellKnownTypes.DebuggerStepThroughAttribute.ToNameSyntax()); classDeclaration = classDeclaration.AddAttributeLists(AttributeList().AddAttributes(debuggerStepThroughAttribute)); } return(classDeclaration); }
/// <summary> /// Generates the class for the provided grain types. /// </summary> internal TypeDeclarationSyntax GenerateClass(GrainInterfaceDescription description) { var generatedTypeName = description.InvokerTypeName; var grainType = description.Type; var baseTypes = new List <BaseTypeSyntax> { SimpleBaseType(wellKnownTypes.IGrainMethodInvoker.ToTypeSyntax()) }; var genericTypes = grainType.GetHierarchyTypeParameters() .Select(_ => TypeParameter(_.ToString())) .ToArray(); // Create the special method invoker marker attribute. var interfaceId = description.InterfaceId; var interfaceIdArgument = interfaceId.ToHexLiteral(); var grainTypeArgument = TypeOfExpression(grainType.WithoutTypeParameters().ToTypeSyntax()); var attributes = new List <AttributeSyntax> { GeneratedCodeAttributeGenerator.GetGeneratedCodeAttributeSyntax(wellKnownTypes), Attribute(wellKnownTypes.MethodInvokerAttribute.ToNameSyntax()) .AddArgumentListArguments( AttributeArgument(grainTypeArgument), AttributeArgument(interfaceIdArgument)), Attribute(wellKnownTypes.ExcludeFromCodeCoverageAttribute.ToNameSyntax()) }; var genericInvokerFields = GenerateGenericInvokerFields(wellKnownTypes, description.Methods); var members = new List <MemberDeclarationSyntax>(genericInvokerFields.Values.Select(x => x.Declaration)) { GenerateInvokeMethod(wellKnownTypes, grainType, genericInvokerFields), GenerateInterfaceTypeProperty(wellKnownTypes, description, genericTypes), GenerateGetTargetMethod(wellKnownTypes, description) }; // If this is an IGrainExtension, make the generated class implement IGrainExtensionMethodInvoker. if (grainType.HasInterface(wellKnownTypes.IGrainExtension)) { baseTypes.Add(SimpleBaseType(wellKnownTypes.IGrainExtensionMethodInvoker.ToTypeSyntax())); } var classDeclaration = ClassDeclaration(generatedTypeName) .AddModifiers(Token(SyntaxKind.InternalKeyword)) .AddBaseListTypes(baseTypes.ToArray()) .AddConstraintClauses(grainType.GetTypeConstraintSyntax()) .AddMembers(members.ToArray()) .AddAttributeLists(AttributeList().AddAttributes(attributes.ToArray())); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } if (this.options.DebuggerStepThrough) { var debuggerStepThroughAttribute = Attribute(this.wellKnownTypes.DebuggerStepThroughAttribute.ToNameSyntax()); classDeclaration = classDeclaration.AddAttributeLists(AttributeList().AddAttributes(debuggerStepThroughAttribute)); } return(classDeclaration); }
private MethodDeclarationSyntax GenerateGetTargetMethod(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description) { var method = wellKnownTypes.IGrainMethodInvoker.Method("GetTarget"); var grainContextParameter = method.Parameters[0].Name.ToIdentifierName(); ExpressionSyntax returnValue; if (description.Type.HasInterface(wellKnownTypes.IGrainExtension)) { returnValue = InvocationExpression(grainContextParameter.Member("GetGrainExtension", description.Type), ArgumentList()); } else { returnValue = grainContextParameter.Member("GrainInstance"); } return (method.GetDeclarationSyntax() .WithExpressionBody(ArrowExpressionClause(returnValue)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); }
private PropertyDeclarationSyntax GenerateInterfaceTypeProperty(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description, TypeParameterSyntax[] genericTypes) { var property = wellKnownTypes.IGrainMethodInvoker.Property("InterfaceType"); var returnValue = TypeOfExpression(description.Type.ToTypeSyntax()); return (PropertyDeclaration(wellKnownTypes.Type.ToTypeSyntax(), property.Name) .WithExpressionBody(ArrowExpressionClause(returnValue)) .AddModifiers(Token(SyntaxKind.PublicKeyword)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); }
/// <summary> /// Generates invoker methods. /// </summary> private MemberDeclarationSyntax[] GenerateInvokeMethods(GrainInterfaceDescription description) { var baseReference = BaseExpression(); var methods = description.Methods; var members = new List <MemberDeclarationSyntax>(); foreach (var methodDescription in methods) { var method = methodDescription.Method; var methodIdArgument = Argument(methodDescription.MethodId.ToHexLiteral()); // Construct a new object array from all method arguments. var parameters = method.Parameters.Select((p, i) => (p, GetSanitizedName(p, i))).ToList <(IParameterSymbol Symbol, string Name)>(); var body = new List <StatementSyntax>(); foreach (var parameter in parameters) { if (parameter.Symbol.Type.HasInterface(wellKnownTypes.IGrainObserver)) { body.Add( ExpressionStatement( InvocationExpression(wellKnownTypes.GrainFactoryBase.ToNameSyntax().Member("CheckGrainObserverParamInternal")) .AddArgumentListArguments(Argument(parameter.Name.ToIdentifierName())))); } } // Get the parameters argument value. var objectArrayType = wellKnownTypes.Object.ToTypeSyntax().GetArrayTypeSyntax(); ExpressionSyntax args; if (method.IsGenericMethod) { // 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.TypeParameters) { allParameters.Add(TypeOfExpression(typeParameter.ToTypeSyntax())); } allParameters.AddRange(parameters.Select(p => GetParameterForInvocation(p.Symbol, p.Name))); args = ArrayCreationExpression(objectArrayType) .WithInitializer( InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(allParameters.ToArray())); } else if (parameters.Count == 0) { args = LiteralExpression(SyntaxKind.NullLiteralExpression); } else { args = ArrayCreationExpression(objectArrayType) .WithInitializer( InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(parameters.Select((p => GetParameterForInvocation(p.Symbol, p.Name))).ToArray())); } var options = GetInvokeOptions(method); // Construct the invocation call. bool asyncMethod; var isOneWayTask = method.HasAttribute(wellKnownTypes.OneWayAttribute); if (method.ReturnsVoid || isOneWayTask) { // One-way methods are never marked async. asyncMethod = false; var invocation = InvocationExpression(baseReference.Member("InvokeOneWayMethod")) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(ExpressionStatement(invocation)); if (isOneWayTask) { if (!wellKnownTypes.Task.Equals(method.ReturnType)) { throw new CodeGenerationException( $"Method {method} is marked with [{wellKnownTypes.OneWayAttribute.Name}], " + $"but has a return type which is not assignable from {typeof(Task)}"); } var done = wellKnownTypes.Task.ToNameSyntax().Member((object _) => Task.CompletedTask); body.Add(ReturnStatement(done)); } } else if (method.ReturnType is INamedTypeSymbol methodReturnType) { // If the method doesn't return a Task type (eg, it returns ValueTask<T>), then we must make an async method and await the invocation result. var isTaskMethod = wellKnownTypes.Task.Equals(methodReturnType) || methodReturnType.IsGenericType && wellKnownTypes.Task_1.Equals(methodReturnType.ConstructedFrom); asyncMethod = !isTaskMethod; var returnType = methodReturnType.IsGenericType ? methodReturnType.TypeArguments[0] : wellKnownTypes.Object; var invokeMethodAsync = "InvokeMethodAsync".ToGenericName().AddTypeArgumentListArguments(returnType.ToTypeSyntax()); var invocation = InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, baseReference, invokeMethodAsync)) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } var methodResult = asyncMethod ? AwaitExpression(invocation) : (ExpressionSyntax)invocation; body.Add(ReturnStatement(methodResult)); } else { throw new NotSupportedException($"Method {method} has unsupported return type, {method.ReturnType}."); } var paramDeclaration = method.Parameters.Select((p, i) => Parameter(GetSanitizedName(p, i).ToIdentifier()).WithType(p.Type.ToTypeSyntax())); var methodDeclaration = method.GetDeclarationSyntax() .WithParameterList(ParameterList().AddParameters(paramDeclaration.ToArray())) .WithModifiers(TokenList()) .WithExplicitInterfaceSpecifier(ExplicitInterfaceSpecifier(method.ContainingType.ToNameSyntax())) .AddBodyStatements(body.ToArray()) // Since explicit implementation is used, constraints must not be specified. .WithConstraintClauses(new SyntaxList <TypeParameterConstraintClauseSyntax>()); if (asyncMethod) { methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.AsyncKeyword)); } members.Add(methodDeclaration); } return(members.ToArray()); ExpressionSyntax GetParameterForInvocation(IParameterSymbol arg, string name) { var identifier = name.ToIdentifierName(); // Addressable arguments must be converted to references before passing. if (arg.Type.HasInterface(wellKnownTypes.IAddressable) && arg.Type.TypeKind == TypeKind.Interface) { return (ConditionalExpression( BinaryExpression(SyntaxKind.IsExpression, identifier, wellKnownTypes.Grain.ToTypeSyntax()), InvocationExpression(identifier.Member("AsReference".ToGenericName().AddTypeArgumentListArguments(arg.Type.ToTypeSyntax()))), identifier)); } return(identifier); }
/// <summary> /// Generates invoker methods. /// </summary> private MemberDeclarationSyntax[] GenerateInvokeMethods(GrainInterfaceDescription description) { var baseReference = BaseExpression(); var methods = description.Methods; var members = new List <MemberDeclarationSyntax>(); foreach (var methodDescription in methods) { var method = methodDescription.Method; var methodIdArgument = Argument(methodDescription.MethodId.ToHexLiteral()); // Construct a new object array from all method arguments. var parameters = method.Parameters.Select((p, i) => (p, GetSanitizedName(p, i))).ToList <(IParameterSymbol Symbol, string Name)>(); var body = new List <StatementSyntax>(); foreach (var parameter in parameters) { if (parameter.Symbol.Type.HasInterface(wellKnownTypes.IGrainObserver)) { body.Add( ExpressionStatement( InvocationExpression(wellKnownTypes.GrainFactoryBase.ToNameSyntax().Member("CheckGrainObserverParamInternal")) .AddArgumentListArguments(Argument(parameter.Name.ToIdentifierName())))); } } // Get the parameters argument value. var objectArrayType = wellKnownTypes.Object.ToTypeSyntax().GetArrayTypeSyntax(); ExpressionSyntax args; if (method.IsGenericMethod) { // 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.TypeParameters) { allParameters.Add(TypeOfExpression(typeParameter.ToTypeSyntax())); } allParameters.AddRange(parameters.Select(p => GetParameterForInvocation(p.Symbol, p.Name))); args = ArrayCreationExpression(objectArrayType) .WithInitializer( InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(allParameters.ToArray())); } else if (parameters.Count == 0) { args = LiteralExpression(SyntaxKind.NullLiteralExpression); } else { args = ArrayCreationExpression(objectArrayType) .WithInitializer( InitializerExpression(SyntaxKind.ArrayInitializerExpression) .AddExpressions(parameters.Select((p => GetParameterForInvocation(p.Symbol, p.Name))).ToArray())); } var options = GetInvokeOptions(method); // Construct the invocation call. bool asyncMethod; var isOneWayTask = method.HasAttribute(wellKnownTypes.OneWayAttribute); if (method.ReturnsVoid || isOneWayTask) { // One-way methods are never marked async. asyncMethod = false; var invocation = InvocationExpression(baseReference.Member("InvokeOneWayMethod")) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } body.Add(ExpressionStatement(invocation)); if (isOneWayTask) { if (!SymbolEqualityComparer.Default.Equals(wellKnownTypes.Task, method.ReturnType)) { throw new CodeGenerationException( $"Method {method} is marked with [{wellKnownTypes.OneWayAttribute.Name}], " + $"but has a return type which is not assignable from {typeof(Task)}"); } var done = wellKnownTypes.Task.ToNameSyntax().Member((object _) => Task.CompletedTask); body.Add(ReturnStatement(done)); } } else if (method.ReturnType is INamedTypeSymbol methodReturnType) { // If the method doesn't return a Task type (eg, it returns ValueTask<T>), then we must make an async method and await the invocation result. var isTaskMethod = SymbolEqualityComparer.Default.Equals(wellKnownTypes.Task, methodReturnType) || methodReturnType.IsGenericType && SymbolEqualityComparer.Default.Equals(wellKnownTypes.Task_1, methodReturnType.ConstructedFrom); asyncMethod = !isTaskMethod; var returnType = methodReturnType.IsGenericType ? methodReturnType.TypeArguments[0] : wellKnownTypes.Object; var invokeMethodAsync = "InvokeMethodAsync".ToGenericName().AddTypeArgumentListArguments(returnType.ToTypeSyntax()); var invocation = InvocationExpression(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, baseReference, invokeMethodAsync)) .AddArgumentListArguments(methodIdArgument) .AddArgumentListArguments(Argument(args)); if (options != null) { invocation = invocation.AddArgumentListArguments(options); } var methodResult = asyncMethod ? AwaitExpression(invocation) : (ExpressionSyntax)invocation; if (this.wellKnownTypes.ValueTask is WellKnownTypes.Some valueTask && SymbolEqualityComparer.Default.Equals(valueTask.Value, methodReturnType)) { body.Add(ExpressionStatement(methodResult)); } else { body.Add(ReturnStatement(methodResult)); } }
public static PropertyDeclarationSyntax GenerateInterfaceIdProperty(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description) { var property = wellKnownTypes.GrainReference.Property("InterfaceTypeCode"); var returnValue = description.InterfaceId.ToHexLiteral(); return (PropertyDeclaration(wellKnownTypes.Int32.ToTypeSyntax(), property.Name) .WithExpressionBody(ArrowExpressionClause(returnValue)) .AddModifiers(Token(SyntaxKind.PublicKeyword)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); }
public static PropertyDeclarationSyntax GenerateInterfaceVersionProperty(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description) { var property = wellKnownTypes.IGrainMethodInvoker.Property("InterfaceVersion"); var returnValue = LiteralExpression( SyntaxKind.NumericLiteralExpression, Literal(description.InterfaceVersion)); return (PropertyDeclaration(wellKnownTypes.UInt16.ToTypeSyntax(), property.Name) .WithExpressionBody(ArrowExpressionClause(returnValue)) .AddModifiers(Token(SyntaxKind.PublicKeyword)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); }
private static MethodDeclarationSyntax GenerateGetMethodNameMethod(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description) { var method = wellKnownTypes.GrainReference.Method("GetMethodName"); var methodDeclaration = method.GetDeclarationSyntax().AddModifiers(Token(SyntaxKind.OverrideKeyword)); var parameters = method.Parameters; var interfaceIdArgument = parameters[0].Name.ToIdentifierName(); var methodIdArgument = parameters[1].Name.ToIdentifierName(); var callThrowMethodNotImplemented = InvocationExpression(IdentifierName("ThrowMethodNotImplemented")) .WithArgumentList(ArgumentList(SeparatedList(new[] { Argument(interfaceIdArgument), Argument(methodIdArgument) }))); // This method is used directly after its declaration to create blocks for each interface id, comprising // primarily of a nested switch statement for each of the methods in the given interface. BlockSyntax ComposeInterfaceBlock(INamedTypeSymbol interfaceType, SwitchStatementSyntax methodSwitch) { return(Block(methodSwitch.AddSections(SwitchSection() .AddLabels(DefaultSwitchLabel()) .AddStatements( ExpressionStatement(callThrowMethodNotImplemented), ReturnStatement(LiteralExpression(SyntaxKind.NullLiteralExpression)))))); } var interfaceCases = GrainInterfaceCommon.GenerateGrainInterfaceAndMethodSwitch( wellKnownTypes, description.Type, methodIdArgument, methodType => new StatementSyntax[] { ReturnStatement(methodType.Name.ToLiteralExpression()) }, ComposeInterfaceBlock); // Generate the default case, which will throw a NotImplementedException. var callThrowInterfaceNotImplemented = InvocationExpression(IdentifierName("ThrowInterfaceNotImplemented")) .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(interfaceIdArgument)))); var defaultCase = SwitchSection() .AddLabels(DefaultSwitchLabel()) .AddStatements( ExpressionStatement(callThrowInterfaceNotImplemented), ReturnStatement(LiteralExpression(SyntaxKind.NullLiteralExpression))); var throwInterfaceNotImplemented = GrainInterfaceCommon.GenerateMethodNotImplementedFunction(wellKnownTypes); var throwMethodNotImplemented = GrainInterfaceCommon.GenerateInterfaceNotImplementedFunction(wellKnownTypes); var interfaceIdSwitch = SwitchStatement(interfaceIdArgument).AddSections(interfaceCases.ToArray()).AddSections(defaultCase); return(methodDeclaration.AddBodyStatements(interfaceIdSwitch, throwInterfaceNotImplemented, throwMethodNotImplemented)); }
private static MemberDeclarationSyntax GenerateInterfaceNameProperty(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description) { var returnValue = description.Type.Name.ToLiteralExpression(); return (PropertyDeclaration(wellKnownTypes.String.ToTypeSyntax(), "InterfaceName") .WithExpressionBody(ArrowExpressionClause(returnValue)) .AddModifiers(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.OverrideKeyword)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); }
private static MemberDeclarationSyntax GenerateIsCompatibleMethod(WellKnownTypes wellKnownTypes, GrainInterfaceDescription description) { var method = wellKnownTypes.GrainReference.Method("IsCompatible"); var interfaceIdParameter = method.Parameters[0].Name.ToIdentifierName(); var interfaceIds = new HashSet <int>( new[] { description.InterfaceId }.Concat( description.Type.AllInterfaces.Where(wellKnownTypes.IsGrainInterface).Select(wellKnownTypes.GetTypeId))); var returnValue = default(BinaryExpressionSyntax); foreach (var interfaceId in interfaceIds) { var check = BinaryExpression( SyntaxKind.EqualsExpression, interfaceIdParameter, interfaceId.ToHexLiteral()); // If this is the first check, assign it, otherwise OR this check with the previous checks. returnValue = returnValue == null ? check : BinaryExpression(SyntaxKind.LogicalOrExpression, returnValue, check); } return (method.GetDeclarationSyntax() .AddModifiers(Token(SyntaxKind.OverrideKeyword)) .WithExpressionBody(ArrowExpressionClause(returnValue)) .WithSemicolonToken(Token(SyntaxKind.SemicolonToken))); }