예제 #1
0
        public void Execute(SourceGeneratorContext context)
        {
            try
            {
                var registrationSymbols = RegistrationSymbols.FromCompilation(context.Compilation);
                var containerClasses    = context.LocateContainerSymbols(registrationSymbols.ContainerSymbol);
                var generator           = new ContainerClassContentGenerator(context, registrationSymbols);

                foreach (var containerClass in containerClasses)
                {
                    var hintName = $"Generated.{containerClass.FullyQualifiedName}";
                    var content  = generator.GenerateClassString(containerClass);

                    WriteOutDebugFile(hintName, content, context);
                    context.AddSource(hintName, SourceText.From(content, Encoding.UTF8));
                }
            }
            catch (DiagnosticException ex)
            {
                context.ReportDiagnostic(ex.Diagnostic);
            }
            catch (Exception ex)
            {
                var descriptor = new DiagnosticDescriptor(DiagnosticConstants.UnknownExceptionId, "Unexpected error", $"Unknown error during generation: {ex.GetType()} {ex.Message}", DiagnosticConstants.Category, DiagnosticSeverity.Error, true);
                context.ReportDiagnostic(Diagnostic.Create(descriptor, Location.None));
            }
        }
예제 #2
0
 private void Report_MultiplesFormatStringAttributesApplied(INamedTypeSymbol type)
 {
     // TODO Reportar mejor la localizacion
     _context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor(
                                                     nameof(FormatToStringAttribute),
                                                     nameof(FormatToStringAttribute),
                                                     $"Multiples attributes form No properties found in '{type.ContainingNamespace}.{type.Name}' type to fill ToString() method.",
                                                     $"{nameof(AutoToStringAttribute)}",
                                                     DiagnosticSeverity.Warning,
                                                     true),
                                                 type.Locations.FirstOrDefault() ?? Location.None
                                                 ));
 }
예제 #3
0
        public void Execute(SourceGeneratorContext context)
        {
            foreach (AdditionalText bfFile in context.AdditionalFiles.Where(x => x.Path.EndsWith(".bf")))
            {
                try
                {
                    (IEnumerable <BFOp> operations, BFTranspilerOptions options) = BFParser.Parse(bfFile);

                    if (!operations.Any())
                    {
                        continue;
                    }

                    BFTranspiler btf = new BFTranspiler(options);

                    string csFile = btf.Transpile(operations);

                    context.AddSource(options.ClassName + ".cs", SourceText.From(csFile, Encoding.UTF8));
                }
                catch (Exception e)
                {
                    context.ReportDiagnostic(
                        Diagnostic.Create(
                            new DiagnosticDescriptor("P0T4T0", "BFError", "Error when transpiling BF: {0}", "BF.Transpile", DiagnosticSeverity.Error, true),
                            Location.Create(Path.GetFileName(bfFile.Path), new TextSpan(), new LinePositionSpan()),
                            e.Message));
                }
            }
        }
예제 #4
0
        public void Execute(SourceGeneratorContext context)
        {
            // retreive the populated receiver
            if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
            {
                return;
            }
            try
            {
                // 简单测试aop 生成
                Action <StringBuilder, IMethodSymbol> beforeCall = (sb, method) => { };
                Action <StringBuilder, IMethodSymbol> afterCall  = (sb, method) => { sb.Append("r++;"); };

                // 获取生成结果
                var code = receiver.SyntaxNodes
                           .Select(i => context.Compilation.GetSemanticModel(i.SyntaxTree).GetDeclaredSymbol(i) as INamedTypeSymbol)
                           .Where(i => i != null && !i.IsStatic)
                           .Select(i => ProxyCodeGenerator.GenerateProxyCode(i, beforeCall, afterCall))
                           .First();

                context.AddSource("code.cs", SourceText.From(code, Encoding.UTF8));
            }
            catch (Exception ex)
            {
                // 失败汇报
                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("n001", ex.ToString(), ex.ToString(), "AOP.Generate", DiagnosticSeverity.Warning, true), Location.Create("code.cs", TextSpan.FromBounds(0, 0), new LinePositionSpan())));
            }
        }
