コード例 #1
0
        /// <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
            });
        }
コード例 #2
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(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()));
            }
        }