Beispiel #1
0
        private static MethodDeclarationSyntax GenerateGetMethodNameMethod(Type grainType)
        {
            var method            = TypeUtils.Method((GrainReference _) => _.GetMethodName(default(int), default(int)));
            var methodDeclaration =
                method.GetDeclarationSyntax()
                .AddModifiers(SF.Token(SyntaxKind.OverrideKeyword));
            var parameters = method.GetParameters();

            var interfaceIdArgument = parameters[0].Name.ToIdentifierName();
            var methodIdArgument    = parameters[1].Name.ToIdentifierName();

            var interfaceCases = CodeGeneratorCommon.GenerateGrainInterfaceAndMethodSwitch(
                grainType,
                methodIdArgument,
                methodType => new StatementSyntax[] { SF.ReturnStatement(methodType.Name.GetLiteralExpression()) });

            // Generate the default case, which will throw a NotImplementedException.
            var errorMessage = SF.BinaryExpression(
                SyntaxKind.AddExpression,
                "interfaceId=".GetLiteralExpression(),
                interfaceIdArgument);
            var throwStatement =
                SF.ThrowStatement(
                    SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax())
                    .AddArgumentListArguments(SF.Argument(errorMessage)));
            var defaultCase       = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement);
            var interfaceIdSwitch =
                SF.SwitchStatement(interfaceIdArgument).AddSections(interfaceCases.ToArray()).AddSections(defaultCase);

            return(methodDeclaration.AddBodyStatements(interfaceIdSwitch));
        }
Beispiel #2
0
        /// <summary>
        /// Compiles the provided syntax tree, and loads and returns the result.
        /// </summary>
        /// <param name="generatedSyntax">The syntax tree.</param>
        /// <returns>The compilation output.</returns>
        private static byte[] CompileAndLoad(GeneratedSyntax generatedSyntax)
        {
            var rawAssembly = CodeGeneratorCommon.CompileAssembly(generatedSyntax, "OrleansCodeGen.dll");

            Assembly.Load(rawAssembly);
            return(rawAssembly);
        }
Beispiel #3
0
        /// <summary>
        /// Generates the class for the provided grain types.
        /// </summary>
        /// <param name="grainType">
        /// The grain interface type.
        /// </param>
        /// <returns>
        /// The generated class.
        /// </returns>
        internal static TypeDeclarationSyntax GenerateClass(Type grainType)
        {
            var baseTypes = new List <BaseTypeSyntax> {
                SF.SimpleBaseType(typeof(IGrainMethodInvoker).GetTypeSyntax())
            };

            var grainTypeInfo = grainType.GetTypeInfo();
            var genericTypes  = grainTypeInfo.IsGenericTypeDefinition
                                   ? grainTypeInfo.GetGenericArguments()
                                .Select(_ => SF.TypeParameter(_.ToString()))
                                .ToArray()
                                   : new TypeParameterSyntax[0];

            // Create the special method invoker marker attribute.
            var interfaceId         = GrainInterfaceUtils.GetGrainInterfaceId(grainType);
            var interfaceIdArgument = SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId));
            var grainTypeArgument   = SF.TypeOfExpression(grainType.GetTypeSyntax(includeGenericParameters: false));
            var attributes          = new List <AttributeSyntax>
            {
                CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(),
                SF.Attribute(typeof(MethodInvokerAttribute).GetNameSyntax())
                .AddArgumentListArguments(
                    SF.AttributeArgument(grainType.GetParseableName().GetLiteralExpression()),
                    SF.AttributeArgument(interfaceIdArgument),
                    SF.AttributeArgument(grainTypeArgument)),
#if !NETSTANDARD
                SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax())
#endif
            };

            var members = new List <MemberDeclarationSyntax>
            {
                GenerateInvokeMethod(grainType),
                GenerateInterfaceIdProperty(grainType)
            };

            // If this is an IGrainExtension, make the generated class implement IGrainExtensionMethodInvoker.
            if (typeof(IGrainExtension).IsAssignableFrom(grainType))
            {
                baseTypes.Add(SF.SimpleBaseType(typeof(IGrainExtensionMethodInvoker).GetTypeSyntax()));
                members.Add(GenerateExtensionInvokeMethod(grainType));
            }

            var classDeclaration =
                SF.ClassDeclaration(
                    CodeGeneratorCommon.ClassPrefix + TypeUtils.GetSuitableClassName(grainType) + ClassSuffix)
                .AddModifiers(SF.Token(SyntaxKind.InternalKeyword))
                .AddBaseListTypes(baseTypes.ToArray())
                .AddConstraintClauses(grainType.GetTypeConstraintSyntax())
                .AddMembers(members.ToArray())
                .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray()));

            if (genericTypes.Length > 0)
            {
                classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes);
            }

            return(classDeclaration);
        }
Beispiel #4
0
        /// <summary>
        /// Generates syntax for an invoke method.
        /// </summary>
        /// <param name="grainType">
        /// The grain type.
        /// </param>
        /// <param name="invokeMethod">
        /// The invoke method to generate.
        /// </param>
        /// <returns>
        /// Syntax for an invoke method.
        /// </returns>
        private static MethodDeclarationSyntax GenerateInvokeMethod(Type grainType, MethodInfo invokeMethod)
        {
            var methodDeclaration = invokeMethod.GetDeclarationSyntax();
            var parameters        = invokeMethod.GetParameters();

            var grainArgument       = parameters[0].Name.ToIdentifierName();
            var interfaceIdArgument = parameters[1].Name.ToIdentifierName();
            var methodIdArgument    = parameters[2].Name.ToIdentifierName();
            var argumentsArgument   = parameters[3].Name.ToIdentifierName();

            var interfaceCases = CodeGeneratorCommon.GenerateGrainInterfaceAndMethodSwitch(
                grainType,
                methodIdArgument,
                methodType => GenerateInvokeForMethod(grainType, grainArgument, methodType, argumentsArgument));

            // Generate the default case, which will throw a NotImplementedException.
            var errorMessage = SF.BinaryExpression(
                SyntaxKind.AddExpression,
                "interfaceId=".GetLiteralExpression(),
                interfaceIdArgument);
            var throwStatement =
                SF.ThrowStatement(
                    SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax())
                    .AddArgumentListArguments(SF.Argument(errorMessage)));
            var defaultCase       = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement);
            var interfaceIdSwitch =
                SF.SwitchStatement(interfaceIdArgument).AddSections(interfaceCases.ToArray()).AddSections(defaultCase);

            // If the provided grain is null, throw an argument exception.
            var argumentNullException =
                SF.ObjectCreationExpression(typeof(ArgumentNullException).GetTypeSyntax())
                .AddArgumentListArguments(SF.Argument(parameters[0].Name.GetLiteralExpression()));
            var grainArgumentCheck =
                SF.IfStatement(
                    SF.BinaryExpression(
                        SyntaxKind.EqualsExpression,
                        grainArgument,
                        SF.LiteralExpression(SyntaxKind.NullLiteralExpression)),
                    SF.ThrowStatement(argumentNullException));

            // Wrap everything in a try-catch block.
            var          faulted   = (Expression <Func <Task <object> > >)(() => TaskUtility.Faulted(null));
            const string Exception = "exception";
            var          exception = SF.Identifier(Exception);
            var          body      =
                SF.TryStatement()
                .AddBlockStatements(grainArgumentCheck, interfaceIdSwitch)
                .AddCatches(
                    SF.CatchClause()
                    .WithDeclaration(
                        SF.CatchDeclaration(typeof(Exception).GetTypeSyntax()).WithIdentifier(exception))
                    .AddBlockStatements(
                        SF.ReturnStatement(
                            faulted.Invoke().AddArgumentListArguments(SF.Argument(SF.IdentifierName(Exception))))));

            return(methodDeclaration.AddBodyStatements(body));
        }