예제 #5
0
        public void Execute(SourceGeneratorContext context)
        {
            //_ = Debugger.Launch(); // uncomment to debug this source generator

            // create a folder to serialize the generated source for debugging
            var generatedPath = default(string?);

            if (serializeSource)
            {
                // place it inside obj so that source is not added to the project
                generatedPath = Path.Combine("Generated", nameof(OverloadsGenerator));
                // delete source generated by previous build
                if (Directory.Exists(generatedPath))
                {
                    Directory.Delete(generatedPath, true);
                }
                _ = Directory.CreateDirectory(generatedPath);
            }

            try
            {
                var collectedExtensionMethods = CollectExtensionMethods(context);
                GenerateSource(context, collectedExtensionMethods, generatedPath);
            }
            catch (Exception ex)
            {
                context.ReportDiagnostic(Diagnostic.Create(UnhandledExceptionError, Location.None, ex.Message));
            }
        }
예제 #6
0
        // return true if we should continue
        private bool ReportDiagnostics(IEnumerable <Diagnostic> diagnostics)
        {
            bool hasError = false;

            foreach (var result in diagnostics)
            {
                hasError |= result.Severity == DiagnosticSeverity.Error;
                _generatorContext.ReportDiagnostic(result);
            }
            return(hasError);
        }
예제 #7
0
        // TODO : Remove when possible to peek into generated code
        private void WriteOutDebugFile(string hintName, string content, SourceGeneratorContext context)
        {
#if DEBUG
            try
            {
                var tempFile = $"{System.IO.Path.GetTempPath()}{hintName}.cs";
                System.IO.File.WriteAllText(tempFile, content);
                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("GDBG", "Write out debug file", tempFile, DiagnosticConstants.Category, DiagnosticSeverity.Warning, true), Location.None));
            }
            catch { throw; }
