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);
        }
        private static MemberDeclarationSyntax GeneratePopulateMethod(SerializationTypeDescriptions typeDescriptions)
        {
            var interfaceMethod  = TypeUtils.Method((IFeaturePopulator <SerializerFeature> _) => _.Populate(default(SerializerFeature)));
            var featureParameter = interfaceMethod.GetParameters()[0].Name.ToIdentifierName();
            var bodyStatements   = new List <StatementSyntax>();

            var addSerializerTypeMethod = TypeUtils.Method((SerializerFeature _) => _.AddSerializerType(default(Type), default(Type)));

            foreach (var serializerType in typeDescriptions.SerializerTypes)
            {
                bodyStatements.Add(
                    SF.ExpressionStatement(
                        SF.InvocationExpression(featureParameter.Member(addSerializerTypeMethod.Name))
                        .AddArgumentListArguments(
                            SF.Argument(SF.TypeOfExpression(serializerType.Target)),
                            SF.Argument(SF.TypeOfExpression(serializerType.Serializer)))));
            }

            var addKnownType = TypeUtils.Method((SerializerFeature _) => _.AddKnownType(default(string), default(string)));

            foreach (var knownType in typeDescriptions.KnownTypes)
            {
                bodyStatements.Add(
                    SF.ExpressionStatement(
                        SF.InvocationExpression(featureParameter.Member(addKnownType.Name))
                        .AddArgumentListArguments(
                            SF.Argument(knownType.Type.GetLiteralExpression()),
                            SF.Argument(knownType.TypeKey.GetLiteralExpression()))));
            }

            return(interfaceMethod.GetDeclarationSyntax().AddBodyStatements(bodyStatements.ToArray()));
        }