Beispiel #5
0
        /// <summary>
        /// Compiles the provided syntax tree, and loads and returns the result.
        /// </summary>
        /// <param name="generatedSyntax">The syntax tree.</param>
        /// <param name="emitDebugSymbols">
        /// Whether or not to emit debug symbols for the generated assembly.
        /// </param>
        /// <returns>The compilation output.</returns>
        private static CachedAssembly CompileAndLoad(GeneratedSyntax generatedSyntax, bool emitDebugSymbols)
        {
            var generated = CodeGeneratorCommon.CompileAssembly(generatedSyntax, "OrleansCodeGen", emitDebugSymbols: emitDebugSymbols);

            Assembly.Load(generated.RawBytes, generated.DebugSymbolRawBytes);
            return(new CachedAssembly(generated)
            {
                Loaded = true
            });
        }
Beispiel #6
0
        /// <summary>
        /// Compiles the provided syntax tree, and loads and returns the result.
        /// </summary>
        /// <param name="generatedSyntax">The syntax tree.</param>
        /// <param name="emitDebugSymbols">
        /// Whether or not to emit debug symbols for the generated assembly.
        /// </param>
        /// <returns>The compilation output.</returns>
        private static CachedAssembly CompileAndLoad(GeneratedSyntax generatedSyntax, bool emitDebugSymbols)
        {
            var generated      = CodeGeneratorCommon.CompileAssembly(generatedSyntax, "OrleansCodeGen", emitDebugSymbols: emitDebugSymbols);
            var loadedAssembly = LoadAssembly(generated);

            return(new CachedAssembly(generated)
            {
                Loaded = true,
                Assembly = loadedAssembly,
            });
        }
Beispiel #7
0
        /// <summary>
        /// Generates the class for the provided grain types.
        /// </summary>
        /// <param name="grainType">
        /// The grain interface type.
        /// </param>
        /// <param name="onEncounteredType">
        /// The callback which is invoked when a type is encountered.
        /// </param>
        /// <returns>
        /// The generated class.
        /// </returns>
        internal static TypeDeclarationSyntax GenerateClass(Type grainType, Action <Type> onEncounteredType)
        {
            var grainTypeInfo = grainType.GetTypeInfo();
            var genericTypes  = grainTypeInfo.IsGenericTypeDefinition
                                   ? grainTypeInfo.GetGenericArguments()
                                .Select(_ => SF.TypeParameter(_.ToString()))
                                .ToArray()
                                   : new TypeParameterSyntax[0];

            // Create the special marker attribute.
            var markerAttribute =
                SF.Attribute(typeof(GrainReferenceAttribute).GetNameSyntax())
                .AddArgumentListArguments(
                    SF.AttributeArgument(
                        SF.TypeOfExpression(grainType.GetTypeSyntax(includeGenericParameters: false))));
            var attributes = SF.AttributeList()
                             .AddAttributes(
                CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(),

                SF.Attribute(typeof(SerializableAttribute).GetNameSyntax()),
#if !NETSTANDARD_TODO
                //ExcludeFromCodeCoverageAttribute became an internal class in netstandard
                SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()),
#endif

                markerAttribute);

            var className        = CodeGeneratorCommon.ClassPrefix + TypeUtils.GetSuitableClassName(grainType) + ClassSuffix;
            var classDeclaration =
                SF.ClassDeclaration(className)
                .AddModifiers(SF.Token(SyntaxKind.InternalKeyword))
                .AddBaseListTypes(
                    SF.SimpleBaseType(typeof(GrainReference).GetTypeSyntax()),
                    SF.SimpleBaseType(grainType.GetTypeSyntax()))
                .AddConstraintClauses(grainType.GetTypeConstraintSyntax())
                .AddMembers(GenerateConstructors(className))
                .AddMembers(
                    GenerateInterfaceIdProperty(grainType),
                    GenerateInterfaceNameProperty(grainType),
                    GenerateIsCompatibleMethod(grainType),
                    GenerateGetMethodNameMethod(grainType))
                .AddMembers(GenerateInvokeMethods(grainType, onEncounteredType))
                .AddAttributeLists(attributes);

            if (genericTypes.Length > 0)
            {
                classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes);
            }

            return(classDeclaration);
        }
Beispiel #8
0
        /// <summary>
        /// Generates the class for the provided grain types.
        /// </summary>
        /// <param name="type">The grain interface type.</param>
        /// <param name="onEncounteredType">
        /// The callback invoked when a type is encountered.
        /// </param>
        /// <returns>
        /// The generated class.
        /// </returns>
        internal static TypeDeclarationSyntax GenerateClass(Type type, Action <Type> onEncounteredType)
        {
            var typeInfo     = type.GetTypeInfo();
            var genericTypes = typeInfo.IsGenericTypeDefinition
                                   ? typeInfo.GetGenericArguments().Select(_ => SF.TypeParameter(_.ToString())).ToArray()
                                   : new TypeParameterSyntax[0];

            var attributes = new List <AttributeSyntax>
            {
                CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(),
#if !NETSTANDARD
                SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()),
#endif
                SF.Attribute(typeof(SerializerAttribute).GetNameSyntax())
                .AddArgumentListArguments(
                    SF.AttributeArgument(SF.TypeOfExpression(type.GetTypeSyntax(includeGenericParameters: false))))
            };

            var className = CodeGeneratorCommon.ClassPrefix + type.GetParseableName(GeneratedTypeNameOptions);
            var fields    = GetFields(type);

            // Mark each field type for generation
            foreach (var field in fields)
            {
                var fieldType = field.FieldInfo.FieldType;
                onEncounteredType(fieldType);
            }

            var members = new List <MemberDeclarationSyntax>(GenerateStaticFields(fields))
            {
                GenerateDeepCopierMethod(type, fields),
                GenerateSerializerMethod(type, fields),
                GenerateDeserializerMethod(type, fields),
            };

            var classDeclaration =
                SF.ClassDeclaration(className)
                .AddModifiers(SF.Token(SyntaxKind.InternalKeyword))
                .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray()))
                .AddMembers(members.ToArray())
                .AddConstraintClauses(type.GetTypeConstraintSyntax());

            if (genericTypes.Length > 0)
            {
                classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes);
            }

            return(classDeclaration);
        }
        /// <summary>
        /// Generates source code for the provided assembly.
        /// </summary>
        /// <param name="input">
        /// The assembly to generate source for.
        /// </param>
        /// <returns>
        /// The generated source.
        /// </returns>
        public string GenerateSourceForAssembly(Assembly input)
        {
            if (input.GetCustomAttribute <GeneratedCodeAttribute>() != null ||
                input.GetCustomAttribute <SkipCodeGenerationAttribute>() != null)
            {
                return(string.Empty);
            }

            var generated = this.GenerateCode(input, new[] { input }.ToList());

            if (generated.Syntax == null)
            {
                return(string.Empty);
            }

            return(CodeGeneratorCommon.GenerateSourceCode(CodeGeneratorCommon.AddGeneratedCodeAttribute(generated)));
        }
