예제 #1
0
        private NamespaceDeclarationSyntax GenerateSerializers(Assembly targetAssembly, FeatureDescriptions features)
        {
            var serializerNamespaceMembers = new List <MemberDeclarationSyntax>();
            var serializerNamespaceName    = $"{SerializerNamespacePrefix}{targetAssembly?.GetName().Name.GetHashCode():X}";

            while (this.serializableTypes.GetNextTypeToProcess(out var toGen))
            {
                if (this.logger.IsEnabled(LogLevel.Trace))
                {
                    this.logger.Trace("Generating serializer for type {0}", toGen.GetParseableName());
                }

                var type = toGen;
                var generatedSerializerName = SerializerGenerator.GetGeneratedClassName(toGen);
                serializerNamespaceMembers.Add(SerializerGenerator.GenerateClass(generatedSerializerName, toGen, encounteredType =>
                {
                    string logContext = null;
                    if (logger.IsEnabled(LogLevel.Trace))
                    {
                        logContext = "generated serializer for " + type.GetLogFormat();
                    }
                    this.serializableTypes.RecordType(encounteredType, targetAssembly, logContext);
                }));
                var qualifiedSerializerName = serializerNamespaceName + '.' + generatedSerializerName + GetGenericTypeSuffix(toGen.GetGenericArguments().Length);
                features.Serializers.SerializerTypes.Add(
                    new SerializerTypeDescription
                {
                    Serializer = SF.ParseTypeName(qualifiedSerializerName),
                    Target     = toGen.GetTypeSyntax(includeGenericParameters: false)
                });
            }

            // Add all generated serializers to their own namespace.
            return(CreateNamespace(serializerNamespaceName, serializerNamespaceMembers));
        }
예제 #2
0
        private CompilationUnitSyntax GenerateSyntax(AggregatedModel model)
        {
            var namespaceGroupings = new Dictionary <INamespaceSymbol, List <MemberDeclarationSyntax> >();

            // Pass the relevent elements of the model to each of the code generators.
            foreach (var grainInterface in model.GrainInterfaces)
            {
                var nsMembers = GetNamespace(namespaceGroupings, grainInterface.Type.ContainingNamespace);
                nsMembers.Add(GrainMethodInvokerGenerator.GenerateClass(this.wellKnownTypes, grainInterface));
                nsMembers.Add(GrainReferenceGenerator.GenerateClass(this.wellKnownTypes, grainInterface));
            }

            var serializersToGenerate = model.Serializers.SerializerTypes
                                        .Where(s => s.SerializerTypeSyntax == null)
                                        .Distinct(SerializerTypeDescription.TargetComparer);

            foreach (var serializerType in serializersToGenerate)
            {
                var nsMembers = GetNamespace(namespaceGroupings, serializerType.Target.ContainingNamespace);
                TypeDeclarationSyntax generated;
                (generated, serializerType.SerializerTypeSyntax) = SerializerGenerator.GenerateClass(this.wellKnownTypes, this.semanticModelForAccessibility, serializerType);
                nsMembers.Add(generated);
            }

            var compilationMembers = new List <MemberDeclarationSyntax>();

            // Group the generated code by namespace since serialized types, such as the generated GrainReference classes must have a stable namespace.
            foreach (var group in namespaceGroupings)
            {
                var ns      = group.Key;
                var members = group.Value;
                if (ns.IsGlobalNamespace)
                {
                    compilationMembers.AddRange(members);
                }
                else
                {
                    compilationMembers.Add(NamespaceDeclaration(ParseName(ns.ToDisplayString())).AddMembers(members.ToArray()));
                }
            }

            // Add and generate feature populators to tie everything together.
            var(attributes, featurePopulators) = FeaturePopulatorGenerator.GenerateSyntax(this.wellKnownTypes, model);
            compilationMembers.AddRange(featurePopulators);

            return(CompilationUnit()
                   .AddUsings(UsingDirective(ParseName("global::Orleans")))
                   .WithAttributeLists(List(attributes))
                   .WithMembers(List(compilationMembers)));

            List <MemberDeclarationSyntax> GetNamespace(Dictionary <INamespaceSymbol, List <MemberDeclarationSyntax> > namespaces, INamespaceSymbol ns)
            {
                if (namespaces.TryGetValue(ns, out var result))
                {
                    return(result);
                }
                return(namespaces[ns] = new List <MemberDeclarationSyntax>());
            }
        }
예제 #3
0
        public static TypeSyntax GetCodecTypeName(this ISerializableTypeDescription type)
        {
            var genericArity = type.TypeParameters.Count;
            var name         = SerializerGenerator.GetSimpleClassName(type);

            if (genericArity > 0)
            {
                name += $"<{new string(',', genericArity - 1)}>";
            }

            return(ParseTypeName(type.GeneratedNamespace + "." + name));
        }