Exemplo n.º 3
0
        /// <summary>
        /// Adds serialization type descriptions from <paramref name="targetAssembly"/> to <paramref name="serializationTypes"/>.
        /// </summary>
        /// <param name="serializationTypes">The serialization type descriptions.</param>
        /// <param name="targetAssembly">The target assembly for generated code.</param>
        /// <param name="assemblies"></param>
        private void AddSerializationTypes(SerializationTypeDescriptions serializationTypes, Assembly targetAssembly, List <Assembly> assemblies)
        {
            // Only types which exist in assemblies referenced by the target assembly can be referenced.
            var references = new HashSet <string>(
                assemblies.SelectMany(asm =>
                                      asm.GetReferencedAssemblies()
                                      .Select(referenced => referenced.Name)
                                      .Concat(new[] { asm.GetName().Name })));

            bool IsAssemblyReferenced(Type type)
            {
                // If the target doesn't reference this type's assembly, it cannot reference a type within that assembly.
                return(references.Contains(type.Assembly.GetName().Name));
            }

            // Visit all types in other assemblies for serialization metadata.
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (!references.Contains(assembly.GetName().Name))
                {
                    continue;
                }
                foreach (var type in TypeUtils.GetDefinedTypes(assembly, this.logger))
                {
                    this.typeCollector.RecordEncounteredType(type);
                }
            }

            // Returns true if a type can be accessed from source and false otherwise.
            bool IsAccessibleType(Type type) => TypeUtilities.IsAccessibleFromAssembly(type, targetAssembly);

            foreach (var type in this.typeCollector.EncounteredTypes)
            {
                // Skip types which can not or should not be referenced.
                if (type.IsGenericParameter)
                {
                    continue;
                }
                if (!IsAssemblyReferenced(type))
                {
                    continue;
                }
                if (type.IsNestedPrivate)
                {
                    continue;
                }
                if (type.GetCustomAttribute <CompilerGeneratedAttribute>() != null)
                {
                    continue;
                }
                if (IsOrleansGeneratedCode(type))
                {
                    continue;
                }

                var qualifiedTypeName = RuntimeTypeNameFormatter.Format(type);
                if (this.knownTypes.Contains(qualifiedTypeName))
                {
                    continue;
                }

                var typeKeyString = type.OrleansTypeKeyString();
                serializationTypes.KnownTypes.Add(new KnownTypeDescription
                {
                    Type    = qualifiedTypeName,
                    TypeKey = typeKeyString
                });

                if (this.logger.IsEnabled(LogLevel.Debug))
                {
                    this.logger.Debug(
                        "Found type {0} with type key \"{1}\"",
                        type.GetParseableName(),
                        typeKeyString);
                }

                if (!IsAccessibleType(type))
                {
                    continue;
                }

                var typeSyntax           = type.GetTypeSyntax(includeGenericParameters: false);
                var serializerAttributes = type.GetCustomAttributes <SerializerAttribute>().ToList();
                if (serializerAttributes.Count > 0)
                {
                    // Account for serializer types.
                    foreach (var serializerAttribute in serializerAttributes)
                    {
                        if (!IsAccessibleType(serializerAttribute.TargetType))
                        {
                            continue;
                        }

                        if (this.logger.IsEnabled(LogLevel.Information))
                        {
                            this.logger.Info(
                                "Found type {0} is a serializer for type {1}",
                                type.GetParseableName(),
                                serializerAttribute.TargetType.GetParseableName());
                        }

                        serializationTypes.SerializerTypes.Add(
                            new SerializerTypeDescription
                        {
                            Serializer = typeSyntax,
                            Target     = serializerAttribute.TargetType.GetTypeSyntax(includeGenericParameters: false)
                        });
                    }
                }
                else
                {
                    // Account for self-serializing types.
                    SerializationManager.GetSerializationMethods(type, out var copier, out var serializer, out var deserializer);
                    if (copier != null || serializer != null || deserializer != null)
                    {
                        if (this.logger.IsEnabled(LogLevel.Information))
                        {
                            this.logger.Info(
                                "Found type {0} is self-serializing.",
                                type.GetParseableName());
                        }

                        serializationTypes.SerializerTypes.Add(
                            new SerializerTypeDescription
                        {
                            Serializer = typeSyntax,
                            Target     = typeSyntax
                        });
                    }
                }
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Adds serialization type descriptions from <paramref name="types"/> to <paramref name="serializationTypes"/>.
        /// </summary>
        /// <param name="serializationTypes">The serialization type descriptions.</param>
        /// <param name="targetAssembly">The target assembly for generated code.</param>
        /// <param name="types">The types.</param>
        private void AddSerializationTypes(SerializationTypeDescriptions serializationTypes, Assembly targetAssembly)
        {
            // Only types which exist in assemblies referenced by the target assembly can be referenced.
            var references = new HashSet <string>(targetAssembly.GetReferencedAssemblies().Select(asm => asm.Name));

            bool IsAssemblyReferenced(Type type)
            {
                // If the target doesn't reference this type's assembly, it cannot reference a type within that assembly.
                var asmName = type.Assembly.GetName().Name;

                if (type.Assembly != targetAssembly)
                {
                    if (!references.Contains(asmName))
                    {
                        return(false);
                    }
                    if (!type.IsSerializable)
                    {
                        return(false);
                    }
                }

                return(true);
            }

            // Visit all types in other assemblies for serialization metadata.
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (!references.Contains(assembly.GetName().Name))
                {
                    continue;
                }
                foreach (var type in TypeUtils.GetDefinedTypes(assembly, this.Logger))
                {
                    this.typeCollector.RecordEncounteredType(type);
                }
            }

            // Returns true if a type can be accessed from source and false otherwise.
            bool IsAccessibleType(Type type)
            {
                var accessible = !type.IsGenericParameter;

                if (type.IsSpecialName)
                {
                    accessible = false;
                }

                if (type.GetCustomAttribute <CompilerGeneratedAttribute>() != null)
                {
                    accessible = false;
                }

                // Obsolete types can be accessed, however obsolete types which have IsError set cannot.
                var obsoleteAttr = type.GetCustomAttribute <ObsoleteAttribute>();

                if (obsoleteAttr != null && obsoleteAttr.IsError)
                {
                    accessible = false;
                }

                if (!TypeUtilities.IsAccessibleFromAssembly(type, targetAssembly))
                {
                    accessible = false;
                }

                return(accessible);
            }

            foreach (var type in this.typeCollector.EncounteredTypes)
            {
                // Skip types which can not or should not be referenced.
                if (!IsAssemblyReferenced(type))
                {
                    continue;
                }
                if (type.IsNestedPrivate)
                {
                    continue;
                }
                if (type.GetCustomAttribute <CompilerGeneratedAttribute>() != null)
                {
                    continue;
                }
                if (type.GetCustomAttribute <GeneratedCodeAttribute>() != null)
                {
                    continue;
                }

                var qualifiedTypeName = RuntimeTypeNameFormatter.Format(type);
                if (this.knownTypes.Contains(qualifiedTypeName))
                {
                    continue;
                }

                serializationTypes.KnownTypes.Add(new KnownTypeDescription
                {
                    Type    = qualifiedTypeName,
                    TypeKey = type.OrleansTypeKeyString()
                });

                if (!IsAccessibleType(type))
                {
                    continue;
                }

                var typeSyntax           = type.GetTypeSyntax(includeGenericParameters: false);
                var serializerAttributes = type.GetCustomAttributes <SerializerAttribute>().ToList();
                if (serializerAttributes.Count > 0)
                {
                    // Account for serializer types.
                    foreach (var serializerAttribute in serializerAttributes)
                    {
                        if (!IsAccessibleType(serializerAttribute.TargetType))
                        {
                            continue;
                        }
                        serializationTypes.SerializerTypes.Add(
                            new SerializerTypeDescription
                        {
                            Serializer = typeSyntax,
                            Target     = serializerAttribute.TargetType.GetTypeSyntax(includeGenericParameters: false)
                        });
                    }
                }
                else
                {
                    // Account for self-serializing types.
                    SerializationManager.GetSerializationMethods(type, out var copier, out var serializer, out var deserializer);
                    if (copier != null || serializer != null || deserializer != null)
                    {
                        serializationTypes.SerializerTypes.Add(
                            new SerializerTypeDescription
                        {
                            Serializer = typeSyntax,
                            Target     = typeSyntax
                        });
                    }
                }
            }
        }
Exemplo n.º 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(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()));
            }
        }