Beispiel #10
0
        /// <summary>
        /// Generates source code for the provided assembly.
        /// </summary>
        /// <param name="input">
        /// The assembly to generate source for.
        /// </param>
        /// <returns>
        /// The generated source.
        /// </returns>
        public string GenerateSourceForAssembly(Assembly input)
        {
            if (!ShouldGenerateCodeForAssembly(input))
            {
                return(string.Empty);
            }

            var generated = GenerateForAssemblies(new List <Assembly> {
                input
            }, false);

            if (generated.Syntax == null)
            {
                return(string.Empty);
            }

            return(CodeGeneratorCommon.GenerateSourceCode(CodeGeneratorCommon.AddGeneratedCodeAttribute(generated)));
        }
Beispiel #11
0
        /// <summary>
        /// Generates source code for the provided assembly.
        /// </summary>
        /// <param name="input">
        /// The assembly to generate source for.
        /// </param>
        /// <returns>
        /// The generated source.
        /// </returns>
        public string GenerateSourceForAssembly(Assembly input)
        {
            RegisterGeneratedCodeTargets(input);

            if (input.GetCustomAttribute <GeneratedCodeAttribute>() != null ||
                input.GetCustomAttribute <SkipCodeGenerationAttribute>() != null)
            {
                return(string.Empty);
            }

            var generated = GenerateForAssemblies(new List <Assembly> {
                input
            }, false);

            if (generated.Syntax == null)
            {
                return(string.Empty);
            }

            return(CodeGeneratorCommon.GenerateSourceCode(CodeGeneratorCommon.AddGeneratedCodeAttribute(generated)));
        }
        /// <summary>
        /// Generates and compiles an assembly for the provided syntax.
        /// </summary>
        /// <param name="generatedSyntax">
        /// The generated code.
        /// </param>
        /// <param name="assemblyName">
        /// The name for the generated assembly.
        /// </param>
        /// <param name="emitDebugSymbols">
        /// Whether or not to emit debug symbols for the generated assembly.
        /// </param>
        /// <returns>
        /// The raw assembly.
        /// </returns>
        /// <exception cref="CodeGenerationException">
        /// An error occurred generating code.
        /// </exception>
        private Assembly CompileAssembly(GeneratedSyntax generatedSyntax, string assemblyName, bool emitDebugSymbols)
        {
            // Add the generated code attribute.
            var code = CodeGeneratorCommon.AddGeneratedCodeAttribute(generatedSyntax);

            // Reference everything which can be referenced.
            var assemblies =
                AppDomain.CurrentDomain.GetAssemblies()
                .Where(asm => !asm.IsDynamic && !string.IsNullOrWhiteSpace(asm.Location))
                .Select(asm => MetadataReference.CreateFromFile(asm.Location))
                .Cast <MetadataReference>()
                .ToArray();

            // Generate the code.
            var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);

#if NETSTANDARD2_0
            // CoreFX bug https://github.com/dotnet/corefx/issues/5540
            // to workaround it, we are calling internal WithTopLevelBinderFlags(BinderFlags.IgnoreCorLibraryDuplicatedTypes)
            // TODO: this API will be public in the future releases of Roslyn.
            // This work is tracked in https://github.com/dotnet/roslyn/issues/5855
            // Once it's public, we should replace the internal reflection API call by the public one.
            var method = typeof(CSharpCompilationOptions).GetMethod("WithTopLevelBinderFlags", BindingFlags.NonPublic | BindingFlags.Instance);
            // we need to pass BinderFlags.IgnoreCorLibraryDuplicatedTypes, but it's an internal class
            // http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/Binder/BinderFlags.cs,00f268571bb66b73
            options = (CSharpCompilationOptions)method.Invoke(options, new object[] { 1u << 26 });
