private static MethodDeclarationSyntax GenerateGetMethodNameMethod(Type grainType) { var method = TypeUtils.Method((GrainReference _) => _.GetMethodName(default(int), default(int))); var methodDeclaration = method.GetDeclarationSyntax() .AddModifiers(SF.Token(SyntaxKind.OverrideKeyword)); var parameters = method.GetParameters(); var interfaceIdArgument = parameters[0].Name.ToIdentifierName(); var methodIdArgument = parameters[1].Name.ToIdentifierName(); var interfaceCases = CodeGeneratorCommon.GenerateGrainInterfaceAndMethodSwitch( grainType, methodIdArgument, methodType => new StatementSyntax[] { SF.ReturnStatement(methodType.Name.GetLiteralExpression()) }); // Generate the default case, which will throw a NotImplementedException. var errorMessage = SF.BinaryExpression( SyntaxKind.AddExpression, "interfaceId=".GetLiteralExpression(), interfaceIdArgument); var throwStatement = SF.ThrowStatement( SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(errorMessage))); var defaultCase = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement); var interfaceIdSwitch = SF.SwitchStatement(interfaceIdArgument).AddSections(interfaceCases.ToArray()).AddSections(defaultCase); return(methodDeclaration.AddBodyStatements(interfaceIdSwitch)); }
/// <summary> /// Compiles the provided syntax tree, and loads and returns the result. /// </summary> /// <param name="generatedSyntax">The syntax tree.</param> /// <returns>The compilation output.</returns> private static byte[] CompileAndLoad(GeneratedSyntax generatedSyntax) { var rawAssembly = CodeGeneratorCommon.CompileAssembly(generatedSyntax, "OrleansCodeGen.dll"); Assembly.Load(rawAssembly); return(rawAssembly); }
/// <summary> /// Generates the class for the provided grain types. /// </summary> /// <param name="grainType"> /// The grain interface type. /// </param> /// <returns> /// The generated class. /// </returns> internal static TypeDeclarationSyntax GenerateClass(Type grainType) { var baseTypes = new List <BaseTypeSyntax> { SF.SimpleBaseType(typeof(IGrainMethodInvoker).GetTypeSyntax()) }; var grainTypeInfo = grainType.GetTypeInfo(); var genericTypes = grainTypeInfo.IsGenericTypeDefinition ? grainTypeInfo.GetGenericArguments() .Select(_ => SF.TypeParameter(_.ToString())) .ToArray() : new TypeParameterSyntax[0]; // Create the special method invoker marker attribute. var interfaceId = GrainInterfaceUtils.GetGrainInterfaceId(grainType); var interfaceIdArgument = SF.LiteralExpression(SyntaxKind.NumericLiteralExpression, SF.Literal(interfaceId)); var grainTypeArgument = SF.TypeOfExpression(grainType.GetTypeSyntax(includeGenericParameters: false)); var attributes = new List <AttributeSyntax> { CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(), SF.Attribute(typeof(MethodInvokerAttribute).GetNameSyntax()) .AddArgumentListArguments( SF.AttributeArgument(grainType.GetParseableName().GetLiteralExpression()), SF.AttributeArgument(interfaceIdArgument), SF.AttributeArgument(grainTypeArgument)), #if !NETSTANDARD SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()) #endif }; var members = new List <MemberDeclarationSyntax> { GenerateInvokeMethod(grainType), GenerateInterfaceIdProperty(grainType) }; // If this is an IGrainExtension, make the generated class implement IGrainExtensionMethodInvoker. if (typeof(IGrainExtension).IsAssignableFrom(grainType)) { baseTypes.Add(SF.SimpleBaseType(typeof(IGrainExtensionMethodInvoker).GetTypeSyntax())); members.Add(GenerateExtensionInvokeMethod(grainType)); } var classDeclaration = SF.ClassDeclaration( CodeGeneratorCommon.ClassPrefix + TypeUtils.GetSuitableClassName(grainType) + ClassSuffix) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword)) .AddBaseListTypes(baseTypes.ToArray()) .AddConstraintClauses(grainType.GetTypeConstraintSyntax()) .AddMembers(members.ToArray()) .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray())); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } return(classDeclaration); }
/// <summary> /// Generates syntax for an invoke method. /// </summary> /// <param name="grainType"> /// The grain type. /// </param> /// <param name="invokeMethod"> /// The invoke method to generate. /// </param> /// <returns> /// Syntax for an invoke method. /// </returns> private static MethodDeclarationSyntax GenerateInvokeMethod(Type grainType, MethodInfo invokeMethod) { var methodDeclaration = invokeMethod.GetDeclarationSyntax(); var parameters = invokeMethod.GetParameters(); var grainArgument = parameters[0].Name.ToIdentifierName(); var interfaceIdArgument = parameters[1].Name.ToIdentifierName(); var methodIdArgument = parameters[2].Name.ToIdentifierName(); var argumentsArgument = parameters[3].Name.ToIdentifierName(); var interfaceCases = CodeGeneratorCommon.GenerateGrainInterfaceAndMethodSwitch( grainType, methodIdArgument, methodType => GenerateInvokeForMethod(grainType, grainArgument, methodType, argumentsArgument)); // Generate the default case, which will throw a NotImplementedException. var errorMessage = SF.BinaryExpression( SyntaxKind.AddExpression, "interfaceId=".GetLiteralExpression(), interfaceIdArgument); var throwStatement = SF.ThrowStatement( SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(errorMessage))); var defaultCase = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement); var interfaceIdSwitch = SF.SwitchStatement(interfaceIdArgument).AddSections(interfaceCases.ToArray()).AddSections(defaultCase); // If the provided grain is null, throw an argument exception. var argumentNullException = SF.ObjectCreationExpression(typeof(ArgumentNullException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(parameters[0].Name.GetLiteralExpression())); var grainArgumentCheck = SF.IfStatement( SF.BinaryExpression( SyntaxKind.EqualsExpression, grainArgument, SF.LiteralExpression(SyntaxKind.NullLiteralExpression)), SF.ThrowStatement(argumentNullException)); // Wrap everything in a try-catch block. var faulted = (Expression <Func <Task <object> > >)(() => TaskUtility.Faulted(null)); const string Exception = "exception"; var exception = SF.Identifier(Exception); var body = SF.TryStatement() .AddBlockStatements(grainArgumentCheck, interfaceIdSwitch) .AddCatches( SF.CatchClause() .WithDeclaration( SF.CatchDeclaration(typeof(Exception).GetTypeSyntax()).WithIdentifier(exception)) .AddBlockStatements( SF.ReturnStatement( faulted.Invoke().AddArgumentListArguments(SF.Argument(SF.IdentifierName(Exception)))))); return(methodDeclaration.AddBodyStatements(body)); }
/// <summary> /// Compiles the provided syntax tree, and loads and returns the result. /// </summary> /// <param name="generatedSyntax">The syntax tree.</param> /// <param name="emitDebugSymbols"> /// Whether or not to emit debug symbols for the generated assembly. /// </param> /// <returns>The compilation output.</returns> private static CachedAssembly CompileAndLoad(GeneratedSyntax generatedSyntax, bool emitDebugSymbols) { var generated = CodeGeneratorCommon.CompileAssembly(generatedSyntax, "OrleansCodeGen", emitDebugSymbols: emitDebugSymbols); Assembly.Load(generated.RawBytes, generated.DebugSymbolRawBytes); return(new CachedAssembly(generated) { Loaded = true }); }
/// <summary> /// Compiles the provided syntax tree, and loads and returns the result. /// </summary> /// <param name="generatedSyntax">The syntax tree.</param> /// <param name="emitDebugSymbols"> /// Whether or not to emit debug symbols for the generated assembly. /// </param> /// <returns>The compilation output.</returns> private static CachedAssembly CompileAndLoad(GeneratedSyntax generatedSyntax, bool emitDebugSymbols) { var generated = CodeGeneratorCommon.CompileAssembly(generatedSyntax, "OrleansCodeGen", emitDebugSymbols: emitDebugSymbols); var loadedAssembly = LoadAssembly(generated); return(new CachedAssembly(generated) { Loaded = true, Assembly = loadedAssembly, }); }
/// <summary> /// Generates the class for the provided grain types. /// </summary> /// <param name="grainType"> /// The grain interface type. /// </param> /// <param name="onEncounteredType"> /// The callback which is invoked when a type is encountered. /// </param> /// <returns> /// The generated class. /// </returns> internal static TypeDeclarationSyntax GenerateClass(Type grainType, Action <Type> onEncounteredType) { var grainTypeInfo = grainType.GetTypeInfo(); var genericTypes = grainTypeInfo.IsGenericTypeDefinition ? grainTypeInfo.GetGenericArguments() .Select(_ => SF.TypeParameter(_.ToString())) .ToArray() : new TypeParameterSyntax[0]; // Create the special marker attribute. var markerAttribute = SF.Attribute(typeof(GrainReferenceAttribute).GetNameSyntax()) .AddArgumentListArguments( SF.AttributeArgument( SF.TypeOfExpression(grainType.GetTypeSyntax(includeGenericParameters: false)))); var attributes = SF.AttributeList() .AddAttributes( CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(), SF.Attribute(typeof(SerializableAttribute).GetNameSyntax()), #if !NETSTANDARD_TODO //ExcludeFromCodeCoverageAttribute became an internal class in netstandard SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()), #endif markerAttribute); var className = CodeGeneratorCommon.ClassPrefix + TypeUtils.GetSuitableClassName(grainType) + ClassSuffix; var classDeclaration = SF.ClassDeclaration(className) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword)) .AddBaseListTypes( SF.SimpleBaseType(typeof(GrainReference).GetTypeSyntax()), SF.SimpleBaseType(grainType.GetTypeSyntax())) .AddConstraintClauses(grainType.GetTypeConstraintSyntax()) .AddMembers(GenerateConstructors(className)) .AddMembers( GenerateInterfaceIdProperty(grainType), GenerateInterfaceNameProperty(grainType), GenerateIsCompatibleMethod(grainType), GenerateGetMethodNameMethod(grainType)) .AddMembers(GenerateInvokeMethods(grainType, onEncounteredType)) .AddAttributeLists(attributes); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } return(classDeclaration); }
/// <summary> /// Generates the class for the provided grain types. /// </summary> /// <param name="type">The grain interface type.</param> /// <param name="onEncounteredType"> /// The callback invoked when a type is encountered. /// </param> /// <returns> /// The generated class. /// </returns> internal static TypeDeclarationSyntax GenerateClass(Type type, Action <Type> onEncounteredType) { var typeInfo = type.GetTypeInfo(); var genericTypes = typeInfo.IsGenericTypeDefinition ? typeInfo.GetGenericArguments().Select(_ => SF.TypeParameter(_.ToString())).ToArray() : new TypeParameterSyntax[0]; var attributes = new List <AttributeSyntax> { CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(), #if !NETSTANDARD SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()), #endif SF.Attribute(typeof(SerializerAttribute).GetNameSyntax()) .AddArgumentListArguments( SF.AttributeArgument(SF.TypeOfExpression(type.GetTypeSyntax(includeGenericParameters: false)))) }; var className = CodeGeneratorCommon.ClassPrefix + type.GetParseableName(GeneratedTypeNameOptions); var fields = GetFields(type); // Mark each field type for generation foreach (var field in fields) { var fieldType = field.FieldInfo.FieldType; onEncounteredType(fieldType); } var members = new List <MemberDeclarationSyntax>(GenerateStaticFields(fields)) { GenerateDeepCopierMethod(type, fields), GenerateSerializerMethod(type, fields), GenerateDeserializerMethod(type, fields), }; var classDeclaration = SF.ClassDeclaration(className) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword)) .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray())) .AddMembers(members.ToArray()) .AddConstraintClauses(type.GetTypeConstraintSyntax()); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } return(classDeclaration); }
/// <summary> /// Generates source code for the provided assembly. /// </summary> /// <param name="input"> /// The assembly to generate source for. /// </param> /// <returns> /// The generated source. /// </returns> public string GenerateSourceForAssembly(Assembly input) { if (input.GetCustomAttribute <GeneratedCodeAttribute>() != null || input.GetCustomAttribute <SkipCodeGenerationAttribute>() != null) { return(string.Empty); } var generated = this.GenerateCode(input, new[] { input }.ToList()); if (generated.Syntax == null) { return(string.Empty); } return(CodeGeneratorCommon.GenerateSourceCode(CodeGeneratorCommon.AddGeneratedCodeAttribute(generated))); }
/// <summary> /// Generates source code for the provided assembly. /// </summary> /// <param name="input"> /// The assembly to generate source for. /// </param> /// <returns> /// The generated source. /// </returns> public string GenerateSourceForAssembly(Assembly input) { if (!ShouldGenerateCodeForAssembly(input)) { return(string.Empty); } var generated = GenerateForAssemblies(new List <Assembly> { input }, false); if (generated.Syntax == null) { return(string.Empty); } return(CodeGeneratorCommon.GenerateSourceCode(CodeGeneratorCommon.AddGeneratedCodeAttribute(generated))); }
/// <summary> /// Generates source code for the provided assembly. /// </summary> /// <param name="input"> /// The assembly to generate source for. /// </param> /// <returns> /// The generated source. /// </returns> public string GenerateSourceForAssembly(Assembly input) { RegisterGeneratedCodeTargets(input); if (input.GetCustomAttribute <GeneratedCodeAttribute>() != null || input.GetCustomAttribute <SkipCodeGenerationAttribute>() != null) { return(string.Empty); } var generated = GenerateForAssemblies(new List <Assembly> { input }, false); if (generated.Syntax == null) { return(string.Empty); } return(CodeGeneratorCommon.GenerateSourceCode(CodeGeneratorCommon.AddGeneratedCodeAttribute(generated))); }
/// <summary> /// Generates and compiles an assembly for the provided syntax. /// </summary> /// <param name="generatedSyntax"> /// The generated code. /// </param> /// <param name="assemblyName"> /// The name for the generated assembly. /// </param> /// <param name="emitDebugSymbols"> /// Whether or not to emit debug symbols for the generated assembly. /// </param> /// <returns> /// The raw assembly. /// </returns> /// <exception cref="CodeGenerationException"> /// An error occurred generating code. /// </exception> private Assembly CompileAssembly(GeneratedSyntax generatedSyntax, string assemblyName, bool emitDebugSymbols) { // Add the generated code attribute. var code = CodeGeneratorCommon.AddGeneratedCodeAttribute(generatedSyntax); // Reference everything which can be referenced. var assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(asm => !asm.IsDynamic && !string.IsNullOrWhiteSpace(asm.Location)) .Select(asm => MetadataReference.CreateFromFile(asm.Location)) .Cast <MetadataReference>() .ToArray(); // Generate the code. var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); #if NETSTANDARD2_0 // CoreFX bug https://github.com/dotnet/corefx/issues/5540 // to workaround it, we are calling internal WithTopLevelBinderFlags(BinderFlags.IgnoreCorLibraryDuplicatedTypes) // TODO: this API will be public in the future releases of Roslyn. // This work is tracked in https://github.com/dotnet/roslyn/issues/5855 // Once it's public, we should replace the internal reflection API call by the public one. var method = typeof(CSharpCompilationOptions).GetMethod("WithTopLevelBinderFlags", BindingFlags.NonPublic | BindingFlags.Instance); // we need to pass BinderFlags.IgnoreCorLibraryDuplicatedTypes, but it's an internal class // http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/Binder/BinderFlags.cs,00f268571bb66b73 options = (CSharpCompilationOptions)method.Invoke(options, new object[] { 1u << 26 }); #endif string source = null; if (this.logger.IsEnabled(LogLevel.Debug)) { source = CodeGeneratorCommon.GenerateSourceCode(code); // Compile the code and load the generated assembly. this.logger.Debug( ErrorCode.CodeGenSourceGenerated, "Generating assembly {0} with source:\n{1}", assemblyName, source); } var compilation = CSharpCompilation.Create(assemblyName) .AddSyntaxTrees(code.SyntaxTree) .AddReferences(assemblies) .WithOptions(options); using (var outputStream = new MemoryStream()) { var emitOptions = new EmitOptions() .WithEmitMetadataOnly(false) .WithIncludePrivateMembers(true); if (emitDebugSymbols) { emitOptions = emitOptions.WithDebugInformationFormat(DebugInformationFormat.Embedded); } var compilationResult = compilation.Emit(outputStream, options: emitOptions); if (!compilationResult.Success) { source = source ?? CodeGeneratorCommon.GenerateSourceCode(code); var errors = string.Join("\n", compilationResult.Diagnostics.Select(_ => _.ToString())); this.logger.Warn( ErrorCode.CodeGenCompilationFailed, "Compilation of assembly {0} failed with errors:\n{1}\nGenerated Source Code:\n{2}", assemblyName, errors, source); throw new CodeGenerationException(errors); } this.logger.Debug( ErrorCode.CodeGenCompilationSucceeded, "Compilation of assembly {0} succeeded.", assemblyName); return(Assembly.Load(outputStream.ToArray())); } }
/// <summary> /// Generates a syntax tree for the provided assemblies. /// </summary> /// <param name="targetAssembly">The assemblies used for accessiblity checks, or <see langword="null"/> during runtime code generation.</param> /// <param name="assemblies">The assemblies to generate code for.</param> /// <returns>The generated syntax tree.</returns> private GeneratedSyntax GenerateCode(Assembly targetAssembly, List <Assembly> assemblies) { var features = new FeatureDescriptions(); var members = new List <MemberDeclarationSyntax>(); // Expand the list of included assemblies and types. var knownAssemblies = new Dictionary <Assembly, KnownAssemblyAttribute>( assemblies.ToDictionary(k => k, k => default(KnownAssemblyAttribute))); foreach (var attribute in assemblies.SelectMany(asm => asm.GetCustomAttributes <KnownAssemblyAttribute>())) { knownAssemblies[attribute.Assembly] = attribute; } if (logger.IsEnabled(LogLevel.Information)) { logger.Info($"Generating code for assemblies: {string.Join(", ", knownAssemblies.Keys.Select(a => a.FullName))}"); } // Get types from assemblies which reference Orleans and are not generated assemblies. var grainClasses = new HashSet <Type>(); var grainInterfaces = new HashSet <Type>(); foreach (var pair in knownAssemblies) { var assembly = pair.Key; var treatTypesAsSerializable = pair.Value?.TreatTypesAsSerializable ?? false; foreach (var type in TypeUtils.GetDefinedTypes(assembly, this.logger)) { if (treatTypesAsSerializable || type.IsSerializable || TypeHasKnownBase(type)) { string logContext = null; if (logger.IsEnabled(LogLevel.Trace)) { if (treatTypesAsSerializable) { logContext = $"known assembly {assembly.GetName().Name} where 'TreatTypesAsSerializable' = true"; } else if (type.IsSerializable) { logContext = $"known assembly {assembly.GetName().Name} where type is [Serializable]"; } else if (type.IsSerializable) { logContext = $"known assembly {assembly.GetName().Name} where type has known base type."; } } serializableTypes.RecordType(type, targetAssembly, logContext); } // Include grain interfaces and classes. var isGrainInterface = GrainInterfaceUtils.IsGrainInterface(type); var isGrainClass = TypeUtils.IsConcreteGrainClass(type); if (isGrainInterface || isGrainClass) { // If code generation is being performed at runtime, the interface must be accessible to the generated code. if (!TypeUtilities.IsAccessibleFromAssembly(type, targetAssembly)) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Skipping inaccessible grain type, {0}", type.GetParseableName()); } continue; } // Attempt to generate serializers for grain state classes, i.e, T in Grain<T>. var baseType = type.BaseType; if (baseType != null && baseType.IsConstructedGenericType) { foreach (var arg in baseType.GetGenericArguments()) { string logContext = null; if (logger.IsEnabled(LogLevel.Trace)) { logContext = "generic base type of " + type.GetLogFormat(); } this.serializableTypes.RecordType(arg, targetAssembly, logContext); } } // Skip classes generated by this generator. if (IsOrleansGeneratedCode(type)) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Skipping generated grain type, {0}", type.GetParseableName()); } continue; } if (this.knownGrainTypes.Contains(type)) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Skipping grain type {0} since it already has generated code.", type.GetParseableName()); } continue; } if (isGrainClass) { if (this.logger.IsEnabled(LogLevel.Information)) { this.logger.Info("Found grain implementation class: {0}", type.GetParseableName()); } grainClasses.Add(type); } if (isGrainInterface) { if (this.logger.IsEnabled(LogLevel.Information)) { this.logger.Info("Found grain interface: {0}", type.GetParseableName()); } GrainInterfaceUtils.ValidateInterfaceRules(type); grainInterfaces.Add(type); } } } } // Group the types by namespace and generate the required code in each namespace. foreach (var groupedGrainInterfaces in grainInterfaces.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_))) { var namespaceName = groupedGrainInterfaces.Key; var namespaceMembers = new List <MemberDeclarationSyntax>(); foreach (var grainInterface in groupedGrainInterfaces) { var referenceTypeName = GrainReferenceGenerator.GetGeneratedClassName(grainInterface); var invokerTypeName = GrainMethodInvokerGenerator.GetGeneratedClassName(grainInterface); namespaceMembers.Add( GrainReferenceGenerator.GenerateClass( grainInterface, referenceTypeName, encounteredType => { string logContext = null; if (logger.IsEnabled(LogLevel.Trace)) { logContext = "used by grain type " + grainInterface.GetLogFormat(); } this.serializableTypes.RecordType(encounteredType, targetAssembly, logContext); })); namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(grainInterface, invokerTypeName)); var genericTypeSuffix = GetGenericTypeSuffix(grainInterface.GetGenericArguments().Length); features.GrainInterfaces.Add( new GrainInterfaceDescription { Interface = grainInterface.GetTypeSyntax(includeGenericParameters: false), Reference = SF.ParseTypeName(namespaceName + '.' + referenceTypeName + genericTypeSuffix), Invoker = SF.ParseTypeName(namespaceName + '.' + invokerTypeName + genericTypeSuffix), InterfaceId = GrainInterfaceUtils.GetGrainInterfaceId(grainInterface) }); } members.Add(CreateNamespace(namespaceName, namespaceMembers)); } foreach (var type in grainClasses) { features.GrainClasses.Add( new GrainClassDescription { ClassType = type.GetTypeSyntax(includeGenericParameters: false) }); } // Generate serializers into their own namespace. var serializerNamespace = this.GenerateSerializers(targetAssembly, features); members.Add(serializerNamespace); // Add serialization metadata for the types which were encountered. this.AddSerializationTypes(features.Serializers, targetAssembly, knownAssemblies.Keys.ToList()); foreach (var attribute in knownAssemblies.Keys.SelectMany(asm => asm.GetCustomAttributes <ConsiderForCodeGenerationAttribute>())) { this.serializableTypes.RecordType(attribute.Type, targetAssembly, "[ConsiderForCodeGeneration]"); if (attribute.ThrowOnFailure && !this.serializableTypes.IsTypeRecorded(attribute.Type) && !this.serializableTypes.IsTypeIgnored(attribute.Type)) { throw new CodeGenerationException( $"Found {attribute.GetType().Name} for type {attribute.Type.GetParseableName()}, but code" + " could not be generated. Ensure that the type is accessible."); } } // Generate metadata directives for all of the relevant types. var(attributeDeclarations, memberDeclarations) = FeaturePopulatorGenerator.GenerateSyntax(targetAssembly, features); members.AddRange(memberDeclarations); var compilationUnit = SF.CompilationUnit().AddAttributeLists(attributeDeclarations.ToArray()).AddMembers(members.ToArray()); return(new GeneratedSyntax { SourceAssemblies = knownAssemblies.Keys.ToList(), Syntax = compilationUnit }); }
/// <summary> /// Generates a syntax tree for the provided assemblies. /// </summary> /// <param name="assemblies">The assemblies to generate code for.</param> /// <param name="runtime">Whether or not runtime code generation is being performed.</param> /// <returns>The generated syntax tree.</returns> private static GeneratedSyntax GenerateForAssemblies(List <Assembly> assemblies, bool runtime) { if (Logger.IsVerbose) { Logger.Verbose( "Generating code for assemblies: {0}", string.Join(", ", assemblies.Select(_ => _.FullName))); } Assembly targetAssembly; HashSet <Type> ignoredTypes; if (runtime) { // Ignore types which have already been accounted for. ignoredTypes = GetTypesWithGeneratedSupportClasses(); targetAssembly = null; } else { ignoredTypes = new HashSet <Type>(); targetAssembly = assemblies.FirstOrDefault(); } var members = new List <MemberDeclarationSyntax>(); // If any KnownAssemblies have been specified, include them during code generation. var knownAssemblies = assemblies.SelectMany(_ => _.GetCustomAttributes <KnownAssemblyAttribute>()) .Select(_ => _.Assembly) .Distinct() .ToSet(); if (knownAssemblies.Count > 0) { knownAssemblies.UnionWith(assemblies); assemblies = knownAssemblies.ToList(); } // Get types from assemblies which reference Orleans and are not generated assemblies. var includedTypes = new HashSet <Type>(); for (var i = 0; i < assemblies.Count; i++) { var assembly = assemblies[i]; foreach (var attribute in assembly.GetCustomAttributes <KnownTypeAttribute>()) { ConsiderType(attribute.Type, runtime, targetAssembly, includedTypes); } foreach (var type in assembly.DefinedTypes) { ConsiderType(type, runtime, targetAssembly, includedTypes); } } includedTypes.RemoveWhere(_ => ignoredTypes.Contains(_)); // Group the types by namespace and generate the required code in each namespace. foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_))) { var namespaceMembers = new List <MemberDeclarationSyntax>(); foreach (var type in group) { // The module containing the serializer. var module = runtime ? null : type.Module; // Every type which is encountered must be considered for serialization. Action <Type> onEncounteredType = encounteredType => { // If a type was encountered which can be accessed, process it for serialization. SerializerGenerationManager.RecordTypeToGenerate(encounteredType, module, targetAssembly); }; if (Logger.IsVerbose2) { Logger.Verbose2("Generating code for: {0}", type.GetParseableName()); } if (GrainInterfaceData.IsGrainInterface(type)) { if (Logger.IsVerbose2) { Logger.Verbose2( "Generating GrainReference and MethodInvoker for {0}", type.GetParseableName()); } GrainInterfaceData.ValidateInterfaceRules(type); namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, onEncounteredType)); namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type)); } // Generate serializers. var first = true; Type toGen; while (SerializerGenerationManager.GetNextTypeToProcess(out toGen)) { if (!runtime) { if (first) { ConsoleText.WriteStatus("ClientGenerator - Generating serializer classes for types:"); first = false; } ConsoleText.WriteStatus( "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace + " defined in Assembly " + toGen.Assembly.GetName()); } if (Logger.IsVerbose2) { Logger.Verbose2( "Generating & Registering Serializer for Type {0}", toGen.GetParseableName()); } namespaceMembers.AddRange(SerializerGenerator.GenerateClass(toGen, onEncounteredType)); } } if (namespaceMembers.Count == 0) { if (Logger.IsVerbose) { Logger.Verbose2("Skipping namespace: {0}", group.Key); } continue; } members.Add( SF.NamespaceDeclaration(SF.ParseName(group.Key)) .AddUsings( TypeUtils.GetNamespaces(typeof(TaskUtility), typeof(GrainExtensions)) .Select(_ => SF.UsingDirective(SF.ParseName(_))) .ToArray()) .AddMembers(namespaceMembers.ToArray())); } return(new GeneratedSyntax { SourceAssemblies = assemblies, Syntax = members.Count > 0 ? SF.CompilationUnit().AddMembers(members.ToArray()) : null }); }
public static (List <AttributeListSyntax>, List <MemberDeclarationSyntax>) GenerateSyntax( Assembly targetAssembly, List <GrainInterfaceDescription> grainInterfaces, List <GrainClassDescription> grainClasses, SerializationTypeDescriptions typeDescriptions) { var attributes = new List <AttributeListSyntax>(); var members = new List <MemberDeclarationSyntax>(); var className = CodeGeneratorCommon.ClassPrefix + Guid.NewGuid().ToString("N").Substring(0, 10) + ClassSuffix; // Generate a class for populating the metadata. var classSyntax = SF.ClassDeclaration(className) .AddBaseListTypes( SF.SimpleBaseType(typeof(IFeaturePopulator <GrainInterfaceFeature>).GetTypeSyntax()), SF.SimpleBaseType(typeof(IFeaturePopulator <GrainClassFeature>).GetTypeSyntax()), SF.SimpleBaseType(typeof(IFeaturePopulator <SerializerFeature>).GetTypeSyntax())) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword), SF.Token(SyntaxKind.SealedKeyword)) .AddMembers(GeneratePopulateMethod(grainInterfaces), GeneratePopulateMethod(grainClasses), GeneratePopulateMethod(typeDescriptions)) .AddAttributeLists(SF.AttributeList(SF.SingletonSeparatedList(CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax()))); var namespaceSyntax = SF.NamespaceDeclaration(NamespaceName.ToIdentifierName()).AddMembers(classSyntax); members.Add(namespaceSyntax); // Generate an assembly-level attribute with an instance of that class. var attribute = SF.AttributeList( SF.AttributeTargetSpecifier(SF.Token(SyntaxKind.AssemblyKeyword)), SF.SingletonSeparatedList( SF.Attribute(typeof(FeaturePopulatorAttribute).GetNameSyntax()) .AddArgumentListArguments(SF.AttributeArgument(SF.TypeOfExpression(SF.ParseTypeName(NamespaceName + "." + className)))))); attributes.Add(attribute); return(attributes, members); }
/// <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 syntax for an invoke method. /// </summary> /// <param name="grainType"> /// The grain type. /// </param> /// <param name="invokeMethod"> /// The invoke method to generate. /// </param> /// <returns> /// Syntax for an invoke method. /// </returns> private static MethodDeclarationSyntax GenerateInvokeMethod(Type grainType, MethodInfo invokeMethod) { var parameters = invokeMethod.GetParameters(); var grainArgument = parameters[0].Name.ToIdentifierName(); var requestArgument = parameters[1].Name.ToIdentifierName(); // Store the relevant values from the request in local variables. var interfaceIdDeclaration = SF.LocalDeclarationStatement( SF.VariableDeclaration(typeof(int).GetTypeSyntax()) .AddVariables( SF.VariableDeclarator("interfaceId") .WithInitializer(SF.EqualsValueClause(requestArgument.Member((InvokeMethodRequest _) => _.InterfaceId))))); var interfaceIdVariable = SF.IdentifierName("interfaceId"); var methodIdDeclaration = SF.LocalDeclarationStatement( SF.VariableDeclaration(typeof(int).GetTypeSyntax()) .AddVariables( SF.VariableDeclarator("methodId") .WithInitializer(SF.EqualsValueClause(requestArgument.Member((InvokeMethodRequest _) => _.MethodId))))); var methodIdVariable = SF.IdentifierName("methodId"); var argumentsDeclaration = SF.LocalDeclarationStatement( SF.VariableDeclaration(typeof(object[]).GetTypeSyntax()) .AddVariables( SF.VariableDeclarator("arguments") .WithInitializer(SF.EqualsValueClause(requestArgument.Member((InvokeMethodRequest _) => _.Arguments))))); var argumentsVariable = SF.IdentifierName("arguments"); var methodDeclaration = invokeMethod.GetDeclarationSyntax() .AddBodyStatements(interfaceIdDeclaration, methodIdDeclaration, argumentsDeclaration); var interfaceCases = CodeGeneratorCommon.GenerateGrainInterfaceAndMethodSwitch( grainType, methodIdVariable, methodType => GenerateInvokeForMethod(grainType, grainArgument, methodType, argumentsVariable)); // Generate the default case, which will throw a NotImplementedException. var errorMessage = SF.BinaryExpression( SyntaxKind.AddExpression, "interfaceId=".GetLiteralExpression(), interfaceIdVariable); var throwStatement = SF.ThrowStatement( SF.ObjectCreationExpression(typeof(NotImplementedException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(errorMessage))); var defaultCase = SF.SwitchSection().AddLabels(SF.DefaultSwitchLabel()).AddStatements(throwStatement); var interfaceIdSwitch = SF.SwitchStatement(interfaceIdVariable).AddSections(interfaceCases.ToArray()).AddSections(defaultCase); // If the provided grain is null, throw an argument exception. var argumentNullException = SF.ObjectCreationExpression(typeof(ArgumentNullException).GetTypeSyntax()) .AddArgumentListArguments(SF.Argument(parameters[0].Name.GetLiteralExpression())); var grainArgumentCheck = SF.IfStatement( SF.BinaryExpression( SyntaxKind.EqualsExpression, grainArgument, SF.LiteralExpression(SyntaxKind.NullLiteralExpression)), SF.ThrowStatement(argumentNullException)); return(methodDeclaration.AddBodyStatements(grainArgumentCheck, interfaceIdSwitch)); }
/// <summary> /// Generates the class for the provided grain types. /// </summary> /// <param name="type">The grain interface type.</param> /// <param name="onEncounteredType"> /// The callback invoked when a type is encountered. /// </param> /// <returns> /// The generated class. /// </returns> internal static IEnumerable <TypeDeclarationSyntax> GenerateClass(Type type, Action <Type> onEncounteredType) { var typeInfo = type.GetTypeInfo(); var genericTypes = typeInfo.IsGenericTypeDefinition ? typeInfo.GetGenericArguments().Select(_ => SF.TypeParameter(_.ToString())).ToArray() : new TypeParameterSyntax[0]; var attributes = new List <AttributeSyntax> { CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(), SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()), SF.Attribute(typeof(SerializerAttribute).GetNameSyntax()) .AddArgumentListArguments( SF.AttributeArgument(SF.TypeOfExpression(type.GetTypeSyntax(includeGenericParameters: false)))) }; var className = CodeGeneratorCommon.ClassPrefix + type.GetParseableName(GeneratedTypeNameOptions); var fields = GetFields(type); // Mark each field type for generation foreach (var field in fields) { var fieldType = field.FieldInfo.FieldType; onEncounteredType(fieldType); } var members = new List <MemberDeclarationSyntax>(GenerateStaticFields(fields)) { GenerateDeepCopierMethod(type, fields), GenerateSerializerMethod(type, fields), GenerateDeserializerMethod(type, fields), }; if (typeInfo.IsConstructedGenericType || !typeInfo.IsGenericTypeDefinition) { members.Add(GenerateRegisterMethod(type)); members.Add(GenerateConstructor(className)); attributes.Add(SF.Attribute(typeof(RegisterSerializerAttribute).GetNameSyntax())); } var classDeclaration = SF.ClassDeclaration(className) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword)) .AddAttributeLists(SF.AttributeList().AddAttributes(attributes.ToArray())) .AddMembers(members.ToArray()) .AddConstraintClauses(type.GetTypeConstraintSyntax()); if (genericTypes.Length > 0) { classDeclaration = classDeclaration.AddTypeParameterListParameters(genericTypes); } var classes = new List <TypeDeclarationSyntax> { classDeclaration }; if (typeInfo.IsGenericTypeDefinition) { // Create a generic representation of the serializer type. var serializerType = SF.GenericName(classDeclaration.Identifier) .WithTypeArgumentList( SF.TypeArgumentList() .AddArguments( type.GetGenericArguments() .Select(_ => SF.OmittedTypeArgument()) .Cast <TypeSyntax>() .ToArray())); var registererClassName = className + "_" + string.Join("_", type.GetTypeInfo().GenericTypeParameters.Select(_ => _.Name)) + "_" + RegistererClassSuffix; classes.Add( SF.ClassDeclaration(registererClassName) .AddModifiers(SF.Token(SyntaxKind.InternalKeyword)) .AddAttributeLists( SF.AttributeList() .AddAttributes( CodeGeneratorCommon.GetGeneratedCodeAttributeSyntax(), SF.Attribute(typeof(ExcludeFromCodeCoverageAttribute).GetNameSyntax()), SF.Attribute(typeof(RegisterSerializerAttribute).GetNameSyntax()))) .AddMembers( GenerateMasterRegisterMethod(type, serializerType), GenerateConstructor(registererClassName))); } return(classes); }
/// <summary> /// Generates a syntax tree for the provided assemblies. /// </summary> /// <param name="assemblies">The assemblies to generate code for.</param> /// <param name="runtime">Whether or not runtime code generation is being performed.</param> /// <returns>The generated syntax tree.</returns> private GeneratedSyntax GenerateForAssemblies(Assembly targetAssembly, bool runtime) { var grainInterfaces = new List <GrainInterfaceDescription>(); var grainClasses = new List <GrainClassDescription>(); var serializationTypes = new SerializationTypeDescriptions(); var members = new List <MemberDeclarationSyntax>(); // Expand the list of included assemblies and types. var(includedTypes, assemblies) = this.GetIncludedTypes(targetAssembly, runtime); if (Logger.IsVerbose) { Logger.Verbose( "Generating code for assemblies: {0}", string.Join(", ", assemblies.Select(_ => _.FullName))); } var serializerNamespaceMembers = new List <MemberDeclarationSyntax>(); var serializerNamespaceName = $"{SerializerNamespacePrefix}{targetAssembly?.GetName().Name.GetHashCode():X}"; // Group the types by namespace and generate the required code in each namespace. foreach (var group in includedTypes.GroupBy(_ => CodeGeneratorCommon.GetGeneratedNamespace(_))) { var namespaceMembers = new List <MemberDeclarationSyntax>(); var namespaceName = group.Key; foreach (var type in group) { // Skip generated classes. if (type.GetCustomAttribute <GeneratedCodeAttribute>() != null) { continue; } // Every type which is encountered must be considered for serialization. void OnEncounteredType(Type encounteredType) { // If a type was encountered which can be accessed, process it for serialization. this.typeCollector.RecordEncounteredType(type); this.serializerGenerationManager.RecordTypeToGenerate(encounteredType, targetAssembly); } if (Logger.IsVerbose2) { Logger.Verbose2("Generating code for: {0}", type.GetParseableName()); } if (GrainInterfaceUtils.IsGrainInterface(type)) { if (Logger.IsVerbose2) { Logger.Verbose2( "Generating GrainReference and MethodInvoker for {0}", type.GetParseableName()); } GrainInterfaceUtils.ValidateInterfaceRules(type); var referenceTypeName = GrainReferenceGenerator.GetGeneratedClassName(type); var invokerTypeName = GrainMethodInvokerGenerator.GetGeneratedClassName(type); namespaceMembers.Add(GrainReferenceGenerator.GenerateClass(type, referenceTypeName, OnEncounteredType)); namespaceMembers.Add(GrainMethodInvokerGenerator.GenerateClass(type, invokerTypeName)); var genericTypeSuffix = GetGenericTypeSuffix(type.GetGenericArguments().Length); grainInterfaces.Add( new GrainInterfaceDescription { Interface = type.GetTypeSyntax(includeGenericParameters: false), Reference = SF.ParseTypeName(namespaceName + '.' + referenceTypeName + genericTypeSuffix), Invoker = SF.ParseTypeName(namespaceName + '.' + invokerTypeName + genericTypeSuffix), InterfaceId = GrainInterfaceUtils.GetGrainInterfaceId(type) }); } if (TypeUtils.IsConcreteGrainClass(type)) { grainClasses.Add( new GrainClassDescription { ClassType = type.GetTypeSyntax(includeGenericParameters: false) }); } // Generate serializers. var first = true; while (this.serializerGenerationManager.GetNextTypeToProcess(out var toGen)) { if (first) { Logger.Info("ClientGenerator - Generating serializer classes for types:"); first = false; } Logger.Info( "\ttype " + toGen.FullName + " in namespace " + toGen.Namespace + " defined in Assembly " + toGen.GetTypeInfo().Assembly.GetName()); if (Logger.IsVerbose2) { Logger.Verbose2( "Generating serializer for type {0}", toGen.GetParseableName()); } var generatedSerializerName = SerializerGenerator.GetGeneratedClassName(toGen); serializerNamespaceMembers.Add(SerializerGenerator.GenerateClass(generatedSerializerName, toGen, OnEncounteredType)); var qualifiedSerializerName = serializerNamespaceName + '.' + generatedSerializerName + GetGenericTypeSuffix(toGen.GetGenericArguments().Length); serializationTypes.SerializerTypes.Add( new SerializerTypeDescription { Serializer = SF.ParseTypeName(qualifiedSerializerName), Target = toGen.GetTypeSyntax(includeGenericParameters: false) }); } } if (namespaceMembers.Count == 0) { if (Logger.IsVerbose) { Logger.Verbose2("Skipping namespace: {0}", namespaceName); } continue; } members.Add(CreateNamespace(namespaceName, namespaceMembers)); } // Add all generated serializers to their own namespace. members.Add(CreateNamespace(serializerNamespaceName, serializerNamespaceMembers)); // Add serialization metadata for the types which were encountered. this.AddSerializationTypes(serializationTypes, targetAssembly); // Generate metadata directives for all of the relevant types. var(attributeDeclarations, memberDeclarations) = FeaturePopulatorGenerator.GenerateSyntax(targetAssembly, grainInterfaces, grainClasses, serializationTypes); members.AddRange(memberDeclarations); var compilationUnit = SF.CompilationUnit().AddAttributeLists(attributeDeclarations.ToArray()).AddMembers(members.ToArray()); return(new GeneratedSyntax { SourceAssemblies = assemblies, Syntax = compilationUnit }); string GetGenericTypeSuffix(int numParams) { if (numParams == 0) { return(string.Empty); } return('<' + new string(',', numParams - 1) + '>'); } NamespaceDeclarationSyntax CreateNamespace(string namespaceName, IEnumerable <MemberDeclarationSyntax> namespaceMembers) { return (SF.NamespaceDeclaration(SF.ParseName(namespaceName)) .AddUsings( TypeUtils.GetNamespaces(typeof(GrainExtensions), typeof(IntrospectionExtensions)) .Select(_ => SF.UsingDirective(SF.ParseName(_))) .ToArray()) .AddMembers(namespaceMembers.ToArray())); } }