#endif
        }
        public void Execute(SourceGeneratorContext context)
        {
            // retreive the populated receiver
            if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
            {
                return;
            }

            var compilation = context.Compilation;

            INamedTypeSymbol attributeSymbol = compilation.GetTypeByMetadataName("AutomaticInterfaceAttribute.GenerateAutomaticInterfaceAttribute");

            List <INamedTypeSymbol> classSymbols = new List <INamedTypeSymbol>();

            foreach (ClassDeclarationSyntax cls in receiver.CandidateClasses)
            {
                var model = compilation.GetSemanticModel(cls.SyntaxTree);

                var classSymbol = model.GetDeclaredSymbol(cls);

                if (classSymbol.GetAttributes().Any(ad => ad.AttributeClass.Name == attributeSymbol.Name)) // todo, weird that  ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default) always returns null - see https://github.com/dotnet/roslyn/issues/30248 maybe?
                {
                    classSymbols.Add(classSymbol);
                }
            }

            foreach (var classSymbol in classSymbols)
            {
                var sourceBuilder = new StringBuilder();
                var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
                var interfaceName = $"I{classSymbol.Name}";
                sourceBuilder.Append($@"
using System;
namespace {namespaceName}
{{
    public interface {interfaceName}
    {{
         
");
                addMembersToInterface(classSymbol, sourceBuilder);

                sourceBuilder.Append(@"
    }      
}");
                File.WriteAllText(@"C:\dev\net_automatic_interface\AutomaticInterface\bla.cs", sourceBuilder.ToString());
                var descriptor = new DiagnosticDescriptor(nameof(AutomaticInterface), "Result", $"Finished compilation for {interfaceName}", "Compilation", DiagnosticSeverity.Warning, isEnabledByDefault: true);
                context.ReportDiagnostic(Diagnostic.Create(descriptor, null));

                // inject the created source into the users compilation
                context.AddSource(nameof(AutomaticInterfaceGenerator), SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
            }
        }
예제 #9
0
        private static void ReportDiagnostic(SourceGeneratorContext context, string id, string title, string messageFormat, DiagnosticSeverity defaultSeverity, params object[] messageArgs)
        {
            var diagnosticDescriptor = new DiagnosticDescriptor(
                id: id,
                title: title,
                messageFormat: messageFormat,
                category: "mocks",
                defaultSeverity: defaultSeverity,
                isEnabledByDefault: true);
            var diagnostic = Diagnostic.Create(diagnosticDescriptor, Location.None, messageArgs);

            context.ReportDiagnostic(diagnostic);
        }
        public void Execute(SourceGeneratorContext context)
        {
            try {
                if (!(context.SyntaxReceiver is CandidateSyntaxReceiver receiver))
                {
                    return;
                }

                _genFactory.RunGeneration(context.Compilation, receiver.DeclaredTypes, generator => {
                    generator.OnReportedDiagnostic += (_, diagnostic) => context.ReportDiagnostic(diagnostic);
                    generator.OnGeneratedSource    += (_, source) => context.AddSourceWithDebug(source);
                    generator.GenerateFiles();
                });
            }
            catch (Exception e) {
                context.WriteException(e);
            }
        }
        public void Execute(SourceGeneratorContext context)
        {
            try
            {
                CodeGenerator generator = new CodeGenerator(GltfSchema);
                generator.ParseSchemas();
                generator.ExpandSchemaReferences();
                generator.EvaluateInheritance();
                generator.PostProcessSchema();
                var generatedFiles = generator.CSharpCodeGen();

                foreach (var file in generatedFiles)
                {
                    context.AddSource(file.Key, SourceText.From(file.Value, Encoding.UTF8));
                }
            }
            catch (Exception e)
            {
                context.ReportDiagnostic(Diagnostic.Create("GLTF1", "Compiler", e.Message, DiagnosticSeverity.Warning, DiagnosticSeverity.Warning, true, 1));
            }
        }
 public void Execute(SourceGeneratorContext context)
 {
     try
     {
         ExecuteInternal(context);
     }
     catch (Exception e)
     {
         //This is temporary till https://github.com/dotnet/roslyn/issues/46084 is fixed
         context.ReportDiagnostic(Diagnostic.Create(
                                      new DiagnosticDescriptor(
                                          "SI0000",
                                          "An exception was thrown by the SvgGenerator generator",
                                          "An exception was thrown by the SvgGenerator generator: '{0}'",
                                          "SvgGenerator",
                                          DiagnosticSeverity.Error,
                                          isEnabledByDefault: true),
                                      Location.None,
                                      e.ToString()));
     }
 }
예제 #13
0
        public void Execute(SourceGeneratorContext context)
        {
            if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
            {
                return;
            }
            var file = RandomUtils.NewCSFileName();

            try
            {
                var source    = CreateGenerateSymbolSource(receiver.SyntaxNodes, context);
                var notations = CreateNotationGenerators()
                                .Select(i => i.GenerateNotations(source))
                                .Combine();
                context.AddSource(file, CreateSourceText(notations));
            }
            catch (Exception ex)
            {
                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("n001", ex.ToString(), ex.ToString(), "Norns.AOT.Generate", DiagnosticSeverity.Warning, true), Location.Create(file, TextSpan.FromBounds(0, 0), new LinePositionSpan())));
            }
        }
예제 #14
0
        private void WriteSerializerClasses(SourceGeneratorContext context, List <INamedTypeSymbol> bitStructClasses, Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo> classesSizeInfo,
                                            INamedTypeSymbol bitStructAttributeSymbol, INamedTypeSymbol bitArrayAttributeSymbol)
        {
            foreach (INamedTypeSymbol classSymbol in bitStructClasses)
            {
                ClassSerializeSizeInfo classSizeInfo = classesSizeInfo[classSymbol];

                string classFullName       = SourceGenUtils.GetTypeFullName(classSymbol);
                string serializerClassName = CreateSerializerName(classSymbol);

                BitStructAttribute bitStructAttribute = SourceGenUtils.GetAttribute <BitStructAttribute>(classSymbol, bitStructAttributeSymbol);

                var sourceBuilder = new StringBuilder();
                sourceBuilder.Append($@"
namespace {classSymbol.ContainingNamespace}
{{
");
                ITypeSymbol[] classContainingTypes = SourceGenUtils.GetContainingTypesList(classSymbol);
                foreach (ITypeSymbol containingType in classContainingTypes)
                {
                    switch (containingType.TypeKind)
                    {
                    case TypeKind.Class:
                        sourceBuilder.Append($@"
    partial class {containingType.Name} {{
");
                        break;

                    case TypeKind.Struct:
                        sourceBuilder.Append($@"
    partial struct {containingType.Name} {{
");
                        break;

                    default:
                        context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Only expecting struct or class containing types. Have {containingType.TypeKind}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                        return;
                    }
                }

                sourceBuilder.Append($@"
    {SourceGenUtils.GetAccessibilityString(classSymbol.DeclaredAccessibility)} static class {serializerClassName}
    {{
");

                var sizeFuncBuilder        = new StringBuilder();
                var serializeFuncBuilder   = new StringBuilder();
                var deserializeFuncBuilder = new StringBuilder();

                if (classSizeInfo.Type == ClassSerializeSizeType.Const)
                {
                    sizeFuncBuilder.Append($@"
        public const int Size = {classSizeInfo.ConstSize};
");
                }

                sizeFuncBuilder.Append($@"
        public static int CalculateSize({classFullName} value)
        {{
            int result = {classSizeInfo.ConstSize};
");

                serializeFuncBuilder.Append($@"
        public static global::System.Span<byte> Serialize(global::System.Span<byte> output, {classFullName} value)
        {{
");

                deserializeFuncBuilder.Append($@"
        public static global::System.ReadOnlySpan<byte> Deserialize(global::System.ReadOnlySpan<byte> input, out {classFullName} value)
        {{
            value = new {classFullName}();
");

                foreach (IFieldSymbol classFieldMember in GetClassFieldMembers(classSymbol))
                {
                    if (classFieldMember.Type.IsIntegerType() ||
                        classFieldMember.Type.TypeKind == TypeKind.Enum)
                    {
                        INamedTypeSymbol      fieldType     = (INamedTypeSymbol)classFieldMember.Type;
                        IntegerOrEnumTypeInfo fieldTypeInfo = GetIntegerOrEnumTypeInfo(fieldType, bitStructAttribute.Endianess);

                        serializeFuncBuilder.Append($@"
            if (!{fieldTypeInfo.SerializeFuncName}(output, {fieldTypeInfo.SerializeTypeCast}value.{classFieldMember.Name}))
            {{
                throw new global::System.Exception(string.Format(""Not enough space to serialize field {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
            }}
            output = output.Slice({fieldTypeInfo.TypeSize});
");

                        deserializeFuncBuilder.Append($@"
            {{
                if (!{fieldTypeInfo.DeserializeFuncName}(input, out var fieldValue))
                {{
                    throw new global::System.Exception(string.Format(""Not enough data to deserialize field {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                }}
                value.{classFieldMember.Name} = {fieldTypeInfo.DeserializeTypeCast}fieldValue;
                input = input.Slice({fieldTypeInfo.TypeSize});
            }}
");
                    }
                    else if (classFieldMember.Type.TypeKind == TypeKind.Class ||
                             classFieldMember.Type.TypeKind == TypeKind.Struct)
                    {
                        if (!SourceGenUtils.HasAttribute(classFieldMember.Type, bitStructAttributeSymbol))
                        {
                            // Type requires BitStruct attribute.
                            return;
                        }

                        INamedTypeSymbol       fieldType         = (INamedTypeSymbol)classFieldMember.Type;
                        ClassSerializeSizeInfo fieldTypeSizeInfo = classesSizeInfo[fieldType];

                        string serializerClassFullName = CreateSerializerFullName(fieldType);

                        if (fieldTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic)
                        {
                            sizeFuncBuilder.Append($@"
            result += {serializerClassFullName}.CalculateSize(value.{classFieldMember.Name});
");
                        }

                        serializeFuncBuilder.Append($@"
            output = {serializerClassFullName}.Serialize(output, value.{classFieldMember.Name});
");

                        deserializeFuncBuilder.Append($@"
            {{
                input = {serializerClassFullName}.Deserialize(input, out var fieldValue);
                value.{classFieldMember.Name} = fieldValue;
            }}
");
                    }
                    else if (classFieldMember.Type.TypeKind == TypeKind.Array)
                    {
                        BitArrayAttribute bitArrayAttribute = SourceGenUtils.GetAttribute <BitArrayAttribute>(classFieldMember, bitArrayAttributeSymbol);
                        if (bitArrayAttribute == null)
                        {
                            // Type requires BitArray attribute.
                            return;
                        }

                        IArrayTypeSymbol arrayType           = (IArrayTypeSymbol)classFieldMember.Type;
                        string           elementTypeFullName = SourceGenUtils.GetTypeFullName(arrayType.ElementType);

                        ClassSerializeSizeInfo elementTypeSizeInfo;
                        string calculateElementSize;
                        string serializeItem;
                        string deserializeItem;

                        if (arrayType.ElementType.IsIntegerType() ||
                            arrayType.ElementType.TypeKind == TypeKind.Enum)
                        {
                            IntegerOrEnumTypeInfo elementTypeInfo = GetIntegerOrEnumTypeInfo((INamedTypeSymbol)arrayType.ElementType, bitStructAttribute.Endianess);
                            elementTypeSizeInfo = new ClassSerializeSizeInfo()
                            {
                                Type      = ClassSerializeSizeType.Const,
                                ConstSize = elementTypeInfo.TypeSize,
                            };

                            calculateElementSize = $"result += {elementTypeInfo.TypeSize};";

                            serializeItem = $@"
                        if (!{elementTypeInfo.SerializeFuncName}(output, {elementTypeInfo.SerializeTypeCast}item))
                        {{
                            throw new global::System.Exception(string.Format(""Not enough space to serialize item from list {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                        }}
                        output = output.Slice({elementTypeInfo.TypeSize});
";

                            deserializeItem = $@"
                        if (!{elementTypeInfo.DeserializeFuncName}(input, out var tmp))
                        {{
                            throw new global::System.Exception(string.Format(""Not enough data to deserialize item from list {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                        }}
                        var item = {elementTypeInfo.DeserializeTypeCast}tmp;
                        input = input.Slice({elementTypeInfo.TypeSize});
";
                        }
                        else if (arrayType.ElementType.TypeKind == TypeKind.Class ||
                                 arrayType.ElementType.TypeKind == TypeKind.Struct)
                        {
                            INamedTypeSymbol elementType = (INamedTypeSymbol)arrayType.ElementType;
                            elementTypeSizeInfo = classesSizeInfo[elementType];

                            string elementSerializerClassFullName = CreateSerializerFullName(elementType);

                            if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Const)
                            {
                                calculateElementSize = $@"result += {elementSerializerClassFullName}.Size;";
                            }
                            else
                            {
                                calculateElementSize = $@"result += {elementSerializerClassFullName}.CalculateSize(item);";
                            }

                            serializeItem   = $@"output = {elementSerializerClassFullName}.Serialize(output, item);";
                            deserializeItem = $@"input = {elementSerializerClassFullName}.Deserialize(input, out var item);";
                        }
                        else
                        {
                            // Can't serialize type.
                            return;
                        }

                        switch (bitArrayAttribute.SizeType)
                        {
                        case BitArraySizeType.Const:
                            if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic)
                            {
                                sizeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                int collectionCount = array?.Length ?? 0;
                if (collectionCount > {bitArrayAttribute.ConstSize})
                {{
                    throw new global::System.Exception(string.Format($""Constant size list {{0}} from type {{1}} has too many items."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                }}

                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {calculateElementSize}
                    }}
                }}

                int backfillCount = {bitArrayAttribute.ConstSize} - collectionCount;
                if (backfillCount > 0)
                {{
                    {elementTypeFullName} item = default;
                    for (int i = 0; i != backfillCount; ++i)
                    {{
                        {calculateElementSize}
                    }}
                }}
            }}
");
                            }

                            serializeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                int collectionCount = array?.Length ?? 0;
                if (collectionCount > {bitArrayAttribute.ConstSize})
                {{
                    throw new global::System.Exception(string.Format($""Constant size list {{0}} from type {{1}} has too many items."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                }}

                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {serializeItem}
                    }}
                }}

                int backfillCount = {bitArrayAttribute.ConstSize} - collectionCount;
                if (backfillCount > 0)
                {{
                    {elementTypeFullName} item = default;
                    for (int i = 0; i != backfillCount; ++i)
                    {{
                        {serializeItem}
                    }}
                }}
            }}
");

                            deserializeFuncBuilder.Append($@"
            {{
                var array = new {elementTypeFullName}[{bitArrayAttribute.ConstSize}];

                for (int i = 0; i != {bitArrayAttribute.ConstSize}; ++i)
                {{
                    {deserializeItem}
                    array[i] = item;
                }}

                value.{classFieldMember.Name} = array;
            }}
");

                            break;

                        case BitArraySizeType.EndFill:
                            sizeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {calculateElementSize}
                    }}
                }}
            }}
");

                            serializeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {serializeItem}
                    }}
                }}
            }}
");
                            deserializeFuncBuilder.Append($@"
                var list = new global::System.Collections.Generic.List<{elementTypeFullName}>();

                while (!input.IsEmpty)
                {{
                    {deserializeItem}
                    list.Add(item);
                }}

                value.{classFieldMember.Name} = list.ToArray();
");

                            break;

                        default:
                            // Unknown BitArraySizeType.
                            return;
                        }
                    }
                    else
                    {
                        // Can't serialize type.
                        return;
                    }
                }

                sizeFuncBuilder.Append($@"
            return result;
        }}
");

                serializeFuncBuilder.Append($@"
            return output;
        }}
");

                deserializeFuncBuilder.Append($@"
            return input;
        }}
");

                sourceBuilder.Append(sizeFuncBuilder.ToString());
                sourceBuilder.Append(serializeFuncBuilder.ToString());
                sourceBuilder.Append(deserializeFuncBuilder.ToString());

                for (int i = 0; i != classContainingTypes.Length + 1; ++i)
                {
                    sourceBuilder.Append($@"
    }}
");
                }

                sourceBuilder.Append($@"
}}
");

                string sourceCode = sourceBuilder.ToString();
                context.AddSource($"{serializerClassName}.cs", SourceText.From(sourceCode, Encoding.UTF8));
            }
        }
예제 #15
0
        // Figures out which classes have a constant serialization size and which have a serialization size that can change
        // depending on the object's value.
        private Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo> GenerateClassSizeInfo(SourceGeneratorContext context, IReadOnlyList <INamedTypeSymbol> bitStructClasses,
                                                                                            INamedTypeSymbol bitStructAttributeSymbol, INamedTypeSymbol bitArrayAttributeSymbol)
        {
            // Fill in the results object with default values, to avoid needing to dynamically insert values into the dictionary.
            var classesSizeInfo = new Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo>();

            foreach (INamedTypeSymbol classSymbol in bitStructClasses)
            {
                classesSizeInfo.Add(classSymbol, new ClassSerializeSizeInfo()
                {
                    Type      = ClassSerializeSizeType.Dynamic,
                    ConstSize = 0,
                });
            }

            // A class has a constant size iff. all its fields are one of the following types:
            //   a. Integer type.
            //   b. Enum type.
            //   c. Classes that have a constant size.
            //   d. Arrays with a constant length and an element type of a, b or c.
            //
            // Because of c and d, this produces a tree(/graph) where constant-size-ness must be propogated from
            // the leaf nodes up the tree.
            //
            // The naive version of this algorithm would be to try to flow the constant-size-ness through the graph
            // directly. (This would probably be done using a dynamic programming pattern.) However, this wouldn't
            // handle recursive data structures and could get stuck in an infinite loop.
            //
            // So, instead this algorithm borrows techniques from the type inference algorithm. Specifically, it
            // begins by assuming all types have a dynamic size. Then it iterates through the list of types and
            // looks for types it can update to have a known constant size. The algorithm keeps looping through
            // the list of types until there are no more changes.
            for (bool changed = true; changed; changed = false)
            {
                foreach (INamedTypeSymbol classSymbol in bitStructClasses)
                {
                    // Assume the class's size is constant until a member variable is found with a dynamic size.
                    ClassSerializeSizeInfo classSizeInfo = new ClassSerializeSizeInfo()
                    {
                        Type      = ClassSerializeSizeType.Const,
                        ConstSize = 0,
                    };

                    if (classesSizeInfo[classSymbol].Type == ClassSerializeSizeType.Const)
                    {
                        // Type's serialization size is already known to be constant. So, no point checking again.
                        continue;
                    }

                    // Iterate through the class's member variables.
                    foreach (IFieldSymbol classFieldMember in GetClassFieldMembers(classSymbol))
                    {
                        // Integers and enums have a constant size.
                        if (classFieldMember.Type.IsIntegerType() ||
                            classFieldMember.Type.TypeKind == TypeKind.Enum)
                        {
                            INamedTypeSymbol      fieldType     = (INamedTypeSymbol)classFieldMember.Type;
                            IntegerOrEnumTypeInfo fieldTypeInfo = GetIntegerOrEnumTypeInfo(fieldType, BitEndianess.LittleEndian);

                            classSizeInfo.ConstSize += fieldTypeInfo.TypeSize;
                        }
                        else if (classFieldMember.Type.TypeKind == TypeKind.Class ||
                                 classFieldMember.Type.TypeKind == TypeKind.Struct)
                        {
                            INamedTypeSymbol fieldType = classFieldMember.Type as INamedTypeSymbol;
                            if (!classesSizeInfo.ContainsKey(fieldType))
                            {
                                // Classes/structs must have the BitStruct attribute.
                                // This ensures that the code is specific about which endianess is required for a class because this it not inherited.
                                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Type {classFieldMember.Type.Name} must have a BitStruct attribute.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                                continue;
                            }

                            ClassSerializeSizeInfo fieldTypeSizeInfo = classesSizeInfo[fieldType];

                            if (fieldTypeSizeInfo.Type == ClassSerializeSizeType.Const)
                            {
                                // Type has a constant size.
                                classSizeInfo.ConstSize += fieldTypeSizeInfo.ConstSize;
                            }
                            else
                            {
                                // Type has a dynamic size (or is not yet known to have a constant size).
                                classSizeInfo.Type = ClassSerializeSizeType.Dynamic;
                            }
                        }
                        else if (classFieldMember.Type.TypeKind == TypeKind.Array)
                        {
                            IArrayTypeSymbol arrayType = (IArrayTypeSymbol)classFieldMember.Type;

                            BitArrayAttribute bitArrayAttribute = SourceGenUtils.GetAttribute <BitArrayAttribute>(classFieldMember, bitArrayAttributeSymbol);
                            if (bitArrayAttribute == null)
                            {
                                // Arrays must be the BitArray attribute, as this specifies how the array's length is handled.
                                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Field {classFieldMember.Name} of type {classSymbol.Name} must have a BitArray attribute.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                                continue;
                            }

                            ClassSerializeSizeInfo elementTypeSizeInfo;

                            if (arrayType.ElementType.IsIntegerType() ||
                                arrayType.ElementType.TypeKind == TypeKind.Enum)
                            {
                                IntegerOrEnumTypeInfo elementTypeInfo = GetIntegerOrEnumTypeInfo((INamedTypeSymbol)arrayType.ElementType, BitEndianess.LittleEndian);
                                elementTypeSizeInfo = new ClassSerializeSizeInfo()
                                {
                                    Type      = ClassSerializeSizeType.Const,
                                    ConstSize = elementTypeInfo.TypeSize,
                                };
                            }
                            else if (arrayType.ElementType.TypeKind == TypeKind.Class ||
                                     arrayType.ElementType.TypeKind == TypeKind.Struct)
                            {
                                INamedTypeSymbol elementType = (INamedTypeSymbol)arrayType.ElementType;
                                elementTypeSizeInfo = classesSizeInfo[elementType];
                            }
                            else
                            {
                                // Unsupported type.
                                // Note: Arrays of arrays aren't supported, as the BitArray attribute is required for the inner arrays.
                                // Though this can be handled by wrapping the inner arrays in a struct.
                                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Can't serialize type of {arrayType.ElementType.Name}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                                continue;
                            }

                            // A constant length array of a constant size element type has a constant serialization length.
                            // Otherwise, it has a dynamic serialization length.
                            switch (bitArrayAttribute.SizeType)
                            {
                            case BitArraySizeType.Const:
                                if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic)
                                {
                                    classSizeInfo.Type = ClassSerializeSizeType.Dynamic;
                                }
                                else
                                {
                                    classSizeInfo.ConstSize += elementTypeSizeInfo.ConstSize * bitArrayAttribute.ConstSize;
                                }
                                break;

                            case BitArraySizeType.EndFill:
                                classSizeInfo.Type = ClassSerializeSizeType.Dynamic;
                                break;

                            default:
                                // Unsupported type.
                                context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Unknown BitArraySizeType value of {bitArrayAttribute.SizeType}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                                continue;
                            }
                        }
                        else
                        {
                            // Unsupported type.
                            context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Can't serialize type of {classSymbol.Name}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                            continue;
                        }
                    }

                    // Check if the class's size info has changed.
                    if (classSizeInfo != classesSizeInfo[classSymbol])
                    {
                        classesSizeInfo[classSymbol] = classSizeInfo;
                        changed = true;
                    }
                }
            }

            return(classesSizeInfo);
        }
예제 #16
0
        public void Execute(SourceGeneratorContext generatorContext)
        {
            var ct          = generatorContext.CancellationToken;
            var compilation = generatorContext.Compilation;

            var jensonSerializeAttribute         = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonSerializeAttribute");
            var jensonPropertyAttribute          = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonPropertyAttribute");
            var jensonTypeDiscriminatorAttribute = compilation.GetTypeByMetadataName("Jenson.Attributes.JensonTypeDiscriminatorAttribute");
            var jsonPropertyNameAttribute        = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonPropertyNameAttribute");
            var jsonIgnoreAttribute = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonIgnoreAttribute");

            if (jensonSerializeAttribute is null)
            {
                throw new Exception("Missing JensonSerializeAttribute in compilation.");
            }
            if (jensonPropertyAttribute is null)
            {
                throw new Exception("Missing JensonPropertyAttribute in compilation.");
            }
            if (jensonTypeDiscriminatorAttribute is null)
            {
                throw new Exception("Missing JensonTypeDiscriminatorAttribute in compilation.");
            }
            if (jsonPropertyNameAttribute is null)
            {
                throw new Exception("Missing JsonPropertyName in compilation.");
            }
            if (jsonIgnoreAttribute is null)
            {
                throw new Exception("Missing JsonIgnore in compilation.");
            }

            var context = new JensonContext(
                generatorContext,
                jensonSerializeAttribute,
                jensonPropertyAttribute,
                jensonTypeDiscriminatorAttribute,
                jsonPropertyNameAttribute,
                jsonIgnoreAttribute);

            var rootTypes = compilation.GlobalNamespace.GetAllTypes(ct)
                            .Where(t => t.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, jensonSerializeAttribute)));

            ct.ThrowIfCancellationRequested();

            var sb = new StringBuilder();

            foreach (var t in rootTypes)
            {
                var typeDeclarations = t.DeclaringSyntaxReferences.Select(sr =>
                {
                    return((TypeDeclarationSyntax)sr.GetSyntax());
                });

                var isPartial = typeDeclarations.Any(td => td.Modifiers.Any(SyntaxKind.PartialKeyword));

                if (!isPartial)
                {
                    foreach (var td in typeDeclarations)
                    {
                        var diagnostic = Diagnostic.Create(MissingPartialModifier, td.GetLocation(), new object[] { t.Name });
                        generatorContext.ReportDiagnostic(diagnostic);
                    }
                }

                var w = new SourceWriter();
                GenerateTypeSerializer(context, w, t);

                generatorContext.AddSource($"{t.Name}.jenson", SourceText.From(w.ToString(), Encoding.UTF8));

                // for debugging
                sb.Append(w.ToString()); sb.AppendLine(); sb.AppendLine();
            }

            // for debugging
            File.WriteAllText("C:\\Users\\jesse\\Desktop\\converter.cs", sb.ToString());
        }