#endif

            string source = null;
            if (this.logger.IsEnabled(LogLevel.Debug))
            {
                source = CodeGeneratorCommon.GenerateSourceCode(code);

                // Compile the code and load the generated assembly.
                this.logger.Debug(
                    ErrorCode.CodeGenSourceGenerated,
                    "Generating assembly {0} with source:\n{1}",
                    assemblyName,
                    source);
            }

            var compilation =
                CSharpCompilation.Create(assemblyName)
                .AddSyntaxTrees(code.SyntaxTree)
                .AddReferences(assemblies)
                .WithOptions(options);

            using (var outputStream = new MemoryStream())
            {
                var emitOptions = new EmitOptions()
                                  .WithEmitMetadataOnly(false)
                                  .WithIncludePrivateMembers(true);

                if (emitDebugSymbols)
                {
                    emitOptions = emitOptions.WithDebugInformationFormat(DebugInformationFormat.Embedded);
                }

                var compilationResult = compilation.Emit(outputStream, options: emitOptions);
                if (!compilationResult.Success)
                {
                    source = source ?? CodeGeneratorCommon.GenerateSourceCode(code);
                    var errors = string.Join("\n", compilationResult.Diagnostics.Select(_ => _.ToString()));
                    this.logger.Warn(
                        ErrorCode.CodeGenCompilationFailed,
                        "Compilation of assembly {0} failed with errors:\n{1}\nGenerated Source Code:\n{2}",
                        assemblyName,
                        errors,
                        source);
                    throw new CodeGenerationException(errors);
                }

                this.logger.Debug(
                    ErrorCode.CodeGenCompilationSucceeded,
                    "Compilation of assembly {0} succeeded.",
                    assemblyName);
                return(Assembly.Load(outputStream.ToArray()));
            }
        }
        /// <summary>
        /// Generates a syntax tree for the provided assemblies.
        /// </summary>
        /// <param name="targetAssembly">The assemblies used for accessiblity checks, or <see langword="null"/> during runtime code generation.</param>
        /// <param name="assemblies">The assemblies to generate code for.</param>
        /// <returns>The generated syntax tree.</returns>
        private GeneratedSyntax GenerateCode(Assembly targetAssembly, List <Assembly> assemblies)
        {
            var features = new FeatureDescriptions();
            var members  = new List <MemberDeclarationSyntax>();

            // Expand the list of included assemblies and types.
            var knownAssemblies =
                new Dictionary <Assembly, KnownAssemblyAttribute>(
                    assemblies.ToDictionary(k => k, k => default(KnownAssemblyAttribute)));

            foreach (var attribute in assemblies.SelectMany(asm => asm.GetCustomAttributes <KnownAssemblyAttribute>()))
            {
                knownAssemblies[attribute.Assembly] = attribute;
            }

            if (logger.IsEnabled(LogLevel.Information))
            {
                logger.Info($"Generating code for assemblies: {string.Join(", ", knownAssemblies.Keys.Select(a => a.FullName))}");
            }

            // Get types from assemblies which reference Orleans and are not generated assemblies.
            var grainClasses    = new HashSet <Type>();
            var grainInterfaces = new HashSet <Type>();

            foreach (var pair in knownAssemblies)
            {
                var assembly = pair.Key;
                var treatTypesAsSerializable = pair.Value?.TreatTypesAsSerializable ?? false;
                foreach (var type in TypeUtils.GetDefinedTypes(assembly, this.logger))
                {
                    if (treatTypesAsSerializable || type.IsSerializable || TypeHasKnownBase(type))
                    {
                        string logContext = null;
                        if (logger.IsEnabled(LogLevel.Trace))
                        {
                            if (treatTypesAsSerializable)
                            {
                                logContext = $"known assembly {assembly.GetName().Name} where 'TreatTypesAsSerializable' = true";
                            }
                            else if (type.IsSerializable)
                            {
                                logContext = $"known assembly {assembly.GetName().Name} where type is [Serializable]";
                            }
                            else if (type.IsSerializable)
                            {
                                logContext = $"known assembly {assembly.GetName().Name} where type has known base type.";
                            }
                        }

                        serializableTypes.RecordType(type, targetAssembly, logContext);
                    }

                    // Include grain interfaces and classes.
                    var isGrainInterface = GrainInterfaceUtils.IsGrainInterface(type);
                    var isGrainClass     = TypeUtils.IsConcreteGrainClass(type);
                    if (isGrainInterface || isGrainClass)
                    {
                        // If code generation is being performed at runtime, the interface must be accessible to the generated code.
                        if (!TypeUtilities.IsAccessibleFromAssembly(type, targetAssembly))
                        {
                            if (this.logger.IsEnabled(LogLevel.Debug))
                            {
                                this.logger.Debug("Skipping inaccessible grain type, {0}", type.GetParseableName());
                            }

                            continue;
                        }

                        // Attempt to generate serializers for grain state classes, i.e, T in Grain<T>.
                        var baseType = type.BaseType;
                        if (baseType != null && baseType.IsConstructedGenericType)
                        {
                            foreach (var arg in baseType.GetGenericArguments())
                            {
                                string logContext = null;
                                if (logger.IsEnabled(LogLevel.Trace))
                                {
                                    logContext = "generic base type of " + type.GetLogFormat();
                                }
                                this.serializableTypes.RecordType(arg, targetAssembly, logContext);
                            }
                        }

                        // Skip classes generated by this generator.
                        if (IsOrleansGeneratedCode(type))
                        {
                            if (this.logger.IsEnabled(LogLevel.Debug))
                            {
                                this.logger.Debug("Skipping generated grain type, {0}", type.GetParseableName());
                            }

                            continue;
                        }

                        if (this.knownGrainTypes.Contains(type))
                        {
                            if (this.logger.IsEnabled(LogLevel.Debug))
                            {
                                this.logger.Debug("Skipping grain type {0} since it already has generated code.", type.GetParseableName());
                            }

                            continue;
                        }

                        if (isGrainClass)
                        {
                            if (this.logger.IsEnabled(LogLevel.Information))
                            {
                                this.logger.Info("Found grain implementation class: {0}", type.GetParseableName());
                            }

                            grainClasses.Add(type);
                        }

                        if (isGrainInterface)
                        {
                            if (this.logger.IsEnabled(LogLevel.Information))
                            {
                                this.logger.Info("Found grain interface: {0}", type.GetParseableName());
                            }

                            GrainInterfaceUtils.ValidateInterfaceRules(type);

                            grainInterfaces.Add(type);
                        }
                    }
                }
            }

            // Group the types by namespace and generate the required code in each namespace.
            foreach (var groupedGrainInterfaces in grainInterfaces.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_)))
            {
                var namespaceName    = groupedGrainInterfaces.Key;
                var namespaceMembers = new List <MemberDeclarationSyntax>();
                foreach (var grainInterface in groupedGrainInterfaces)
                {
                    var referenceTypeName = GrainReferenceGenerator.GetGeneratedClassName(grainInterface);
                    var invokerTypeName   = GrainMethodInvokerGenerator.GetGeneratedClassName(grainInterface);
                    namespaceMembers.Add(
                        GrainReferenceGenerator.GenerateClass(
                            grainInterface,
                            referenceTypeName,
                            encounteredType =>
                    {
                        string logContext = null;
                        if (logger.IsEnabled(LogLevel.Trace))
                        {
                            logContext = "used by grain type " + grainInterface.GetLogFormat();
                        }
                        this.serializableTypes.RecordType(encounteredType, targetAssembly, logContext);
                    }));
                    namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(grainInterface, invokerTypeName));
                    var genericTypeSuffix = GetGenericTypeSuffix(grainInterface.GetGenericArguments().Length);
                    features.GrainInterfaces.Add(
                        new GrainInterfaceDescription
                    {
                        Interface   = grainInterface.GetTypeSyntax(includeGenericParameters: false),
                        Reference   = SF.ParseTypeName(namespaceName + '.' + referenceTypeName + genericTypeSuffix),
                        Invoker     = SF.ParseTypeName(namespaceName + '.' + invokerTypeName + genericTypeSuffix),
                        InterfaceId = GrainInterfaceUtils.GetGrainInterfaceId(grainInterface)
                    });
                }

                members.Add(CreateNamespace(namespaceName, namespaceMembers));
            }

            foreach (var type in grainClasses)
            {
                features.GrainClasses.Add(
                    new GrainClassDescription
                {
                    ClassType = type.GetTypeSyntax(includeGenericParameters: false)
                });
            }

            // Generate serializers into their own namespace.
            var serializerNamespace = this.GenerateSerializers(targetAssembly, features);

            members.Add(serializerNamespace);

            // Add serialization metadata for the types which were encountered.
            this.AddSerializationTypes(features.Serializers, targetAssembly, knownAssemblies.Keys.ToList());

            foreach (var attribute in knownAssemblies.Keys.SelectMany(asm => asm.GetCustomAttributes <ConsiderForCodeGenerationAttribute>()))
            {
                this.serializableTypes.RecordType(attribute.Type, targetAssembly, "[ConsiderForCodeGeneration]");
                if (attribute.ThrowOnFailure && !this.serializableTypes.IsTypeRecorded(attribute.Type) && !this.serializableTypes.IsTypeIgnored(attribute.Type))
                {
                    throw new CodeGenerationException(
                              $"Found {attribute.GetType().Name} for type {attribute.Type.GetParseableName()}, but code" +
                              " could not be generated. Ensure that the type is accessible.");
                }
            }

            // Generate metadata directives for all of the relevant types.
            var(attributeDeclarations, memberDeclarations) = FeaturePopulatorGenerator.GenerateSyntax(targetAssembly, features);
            members.AddRange(memberDeclarations);

            var compilationUnit = SF.CompilationUnit().AddAttributeLists(attributeDeclarations.ToArray()).AddMembers(members.ToArray());

            return(new GeneratedSyntax
            {
                SourceAssemblies = knownAssemblies.Keys.ToList(),
                Syntax = compilationUnit
            });
        }