예제 #4
0
        public CodeGenerator(Compilation compilation, CodeGeneratorOptions options, ILogger log)
        {
            this.compilation         = compilation;
            this.options             = options;
            this.log                 = log;
            this.wellKnownTypes      = new WellKnownTypes(compilation);
            this.compilationAnalyzer = new CompilationAnalyzer(log, this.wellKnownTypes, compilation);

            var firstSyntaxTree = compilation.SyntaxTrees.FirstOrDefault() ?? throw new InvalidOperationException("Compilation has no syntax trees.");

            this.semanticModelForAccessibility = compilation.GetSemanticModel(firstSyntaxTree);
            this.serializerTypeAnalyzer        = SerializerTypeAnalyzer.Create(this.wellKnownTypes);
            this.serializerGenerator           = new SerializerGenerator(this.options, this.wellKnownTypes);
            this.grainMethodInvokerGenerator   = new GrainMethodInvokerGenerator(this.options, this.wellKnownTypes);
            this.grainReferenceGenerator       = new GrainReferenceGenerator(this.options, this.wellKnownTypes);
        }
예제 #5
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
            });
        }
예제 #6
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
            });
        }
예제 #7
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
            });
        }
예제 #8
0
        private void ProcessSerializableType(AggregatedModel model, INamedTypeSymbol type)
        {
            if (!ValidForKnownTypes(type))
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping abstract type {type}");
                }
                return;
            }

            AddKnownType(model, type);

            var serializerModel = model.Serializers;

            if (type.IsAbstract)
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping abstract type {type}");
                }
                return;
            }

            // Ensure that the type is accessible from generated code.
            var accessible = this.semanticModelForAccessibility.IsAccessible(0, type);

            if (!accessible)
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping inaccessible type {type}");
                }
                return;
            }

            if (type.HasBaseType(this.wellKnownTypes.Exception))
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping Exception type {type}");
                }
                return;
            }

            if (type.HasBaseType(this.wellKnownTypes.Delegate))
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping Delegate type {type}");
                }
                return;
            }

            if (type.AllInterfaces.Contains(this.wellKnownTypes.IAddressable))
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping IAddressable type {type}");
                }
                return;
            }

            // Account for types that serialize themselves and/or are serializers for other types.
            var selfSerializing = false;

            if (this.serializerTypeAnalyzer.IsSerializer(type, out var serializerTargets))
            {
                var typeSyntax = type.ToTypeSyntax();
                foreach (var target in serializerTargets)
                {
                    if (this.log.IsEnabled(LogLevel.Trace))
                    {
                        this.log.LogTrace($"{nameof(ProcessSerializableType)} type {type} is a serializer for {target}");
                    }

                    if (target.Equals(type))
                    {
                        selfSerializing = true;
                        typeSyntax      = type.WithoutTypeParameters().ToTypeSyntax();
                    }

                    serializerModel.SerializerTypes.Add(new SerializerTypeDescription
                    {
                        Target = target,
                        SerializerTypeSyntax       = typeSyntax,
                        OverrideExistingSerializer = true
                    });
                }
            }

            if (selfSerializing)
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping serializer generation for self-serializing type {type}");
                }
                return;
            }

            if (type.HasAttribute(this.wellKnownTypes.GeneratedCodeAttribute))
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} type {type} is a generated type and no serializer will be generated for it");
                }

                return;
            }

            if (type.IsStatic)
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} type {type} is a static type and no serializer will be generated for it");
                }

                return;
            }

            if (type.TypeParameters.Any(p => p.ConstraintTypes.Any(c => c.Equals(this.wellKnownTypes.Delegate))))
            {
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping type with Delegate parameter constraint, {type}");
                }
                return;
            }

            var isSerializable = this.compilationAnalyzer.IsSerializable(type);

            if (this.compilationAnalyzer.IsFromKnownAssembly(type) && isSerializable)
            {
                // Skip types which have fields whose types are inaccessible from generated code.
                foreach (var field in type.GetAllMembers <IFieldSymbol>())
                {
                    // Ignore fields which won't be serialized anyway.
                    if (!SerializerGenerator.ShouldSerializeField(this.wellKnownTypes, field))
                    {
                        if (this.log.IsEnabled(LogLevel.Trace))
                        {
                            this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping non-serialized field {field} in type {type}");
                        }

                        continue;
                    }

                    // Check field type accessibility.
                    var fieldAccessible = this.semanticModelForAccessibility.IsAccessible(0, field.Type);
                    if (!fieldAccessible)
                    {
                        if (this.log.IsEnabled(LogLevel.Trace))
                        {
                            this.log.LogTrace($"{nameof(ProcessSerializableType)} skipping type {type} with inaccessible field type {field.Type} (field: {field})");
                        }

                        return;
                    }
                }

                // Add the type that needs generation.
                // The serializer generator will fill in the missing SerializerTypeSyntax field with the
                // generated type.
                if (this.log.IsEnabled(LogLevel.Trace))
                {
                    this.log.LogTrace($"{nameof(ProcessSerializableType)} will generate a serializer for type {type}");
                }

                serializerModel.SerializerTypes.Add(new SerializerTypeDescription
                {
                    Target = type
                });
            }
            else if (this.log.IsEnabled(LogLevel.Trace))
            {
                this.log.LogTrace($"{nameof(ProcessSerializableType)} will not generate a serializer for type {type}");
            }
        }
