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)); }
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>()); } }
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)); }
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); }
/// <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 }); }
/// <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 }); }
/// <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 }); }
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}"); } }
/// <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()))); } } }