Beispiel #14
0
        /// <summary>
        /// Generates a syntax tree for the provided assemblies.
        /// </summary>
        /// <param name="assemblies">The assemblies to generate code for.</param>
        /// <param name="runtime">Whether or not runtime code generation is being performed.</param>
        /// <returns>The generated syntax tree.</returns>
        private static GeneratedSyntax GenerateForAssemblies(List <Assembly> assemblies, bool runtime)
        {
            if (Logger.IsVerbose)
            {
                Logger.Verbose(
                    "Generating code for assemblies: {0}",
                    string.Join(", ", assemblies.Select(_ => _.FullName)));
            }

            Assembly       targetAssembly;
            HashSet <Type> ignoredTypes;

            if (runtime)
            {
                // Ignore types which have already been accounted for.
                ignoredTypes   = GetTypesWithGeneratedSupportClasses();
                targetAssembly = null;
            }
            else
            {
                ignoredTypes   = new HashSet <Type>();
                targetAssembly = assemblies.FirstOrDefault();
            }

            var members = new List <MemberDeclarationSyntax>();

            // If any KnownAssemblies have been specified, include them during code generation.
            var knownAssemblies =
                assemblies.SelectMany(_ => _.GetCustomAttributes <KnownAssemblyAttribute>())
                .Select(_ => _.Assembly)
                .Distinct()
                .ToSet();

            if (knownAssemblies.Count > 0)
            {
                knownAssemblies.UnionWith(assemblies);
                assemblies = knownAssemblies.ToList();
            }

            // Get types from assemblies which reference Orleans and are not generated assemblies.
            var includedTypes = new HashSet <Type>();

            for (var i = 0; i < assemblies.Count; i++)
            {
                var assembly = assemblies[i];
                foreach (var attribute in assembly.GetCustomAttributes <KnownTypeAttribute>())
                {
                    ConsiderType(attribute.Type, runtime, targetAssembly, includedTypes);
                }

                foreach (var type in assembly.DefinedTypes)
                {
                    ConsiderType(type, runtime, targetAssembly, includedTypes);
                }
            }

            includedTypes.RemoveWhere(_ => ignoredTypes.Contains(_));

            // Group the types by namespace and generate the required code in each namespace.
            foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_)))
            {
                var namespaceMembers = new List <MemberDeclarationSyntax>();
                foreach (var type in group)
                {
                    // The module containing the serializer.
                    var module = runtime ? null : type.Module;

                    // Every type which is encountered must be considered for serialization.
                    Action <Type> onEncounteredType = encounteredType =>
                    {
                        // If a type was encountered which can be accessed, process it for serialization.
                        SerializerGenerationManager.RecordTypeToGenerate(encounteredType, module, targetAssembly);
                    };

                    if (Logger.IsVerbose2)
                    {
                        Logger.Verbose2("Generating code for: {0}", type.GetParseableName());
                    }

                    if (GrainInterfaceData.IsGrainInterface(type))
                    {
                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating GrainReference and MethodInvoker for {0}",
                                type.GetParseableName());
                        }

                        GrainInterfaceData.ValidateInterfaceRules(type);

                        namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, onEncounteredType));
                        namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type));
                    }

                    // Generate serializers.
                    var  first = true;
                    Type toGen;
                    while (SerializerGenerationManager.GetNextTypeToProcess(out toGen))
                    {
                        if (!runtime)
                        {
                            if (first)
                            {
                                ConsoleText.WriteStatus("ClientGenerator - Generating serializer classes for types:");
                                first = false;
                            }

                            ConsoleText.WriteStatus(
                                "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace
                                + " defined in Assembly " + toGen.Assembly.GetName());
                        }

                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating & Registering Serializer for Type {0}",
                                toGen.GetParseableName());
                        }

                        namespaceMembers.AddRange(SerializerGenerator.GenerateClass(toGen, onEncounteredType));
                    }
                }

                if (namespaceMembers.Count == 0)
                {
                    if (Logger.IsVerbose)
                    {
                        Logger.Verbose2("Skipping namespace: {0}", group.Key);
                    }

                    continue;
                }

                members.Add(
                    SF.NamespaceDeclaration(SF.ParseName(group.Key))
                    .AddUsings(
                        TypeUtils.GetNamespaces(typeof(TaskUtility), typeof(GrainExtensions))
                        .Select(_ => SF.UsingDirective(SF.ParseName(_)))
                        .ToArray())
                    .AddMembers(namespaceMembers.ToArray()));
            }

            return(new GeneratedSyntax
            {
                SourceAssemblies = assemblies,
                Syntax = members.Count > 0 ? SF.CompilationUnit().AddMembers(members.ToArray()) : null
            });
        }
        public static (List <AttributeListSyntax>, List <MemberDeclarationSyntax>) GenerateSyntax(
            Assembly targetAssembly,
            List <GrainInterfaceDescription> grainInterfaces,
            List <GrainClassDescription> grainClasses,
            SerializationTypeDescriptions typeDescriptions)
        {
            var attributes = new List <AttributeListSyntax>();
            var members    = new List <MemberDeclarationSyntax>();
            var className  = CodeGeneratorCommon.ClassPrefix + Guid.NewGuid().ToString("N").Substring(0, 10) + ClassSuffix;

            // Generate a class for populating the metadata.
            var classSyntax = SF.ClassDeclaration(className)
                              .AddBaseListTypes(
                SF.SimpleBaseType(typeof(IFeaturePopulator <GrainInterfaceFeature>).GetTypeSyntax()),
                SF.SimpleBaseType(typeof(IFeaturePopulator <GrainClassFeature>).GetTypeSyntax()),
                SF.SimpleBaseType(typeof(IFeaturePopulator <SerializerFeature>).GetTypeSyntax()))
                              .AddModifiers(SF.Token(SyntaxKind.InternalKeyword), SF.Token(SyntaxKind.SealedKeyword))
                              .AddMembers(GeneratePopulateMethod(grainInterfaces), GeneratePopulateMethod(grainClasses), GeneratePopulateMethod(typeDescriptions))
                              .AddAttributeLists(SF.AttributeList(SF.SingletonSeparatedList(CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax())));

            var namespaceSyntax = SF.NamespaceDeclaration(NamespaceName.ToIdentifierName()).AddMembers(classSyntax);

            members.Add(namespaceSyntax);

            // Generate an assembly-level attribute with an instance of that class.
            var attribute = SF.AttributeList(
                SF.AttributeTargetSpecifier(SF.Token(SyntaxKind.AssemblyKeyword)),
                SF.SingletonSeparatedList(
                    SF.Attribute(typeof(FeaturePopulatorAttribute).GetNameSyntax())
                    .AddArgumentListArguments(SF.AttributeArgument(SF.TypeOfExpression(SF.ParseTypeName(NamespaceName + "." + className))))));

            attributes.Add(attribute);

            return(attributes, members);
        }