예제 #9
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()));
            }
        }
        private CompilationUnitSyntax GenerateSyntax(AggregatedModel model)
        {
            var namespaceGroupings = new Dictionary <INamespaceSymbol, List <MemberDeclarationSyntax> >();

            // Pass the relevant elements of the model to each of the code generators.
            foreach (var grainInterface in model.GrainInterfaces)
            {
                var nsMembers = GetNamespace(namespaceGroupings, grainInterface.Type.ContainingNamespace);
                nsMembers.Add(GrainMethodInvokerGenerator.GenerateClass(this.wellKnownTypes, grainInterface));
                nsMembers.Add(GrainReferenceGenerator.GenerateClass(this.wellKnownTypes, grainInterface));
            }

            var serializersToGenerate = model.Serializers.SerializerTypes
                                        .Where(s => s.SerializerTypeSyntax == null)
                                        .Distinct(SerializerTypeDescription.TargetComparer);

            foreach (var serializerType in serializersToGenerate)
            {
                var nsMembers = GetNamespace(namespaceGroupings, serializerType.Target.ContainingNamespace);
                TypeDeclarationSyntax generated;
                (generated, serializerType.SerializerTypeSyntax) = SerializerGenerator.GenerateClass(this.wellKnownTypes, this.semanticModelForAccessibility, serializerType, this.log);
                nsMembers.Add(generated);
            }

            var compilationMembers = new List <MemberDeclarationSyntax>();

            // Group the generated code by namespace since serialized types, such as the generated GrainReference classes must have a stable namespace.
            foreach (var group in namespaceGroupings)
            {
                var ns      = group.Key;
                var members = group.Value;
                if (ns.IsGlobalNamespace)
                {
                    compilationMembers.AddRange(members);
                }
                else
                {
                    compilationMembers.Add(NamespaceDeclaration(ParseName(ns.ToDisplayString())).AddMembers(members.ToArray()));
                }
            }

            // Add and generate feature populators to tie everything together.
            var(attributes, featurePopulators) = FeaturePopulatorGenerator.GenerateSyntax(this.wellKnownTypes, model);
            compilationMembers.AddRange(featurePopulators);

            // Add some attributes detailing which assemblies this generated code targets.
            attributes.Add(AttributeList(
                               AttributeTargetSpecifier(Token(SyntaxKind.AssemblyKeyword)),
                               SeparatedList(GetCodeGenerationTargetAttribute().ToArray())));

            return(CompilationUnit()
                   .AddUsings(UsingDirective(ParseName("global::Orleans")))
                   .WithAttributeLists(List(attributes))
                   .WithMembers(List(compilationMembers)));

            List <MemberDeclarationSyntax> GetNamespace(Dictionary <INamespaceSymbol, List <MemberDeclarationSyntax> > namespaces, INamespaceSymbol ns)
            {
                if (namespaces.TryGetValue(ns, out var result))
                {
                    return(result);
                }
                return(namespaces[ns] = new List <MemberDeclarationSyntax>());
            }

            IEnumerable <AttributeSyntax> GetCodeGenerationTargetAttribute()
            {
                yield return(GenerateAttribute(this.compilation.Assembly));

                foreach (var assembly in this.compilationAnalyzer.ReferencedAssemblies)
                {
                    if (this.compilationAnalyzer.AssembliesExcludedFromCodeGeneration.Contains(assembly) ||
                        this.compilationAnalyzer.AssembliesExcludedFromMetadataGeneration.Contains(assembly))
                    {
                        continue;
                    }

                    yield return(GenerateAttribute(assembly));
                }

                AttributeSyntax GenerateAttribute(IAssemblySymbol assembly)
                {
                    var assemblyName = assembly.Identity.GetDisplayName(fullKey: true);

                    this.log.LogTrace($"Adding [assembly: OrleansCodeGenerationTarget(\"{assemblyName}\")]");
                    var nameSyntax = this.wellKnownTypes.OrleansCodeGenerationTargetAttribute.ToNameSyntax();

                    return(Attribute(nameSyntax)
                           .AddArgumentListArguments(AttributeArgument(assemblyName.ToLiteralExpression())));
                }
            }
        }