Beispiel #16
0
        /// <summary>
        /// Generates a syntax tree for the provided assemblies.
        /// </summary>
        /// <param name="assemblies">The assemblies to generate code for.</param>
        /// <param name="runtime">Whether or not runtime code generation is being performed.</param>
        /// <returns>The generated syntax tree.</returns>
        private GeneratedSyntax GenerateForAssemblies(List <Assembly> assemblies, bool runtime)
        {
            if (Logger.IsVerbose)
            {
                Logger.Verbose(
                    "Generating code for assemblies: {0}",
                    string.Join(", ", assemblies.Select(_ => _.FullName)));
            }

            Assembly       targetAssembly;
            HashSet <Type> ignoredTypes;

            if (runtime)
            {
                // Ignore types which have already been accounted for.
                ignoredTypes   = GetTypesWithGeneratedSupportClasses();
                targetAssembly = null;
            }
            else
            {
                ignoredTypes   = new HashSet <Type>();
                targetAssembly = assemblies.FirstOrDefault();
            }

            var members = new List <MemberDeclarationSyntax>();

            // Include assemblies which are marked as included.
            var knownAssemblyAttributes = new Dictionary <Assembly, KnownAssemblyAttribute>();
            var knownAssemblies         = new HashSet <Assembly>();

            foreach (var attribute in assemblies.SelectMany(asm => asm.GetCustomAttributes <KnownAssemblyAttribute>()))
            {
                knownAssemblyAttributes[attribute.Assembly] = attribute;
                knownAssemblies.Add(attribute.Assembly);
            }

            if (knownAssemblies.Count > 0)
            {
                knownAssemblies.UnionWith(assemblies);
                assemblies = knownAssemblies.ToList();
            }

            // Get types from assemblies which reference Orleans and are not generated assemblies.
            var includedTypes = new HashSet <Type>();

            for (var i = 0; i < assemblies.Count; i++)
            {
                var assembly = assemblies[i];
                foreach (var attribute in assembly.GetCustomAttributes <ConsiderForCodeGenerationAttribute>())
                {
                    ConsiderType(attribute.Type, runtime, targetAssembly, includedTypes, considerForSerialization: true);
                    if (attribute.ThrowOnFailure && !serializerGenerationManager.IsTypeRecorded(attribute.Type))
                    {
                        throw new CodeGenerationException(
                                  $"Found {attribute.GetType().Name} for type {attribute.Type.GetParseableName()}, but code"
                                  + " could not be generated. Ensure that the type is accessible.");
                    }
                }

                KnownAssemblyAttribute knownAssemblyAttribute;
                var considerAllTypesForSerialization = knownAssemblyAttributes.TryGetValue(assembly, out knownAssemblyAttribute) &&
                                                       knownAssemblyAttribute.TreatTypesAsSerializable;
                foreach (var type in TypeUtils.GetDefinedTypes(assembly, Logger))
                {
                    var considerForSerialization = considerAllTypesForSerialization || type.IsSerializable;
                    ConsiderType(type.AsType(), runtime, targetAssembly, includedTypes, considerForSerialization);
                }
            }

            includedTypes.RemoveWhere(_ => ignoredTypes.Contains(_));

            // Group the types by namespace and generate the required code in each namespace.
            foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_)))
            {
                var namespaceMembers = new List <MemberDeclarationSyntax>();
                foreach (var type in group)
                {
                    // The module containing the serializer.
                    var module = runtime ? null : type.GetTypeInfo().Module;

                    // Every type which is encountered must be considered for serialization.
                    Action <Type> onEncounteredType = encounteredType =>
                    {
                        // If a type was encountered which can be accessed, process it for serialization.
                        serializerGenerationManager.RecordTypeToGenerate(encounteredType, module, targetAssembly);
                    };

                    if (Logger.IsVerbose2)
                    {
                        Logger.Verbose2("Generating code for: {0}", type.GetParseableName());
                    }

                    if (GrainInterfaceUtils.IsGrainInterface(type))
                    {
                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating GrainReference and MethodInvoker for {0}",
                                type.GetParseableName());
                        }

                        GrainInterfaceUtils.ValidateInterfaceRules(type);

                        namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, onEncounteredType));
                        namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type));
                    }

                    // Generate serializers.
                    var  first = true;
                    Type toGen;
                    while (serializerGenerationManager.GetNextTypeToProcess(out toGen))
                    {
                        if (!runtime)
                        {
                            if (first)
                            {
                                ConsoleText.WriteStatus("ClientGenerator - Generating serializer classes for types:");
                                first = false;
                            }

                            ConsoleText.WriteStatus(
                                "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace
                                + " defined in Assembly " + toGen.GetTypeInfo().Assembly.GetName());
                        }

                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating & Registering Serializer for Type {0}",
                                toGen.GetParseableName());
                        }

                        namespaceMembers.Add(SerializerGenerator.GenerateClass(toGen, onEncounteredType));
                    }
                }

                if (namespaceMembers.Count == 0)
                {
                    if (Logger.IsVerbose)
                    {
                        Logger.Verbose2("Skipping namespace: {0}", group.Key);
                    }

                    continue;
                }

                members.Add(
                    SF.NamespaceDeclaration(SF.ParseName(group.Key))
                    .AddUsings(
                        TypeUtils.GetNamespaces(typeof(TaskUtility), typeof(GrainExtensions), typeof(IntrospectionExtensions))
                        .Select(_ => SF.UsingDirective(SF.ParseName(_)))
                        .ToArray())
                    .AddMembers(namespaceMembers.ToArray()));
            }

            return(new GeneratedSyntax
            {
                SourceAssemblies = assemblies,
                Syntax = members.Count > 0 ? SF.CompilationUnit().AddMembers(members.ToArray()) : null
            });
        }
Beispiel #17
0
        /// <summary>
        /// Generates a syntax tree for the provided assemblies.
        /// </summary>
        /// <param name="assemblies">The assemblies to generate code for.</param>
        /// <param name="runtime">Whether or not runtime code generation is being performed.</param>
        /// <returns>The generated syntax tree.</returns>
        private static GeneratedSyntax GenerateForAssemblies(List <Assembly> assemblies, bool runtime)
        {
            if (Logger.IsVerbose)
            {
                Logger.Verbose(
                    "Generating code for assemblies: {0}",
                    string.Join(", ", assemblies.Select(_ => _.FullName)));
            }

            Assembly       targetAssembly;
            HashSet <Type> ignoredTypes;

            if (runtime)
            {
                // Ignore types which have already been accounted for.
                ignoredTypes = CodeGeneratorCommon.GetTypesWithImplementations(
                    typeof(MethodInvokerAttribute),
                    typeof(GrainReferenceAttribute),
                    typeof(GrainStateAttribute),
                    typeof(SerializerAttribute));
                targetAssembly = null;
            }
            else
            {
                ignoredTypes   = new HashSet <Type>();
                targetAssembly = assemblies.FirstOrDefault();
            }

            var members = new List <MemberDeclarationSyntax>();

            // Get types from assemblies which reference Orleans and are not generated assemblies.
            var includedTypes = new HashSet <Type>();

            foreach (var type in assemblies.SelectMany(_ => _.GetTypes()))
            {
                // The module containing the serializer.
                var module = runtime ? null : type.Module;

                // Every type which is encountered must be considered for serialization.
                if (!type.IsNested && !type.IsGenericParameter && type.IsSerializable)
                {
                    // If a type was encountered which can be accessed, process it for serialization.
                    var isAccessibleForSerialization =
                        !TypeUtilities.IsTypeIsInaccessibleForSerialization(type, module, targetAssembly);
                    if (isAccessibleForSerialization)
                    {
                        includedTypes.Add(type);
                        SerializerGenerationManager.RecordTypeToGenerate(type);
                    }
                }

                // Collect the types which require code generation.
                if (GrainInterfaceData.IsGrainInterface(type))
                {
                    if (Logger.IsVerbose2)
                    {
                        Logger.Verbose2("Will generate code for: {0}", type.GetParseableName());
                    }

                    includedTypes.Add(type);
                }
            }

            includedTypes.RemoveWhere(_ => ignoredTypes.Contains(_));

            // Group the types by namespace and generate the required code in each namespace.
            foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_)))
            {
                var namespaceMembers = new List <MemberDeclarationSyntax>();
                foreach (var type in group)
                {
                    // The module containing the serializer.
                    var module = runtime ? null : type.Module;

                    // Every type which is encountered must be considered for serialization.
                    Action <Type> onEncounteredType = encounteredType =>
                    {
                        // If a type was encountered which can be accessed, process it for serialization.
                        var isAccessibleForSerialization =
                            !TypeUtilities.IsTypeIsInaccessibleForSerialization(encounteredType, module, targetAssembly);
                        if (isAccessibleForSerialization)
                        {
                            SerializerGenerationManager.RecordTypeToGenerate(encounteredType);
                        }
                    };

                    if (Logger.IsVerbose2)
                    {
                        Logger.Verbose2("Generating code for: {0}", type.GetParseableName());
                    }

                    if (GrainInterfaceData.IsGrainInterface(type))
                    {
                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating GrainReference and MethodInvoker for {0}",
                                type.GetParseableName());
                        }

                        namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, onEncounteredType));
                        namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type));
                    }

                    // Generate serializers.
                    var  first = true;
                    Type toGen;
                    while (SerializerGenerationManager.GetNextTypeToProcess(out toGen))
                    {
                        // Filter types which are inaccessible by the serialzation module/assembly.
                        var skipSerialzerGeneration =
                            toGen.GetAllFields()
                            .Any(
                                field =>
                                TypeUtilities.IsTypeIsInaccessibleForSerialization(
                                    field.FieldType,
                                    module,
                                    targetAssembly));
                        if (skipSerialzerGeneration)
                        {
                            continue;
                        }

                        if (!runtime)
                        {
                            if (first)
                            {
                                ConsoleText.WriteStatus("ClientGenerator - Generating serializer classes for types:");
                                first = false;
                            }

                            ConsoleText.WriteStatus(
                                "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace
                                + " defined in Assembly " + toGen.Assembly.GetName());
                        }

                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating & Registering Serializer for Type {0}",
                                toGen.GetParseableName());
                        }

                        namespaceMembers.AddRange(SerializerGenerator.GenerateClass(toGen, onEncounteredType));
                    }
                }

                if (namespaceMembers.Count == 0)
                {
                    if (Logger.IsVerbose)
                    {
                        Logger.Verbose2("Skipping namespace: {0}", group.Key);
                    }

                    continue;
                }

                members.Add(
                    SF.NamespaceDeclaration(SF.ParseName(group.Key))
                    .AddUsings(
                        TypeUtils.GetNamespaces(typeof(TaskUtility), typeof(GrainExtensions))
                        .Select(_ => SF.UsingDirective(SF.ParseName(_)))
                        .ToArray())
                    .AddMembers(namespaceMembers.ToArray()));
            }

            return(new GeneratedSyntax
            {
                SourceAssemblies = assemblies,
                Syntax = members.Count > 0 ? SF.CompilationUnit().AddMembers(members.ToArray()) : null
            });
        }
Beispiel #18
0
        /// <summary>
        /// Generates syntax for an invoke method.
        /// </summary>
        /// <param name="grainType">
        /// The grain type.
        /// </param>
        /// <param name="invokeMethod">
        /// The invoke method to generate.
        /// </param>
        /// <returns>
        /// Syntax for an invoke method.
        /// </returns>
        private static MethodDeclarationSyntax GenerateInvokeMethod(Type grainType, MethodInfo invokeMethod)
        {
            var parameters = invokeMethod.GetParameters();

            var grainArgument   = parameters[0].Name.ToIdentifierName();
            var requestArgument = parameters[1].Name.ToIdentifierName();

            // Store the relevant values from the request in local variables.
            var interfaceIdDeclaration =
                SF.LocalDeclarationStatement(
                    SF.VariableDeclaration(typeof(int).GetTypeSyntax())
                    .AddVariables(
                        SF.VariableDeclarator("interfaceId")
                        .WithInitializer(SF.EqualsValueClause(requestArgument.Member((InvokeMethodRequest _) => _.InterfaceId)))));
            var interfaceIdVariable = SF.IdentifierName("interfaceId");

            var methodIdDeclaration =
                SF.LocalDeclarationStatement(
                    SF.VariableDeclaration(typeof(int).GetTypeSyntax())
                    .AddVariables(
                        SF.VariableDeclarator("methodId")
                        .WithInitializer(SF.EqualsValueClause(requestArgument.Member((InvokeMethodRequest _) => _.MethodId)))));
            var methodIdVariable = SF.IdentifierName("methodId");

            var argumentsDeclaration =
                SF.LocalDeclarationStatement(
                    SF.VariableDeclaration(typeof(object[]).GetTypeSyntax())
                    .AddVariables(
                        SF.VariableDeclarator("arguments")
                        .WithInitializer(SF.EqualsValueClause(requestArgument.Member((InvokeMethodRequest _) => _.Arguments)))));
            var argumentsVariable = SF.IdentifierName("arguments");

            var methodDeclaration = invokeMethod.GetDeclarationSyntax()
                                    .AddBodyStatements(interfaceIdDeclaration, methodIdDeclaration, argumentsDeclaration);

            var interfaceCases = CodeGeneratorCommon.GenerateGrainInterfaceAndMethodSwitch(
                grainType,
                methodIdVariable,
                methodType => GenerateInvokeForMethod(grainType, grainArgument, methodType, argumentsVariable));

            // Generate the default case, which will throw a NotImplementedException.
            var errorMessage = SF.BinaryExpression(
                SyntaxKind.AddExpression,
                "interfaceId=".GetLiteralExpression(),
                interfaceIdVariable);
            var throwStatement =
                SF.ThrowStatement(
                    SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax())
                    .AddArgumentListArguments(SF.Argument(errorMessage)));
            var defaultCase       = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement);
            var interfaceIdSwitch =
                SF.SwitchStatement(interfaceIdVariable).AddSections(interfaceCases.ToArray()).AddSections(defaultCase);

            // If the provided grain is null, throw an argument exception.
            var argumentNullException =
                SF.ObjectCreationExpression(typeof(ArgumentNullException).GetTypeSyntax())
                .AddArgumentListArguments(SF.Argument(parameters[0].Name.GetLiteralExpression()));
            var grainArgumentCheck =
                SF.IfStatement(
                    SF.BinaryExpression(
                        SyntaxKind.EqualsExpression,
                        grainArgument,
                        SF.LiteralExpression(SyntaxKind.NullLiteralExpression)),
                    SF.ThrowStatement(argumentNullException));

            return(methodDeclaration.AddBodyStatements(grainArgumentCheck, interfaceIdSwitch));
        }
Beispiel #19
0
        /// <summary>
        /// Generates the class for the provided grain types.
        /// </summary>
        /// <param name="type">The grain interface type.</param>
        /// <param name="onEncounteredType">
        /// The callback invoked when a type is encountered.
        /// </param>
        /// <returns>
        /// The generated class.
        /// </returns>
        internal static IEnumerable <TypeDeclarationSyntax> GenerateClass(Type type, Action <Type> onEncounteredType)
        {
            var typeInfo     = type.GetTypeInfo();
            var genericTypes = typeInfo.IsGenericTypeDefinition
                                   ? typeInfo.GetGenericArguments().Select(_ => SF.TypeParameter(_.ToString())).ToArray()
                                   : new TypeParameterSyntax[0];

            var attributes = new List <AttributeSyntax>
            {
                CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(),
                SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()),
                SF.Attribute(typeof(SerializerAttribute).GetNameSyntax())
                .AddArgumentListArguments(
                    SF.AttributeArgument(SF.TypeOfExpression(type.GetTypeSyntax(includeGenericParameters: false))))
            };

            var className = CodeGeneratorCommon.ClassPrefix + type.GetParseableName(GeneratedTypeNameOptions);
            var fields    = GetFields(type);

            // Mark each field type for generation
            foreach (var field in fields)
            {
                var fieldType = field.FieldInfo.FieldType;
                onEncounteredType(fieldType);
            }

            var members = new List <MemberDeclarationSyntax>(GenerateStaticFields(fields))
            {
                GenerateDeepCopierMethod(type, fields),
                GenerateSerializerMethod(type, fields),
                GenerateDeserializerMethod(type, fields),
            };

            if (typeInfo.IsConstructedGenericType || !typeInfo.IsGenericTypeDefinition)
            {
                members.Add(GenerateRegisterMethod(type));
                members.Add(GenerateConstructor(className));
                attributes.Add(SF.Attribute(typeof(RegisterSerializerAttribute).GetNameSyntax()));
            }

            var classDeclaration =
                SF.ClassDeclaration(className)
                .AddModifiers(SF.Token(SyntaxKind.InternalKeyword))
                .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray()))
                .AddMembers(members.ToArray())
                .AddConstraintClauses(type.GetTypeConstraintSyntax());

            if (genericTypes.Length > 0)
            {
                classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes);
            }

            var classes = new List <TypeDeclarationSyntax> {
                classDeclaration
            };

            if (typeInfo.IsGenericTypeDefinition)
            {
                // Create a generic representation of the serializer type.
                var serializerType =
                    SF.GenericName(classDeclaration.Identifier)
                    .WithTypeArgumentList(
                        SF.TypeArgumentList()
                        .AddArguments(
                            type.GetGenericArguments()
                            .Select(_ => SF.OmittedTypeArgument())
                            .Cast <TypeSyntax>()
                            .ToArray()));
                var registererClassName = className + "_" +
                                          string.Join("_",
                                                      type.GetTypeInfo().GenericTypeParameters.Select(_ => _.Name)) + "_" +
                                          RegistererClassSuffix;
                classes.Add(
                    SF.ClassDeclaration(registererClassName)
                    .AddModifiers(SF.Token(SyntaxKind.InternalKeyword))
                    .AddAttributeLists(
                        SF.AttributeList()
                        .AddAttributes(
                            CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(),
                            SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()),
                            SF.Attribute(typeof(RegisterSerializerAttribute).GetNameSyntax())))
                    .AddMembers(
                        GenerateMasterRegisterMethod(type, serializerType),
                        GenerateConstructor(registererClassName)));
            }

            return(classes);
        }
        /// <summary>
        /// Generates a syntax tree for the provided assemblies.
        /// </summary>
        /// <param name="assemblies">The assemblies to generate code for.</param>
        /// <param name="runtime">Whether or not runtime code generation is being performed.</param>
        /// <returns>The generated syntax tree.</returns>
        private GeneratedSyntax GenerateForAssemblies(Assembly targetAssembly, bool runtime)
        {
            var grainInterfaces    = new List <GrainInterfaceDescription>();
            var grainClasses       = new List <GrainClassDescription>();
            var serializationTypes = new SerializationTypeDescriptions();

            var members = new List <MemberDeclarationSyntax>();

            // Expand the list of included assemblies and types.
            var(includedTypes, assemblies) = this.GetIncludedTypes(targetAssembly, runtime);

            if (Logger.IsVerbose)
            {
                Logger.Verbose(
                    "Generating code for assemblies: {0}",
                    string.Join(", ", assemblies.Select(_ => _.FullName)));
            }

            var serializerNamespaceMembers = new List <MemberDeclarationSyntax>();
            var serializerNamespaceName    = $"{SerializerNamespacePrefix}{targetAssembly?.GetName().Name.GetHashCode():X}";

            // Group the types by namespace and generate the required code in each namespace.
            foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_)))
            {
                var namespaceMembers = new List <MemberDeclarationSyntax>();
                var namespaceName    = group.Key;
                foreach (var type in group)
                {
                    // Skip generated classes.
                    if (type.GetCustomAttribute <GeneratedCodeAttribute>() != null)
                    {
                        continue;
                    }

                    // Every type which is encountered must be considered for serialization.
                    void OnEncounteredType(Type encounteredType)
                    {
                        // If a type was encountered which can be accessed, process it for serialization.
                        this.typeCollector.RecordEncounteredType(type);
                        this.serializerGenerationManager.RecordTypeToGenerate(encounteredType, targetAssembly);
                    }

                    if (Logger.IsVerbose2)
                    {
                        Logger.Verbose2("Generating code for: {0}", type.GetParseableName());
                    }

                    if (GrainInterfaceUtils.IsGrainInterface(type))
                    {
                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating GrainReference and MethodInvoker for {0}",
                                type.GetParseableName());
                        }

                        GrainInterfaceUtils.ValidateInterfaceRules(type);

                        var referenceTypeName = GrainReferenceGenerator.GetGeneratedClassName(type);
                        var invokerTypeName   = GrainMethodInvokerGenerator.GetGeneratedClassName(type);
                        namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, referenceTypeName, OnEncounteredType));
                        namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type, invokerTypeName));
                        var genericTypeSuffix = GetGenericTypeSuffix(type.GetGenericArguments().Length);
                        grainInterfaces.Add(
                            new GrainInterfaceDescription
                        {
                            Interface   = type.GetTypeSyntax(includeGenericParameters: false),
                            Reference   = SF.ParseTypeName(namespaceName + '.' + referenceTypeName + genericTypeSuffix),
                            Invoker     = SF.ParseTypeName(namespaceName + '.' + invokerTypeName + genericTypeSuffix),
                            InterfaceId = GrainInterfaceUtils.GetGrainInterfaceId(type)
                        });
                    }

                    if (TypeUtils.IsConcreteGrainClass(type))
                    {
                        grainClasses.Add(
                            new GrainClassDescription
                        {
                            ClassType = type.GetTypeSyntax(includeGenericParameters: false)
                        });
                    }

                    // Generate serializers.
                    var first = true;
                    while (this.serializerGenerationManager.GetNextTypeToProcess(out var toGen))
                    {
                        if (first)
                        {
                            Logger.Info("ClientGenerator - Generating serializer classes for types:");
                            first = false;
                        }

                        Logger.Info(
                            "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace
                            + " defined in Assembly " + toGen.GetTypeInfo().Assembly.GetName());

                        if (Logger.IsVerbose2)
                        {
                            Logger.Verbose2(
                                "Generating serializer for type {0}",
                                toGen.GetParseableName());
                        }

                        var generatedSerializerName = SerializerGenerator.GetGeneratedClassName(toGen);
                        serializerNamespaceMembers.Add(SerializerGenerator.GenerateClass(generatedSerializerName, toGen, OnEncounteredType));
                        var qualifiedSerializerName = serializerNamespaceName + '.' + generatedSerializerName + GetGenericTypeSuffix(toGen.GetGenericArguments().Length);
                        serializationTypes.SerializerTypes.Add(
                            new SerializerTypeDescription
                        {
                            Serializer = SF.ParseTypeName(qualifiedSerializerName),
                            Target     = toGen.GetTypeSyntax(includeGenericParameters: false)
                        });
                    }
                }

                if (namespaceMembers.Count == 0)
                {
                    if (Logger.IsVerbose)
                    {
                        Logger.Verbose2("Skipping namespace: {0}", namespaceName);
                    }

                    continue;
                }

                members.Add(CreateNamespace(namespaceName, namespaceMembers));
            }

            // Add all generated serializers to their own namespace.
            members.Add(CreateNamespace(serializerNamespaceName, serializerNamespaceMembers));

            // Add serialization metadata for the types which were encountered.
            this.AddSerializationTypes(serializationTypes, targetAssembly);

            // Generate metadata directives for all of the relevant types.
            var(attributeDeclarations, memberDeclarations) = FeaturePopulatorGenerator.GenerateSyntax(targetAssembly, grainInterfaces, grainClasses, serializationTypes);
            members.AddRange(memberDeclarations);

            var compilationUnit = SF.CompilationUnit().AddAttributeLists(attributeDeclarations.ToArray()).AddMembers(members.ToArray());

            return(new GeneratedSyntax
            {
                SourceAssemblies = assemblies,
                Syntax = compilationUnit
            });

            string GetGenericTypeSuffix(int numParams)
            {
                if (numParams == 0)
                {
                    return(string.Empty);
                }
                return('<' + new string(',', numParams - 1) + '>');
            }

            NamespaceDeclarationSyntax CreateNamespace(string namespaceName, IEnumerable <MemberDeclarationSyntax> namespaceMembers)
            {
                return
                    (SF.NamespaceDeclaration(SF.ParseName(namespaceName))
                     .AddUsings(
                         TypeUtils.GetNamespaces(typeof(GrainExtensions), typeof(IntrospectionExtensions))
                         .Select(_ => SF.UsingDirective(SF.ParseName(_)))
                         .ToArray())
                     .AddMembers(namespaceMembers.ToArray()));
            }
        }