Пример #1
0
        /// <summary>
        /// Returns the fully qualified clone method name.
        /// </summary>
        public static string GenerateCloneMethodsForAssembly(
            CodeWriter writer,
            CompilerOptions options,
            Assembly assembly,
            TypeModelContainer container)
        {
            string @namespace = $"FlatSharp.Compiler.Generated";
            string className  = $"CloneHelpers_{Guid.NewGuid():n}";
            string methodName = "Clone";

            string fullyQualifiedMethodName = $"{@namespace}.{className}.{methodName}";

            HashSet <Type> seenTypes = new HashSet <Type>();

            foreach (var type in assembly.GetTypes())
            {
                if (type.IsNested)
                {
                    continue;
                }

                if (container.TryCreateTypeModel(type, out var typeModel))
                {
                    typeModel.TraverseObjectGraph(seenTypes);
                }
            }

            Dictionary <Type, string> methodNameMap = new Dictionary <Type, string>();

            foreach (var seenType in seenTypes)
            {
                methodNameMap[seenType] = fullyQualifiedMethodName;
            }

            writer.AppendLine($"namespace {@namespace}");
            using (writer.WithBlock())
            {
                writer.AppendLine($"internal static class {className}");
                using (writer.WithBlock())
                {
                    foreach (var seenType in seenTypes)
                    {
                        if (!container.TryCreateTypeModel(seenType, out ITypeModel? model))
                        {
                            ErrorContext.Current.RegisterError($"Unable to create type model for Type '{seenType.FullName}.'");
                            continue;
                        }

                        GenerateCloneMethod(writer, options, model, methodNameMap);
                    }
                }
            }

            return(fullyQualifiedMethodName);
        }
Пример #2
0
        private static void GenerateCloneMethod(
            CodeWriter codeWriter,
            CompilerOptions options,
            ITypeModel typeModel,
            Dictionary <Type, string> methodNameMap)
        {
            string typeName            = CSharpHelpers.GetCompilableTypeName(typeModel.ClrType);
            CodeGeneratedMethod method = typeModel.CreateCloneMethodBody(new CloneCodeGenContext("item", methodNameMap));

            if (!typeModel.ClrType.IsValueType)
            {
                typeName += "?";

                if (options.NullableWarnings == true)
                {
                    codeWriter.AppendLine("[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull(\"item\")]");
                }
            }

            codeWriter.AppendLine(method.GetMethodImplAttribute());
            codeWriter.AppendLine($"public static {typeName} Clone({typeName} item)");
            using (codeWriter.WithBlock())
            {
                codeWriter.AppendLine(method.MethodBody);
            }
        }
Пример #3
0
        protected override void OnWriteCode(CodeWriter writer, CodeWritingPass pass, string forFile, IReadOnlyDictionary <string, string> precompiledSerailizers)
        {
            this.AssignIndexes();

            string attribute = this.IsTable ? "[FlatBufferTable]" : "[FlatBufferStruct]";

            writer.AppendLine(attribute);
            writer.AppendLine("[System.Runtime.CompilerServices.CompilerGenerated]");
            writer.AppendLine($"public partial class {this.Name} : object");
            writer.AppendLine($"{{");

            using (writer.IncreaseIndent())
            {
                writer.AppendLine($"partial void OnInitialized();");

                // default ctor.
                string obsolete = this.ObsoleteDefaultConstructor ? $"[Obsolete]" : string.Empty;
                writer.AppendLine($"{obsolete} public {this.Name}() {{ this.OnInitialized(); }}");

                writer.AppendLine($"public {this.Name}({this.Name} source)");
                using (writer.WithBlock())
                {
                    foreach (var field in this.Fields)
                    {
                        field.WriteCopyConstructorLine(writer, "source", this);
                    }

                    writer.AppendLine("this.OnInitialized();");
                }

                foreach (var field in this.Fields)
                {
                    if (!this.IsTable && field.Deprecated)
                    {
                        ErrorContext.Current?.RegisterError($"FlatBuffer structs may not have deprecated fields.");
                    }

                    field.WriteField(writer, this);
                }

                if (pass == CodeWritingPass.SecondPass && precompiledSerailizers != null && this.RequestedSerializer != null)
                {
                    if (precompiledSerailizers.TryGetValue(this.FullName, out string serializer))
                    {
                        writer.AppendLine($"public static ISerializer<{this.FullName}> Serializer {{ get; }} = new {RoslynSerializerGenerator.GeneratedSerializerClassName}().AsISerializer();");
                        writer.AppendLine(string.Empty);
                        writer.AppendLine($"#region Serializer for {this.FullName}");
                        writer.AppendLine(serializer);
                        writer.AppendLine($"#endregion");
                    }
                    else
                    {
                        ErrorContext.Current.RegisterError($"Table {this.FullName} requested serializer, but none was found.");
                    }
                }
            }

            writer.AppendLine($"}}");
        }
Пример #4
0
        public void EmitStructVector(TableOrStructDefinition parent, CodeWriter writer, CompileContext context)
        {
            string typeName = parent.ResolveTypeName(this.FbsTypeName, context, out ITypeModel? typeModel);

            writer.AppendLine($"public {this.ClassName} {this.Name} {{ get; }}");
            writer.AppendLine();

            // class is next.
            writer.AppendLine($"public sealed partial class {this.ClassName} : System.Collections.Generic.IEnumerable<{typeName}>");
            using (writer.WithBlock())
            {
                writer.AppendLine($"private readonly {parent.Name} item;");

                // ctor
                writer.AppendLine();
                writer.AppendLine($"public {this.ClassName}({parent.Name} item)");
                using (writer.WithBlock())
                {
                    writer.AppendLine($"this.item = item;");
                }

                writer.AppendLine($"public int Count => {this.PropertyNames.Count};");

                // indexer
                writer.AppendLine();
                writer.AppendLine($"public {typeName} this[int index]");
                using (writer.WithBlock())
                {
                    writer.AppendLine("get");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine("var thisItem = this.item;");
                        writer.AppendLine("switch (index)");
                        using (writer.WithBlock())
                        {
                            for (int i = 0; i < this.PropertyNames.Count; ++i)
                            {
                                writer.AppendLine($"case {i}: return thisItem.{this.PropertyNames[i]};");
                            }

                            writer.AppendLine($"default: throw new IndexOutOfRangeException();");
                        }
                    }

                    writer.AppendLine();

                    writer.AppendLine("set");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine("var thisItem = this.item;");
                        writer.AppendLine("switch (index)");
                        using (writer.WithBlock())
                        {
                            for (int i = 0; i < this.PropertyNames.Count; ++i)
                            {
                                writer.AppendLine($"case {i}: thisItem.{this.PropertyNames[i]} = value; break;");
                            }

                            writer.AppendLine($"default: throw new IndexOutOfRangeException();");
                        }
                    }
                }

                writer.AppendLine("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => this.GetEnumerator();");
                writer.AppendLine();
                writer.AppendLine($"public System.Collections.Generic.IEnumerator<{typeName}> GetEnumerator()");
                using (writer.WithBlock())
                {
                    writer.AppendLine("var thisItem = this.item;");
                    for (int i = 0; i < this.PropertyNames.Count; ++i)
                    {
                        writer.AppendLine($"yield return thisItem.{this.PropertyNames[i]};");
                    }

                    if (this.PropertyNames.Count == 0)
                    {
                        writer.AppendLine("yield break;");
                    }
                }

                string arrayOrSpanType = $"{typeName}[]";
                if (typeModel is not null &&
                    typeModel.ClassifyContextually(FlatBufferSchemaType.Struct).IsRequiredValue())
                {
                    arrayOrSpanType = $"ReadOnlySpan<{typeName}>";
                }

                foreach (var collectionType in new[] { arrayOrSpanType, $"IReadOnlyList<{typeName}>" })
                {
                    writer.AppendMethodSummaryComment($"Deep copies the first {this.PropertyNames.Count} items from the source into this struct vector.");
                    writer.AppendLine($"public void CopyFrom({collectionType} source)");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine("var thisItem = this.item;");

                        // Load in reverse so that the JIT can just do a bounds check on the very first item.
                        // This also requries the parameter being a local variable instead of a param.
                        writer.AppendLine("var s = source;");
                        for (int i = this.PropertyNames.Count - 1; i >= 0; --i)
                        {
                            writer.AppendLine($"thisItem.{this.PropertyNames[i]} = {context.FullyQualifiedCloneMethodName}(s[{i}]);");
                        }
                    }
                }
            }
        }
Пример #5
0
        protected override void OnWriteCode(CodeWriter writer, CompileContext context)
        {
            this.AssignIndexes();

            string attribute = "[FlatBufferStruct]";

            if (this.IsTable)
            {
                if (string.IsNullOrEmpty(this.FileIdentifier))
                {
                    attribute = "[FlatBufferTable]";
                }
                else
                {
                    attribute = $"[FlatBufferTable({nameof(FlatBufferTableAttribute.FileIdentifier)} = \"{this.FileIdentifier}\")]";
                }
            }

            writer.AppendLine(attribute);
            writer.AppendLine("[System.Runtime.CompilerServices.CompilerGenerated]");
            writer.AppendLine($"public partial class {this.Name} : object");
            writer.AppendLine($"{{");

            using (writer.IncreaseIndent())
            {
                // Default ctor.
                var defaultCtorKind = this.DefaultConstructorKind ?? Compiler.DefaultConstructorKind.Public;
                if (defaultCtorKind != Compiler.DefaultConstructorKind.None)
                {
                    if (defaultCtorKind == Compiler.DefaultConstructorKind.PublicObsolete)
                    {
                        writer.AppendLine("[Obsolete]");
                    }

                    writer.AppendLine($"public {this.Name}()");
                    using (writer.WithBlock())
                    {
                        foreach (var field in this.Fields)
                        {
                            field.WriteDefaultConstructorLine(writer, context);
                        }

                        this.EmitStructVectorInitializations(writer);
                        writer.AppendLine("this.OnInitialized(null);");
                    }
                }
                else if (!this.IsTable)
                {
                    ErrorContext.Current.RegisterError("Structs must have default constructors.");
                }

                writer.AppendLine("#pragma warning disable CS8618"); // NULL FORGIVING
                writer.AppendLine($"protected {this.Name}({nameof(FlatBufferDeserializationContext)} context)");
                using (writer.WithBlock())
                {
                    this.EmitStructVectorInitializations(writer);
                    writer.AppendLine("this.OnInitialized(context);");
                }
                writer.AppendLine("#pragma warning restore CS8618"); // NULL FORGIVING

                // Copy constructor.
                writer.AppendLine($"public {this.Name}({this.Name} source)");
                using (writer.WithBlock())
                {
                    foreach (var field in this.Fields)
                    {
                        field.WriteCopyConstructorLine(writer, "source", context);
                    }

                    this.EmitStructVectorInitializations(writer);
                    writer.AppendLine("this.OnInitialized(null);");
                }

                writer.AppendLine($"partial void OnInitialized({nameof(FlatBufferDeserializationContext)}? context);");

                foreach (var field in this.Fields)
                {
                    field.WriteField(writer, this, context);
                }

                foreach (var structVector in this.StructVectors)
                {
                    structVector.EmitStructVector(this, writer, context);
                }

                if (context.CompilePass >= CodeWritingPass.SerializerGeneration && this.RequestedSerializer is not null)
                {
                    // generate the serializer.
                    string serializer = this.GenerateSerializerForType(
                        context,
                        this.RequestedSerializer.Value);

                    writer.AppendLine($"public static ISerializer<{this.FullName}> {SerializerPropertyName} {{ get; }} = new {RoslynSerializerGenerator.GeneratedSerializerClassName}().AsISerializer();");
                    writer.AppendLine(string.Empty);
                    writer.AppendLine($"#region Serializer for {this.FullName}");
                    writer.AppendLine(serializer);
                    writer.AppendLine($"#endregion");
                }
            }

            writer.AppendLine($"}}");
        }
Пример #6
0
        public void EmitStructVector(TableOrStructDefinition parent, CodeWriter writer, CompileContext context)
        {
            string typeName = parent.ResolveTypeName(this.FbsTypeName, context);

            string className = $"{this.Name}Vector";

            // two parts: property definition and class definition.
            writer.AppendLine($"private {className}? _{this.Name};");
            writer.AppendLine($"public {className} {this.Name} => (this._{this.Name} ??= new {className}(this));");
            writer.AppendLine();

            // class is next.
            writer.AppendLine($"public sealed partial class {className} : System.Collections.Generic.IEnumerable<{typeName}>");
            using (writer.WithBlock())
            {
                writer.AppendLine($"private readonly {parent.Name} item;");

                // ctor
                writer.AppendLine();
                writer.AppendLine($"public {className}({parent.Name} item)");
                using (writer.WithBlock())
                {
                    writer.AppendLine($"this.item = item;");
                }

                writer.AppendLine($"public int Count => {this.PropertyNames.Count};");

                // indexer
                writer.AppendLine();
                writer.AppendLine($"public {typeName} this[int index]");
                using (writer.WithBlock())
                {
                    writer.AppendLine("get");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine("switch (index)");
                        using (writer.WithBlock())
                        {
                            for (int i = 0; i < this.PropertyNames.Count; ++i)
                            {
                                writer.AppendLine($"case {i}: return this.item.{this.PropertyNames[i]};");
                            }

                            writer.AppendLine($"default: throw new IndexOutOfRangeException();");
                        }
                    }

                    writer.AppendLine();

                    writer.AppendLine("set");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine("switch (index)");
                        using (writer.WithBlock())
                        {
                            for (int i = 0; i < this.PropertyNames.Count; ++i)
                            {
                                writer.AppendLine($"case {i}: this.item.{this.PropertyNames[i]} = value; break;");
                            }

                            writer.AppendLine($"default: throw new IndexOutOfRangeException();");
                        }
                    }
                }

                writer.AppendLine("System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => this.GetEnumerator();");
                writer.AppendLine();
                writer.AppendLine($"public System.Collections.Generic.IEnumerator<{typeName}> GetEnumerator()");
                using (writer.WithBlock())
                {
                    for (int i = 0; i < this.PropertyNames.Count; ++i)
                    {
                        writer.AppendLine($"yield return this.item.{this.PropertyNames[i]};");
                    }

                    if (this.PropertyNames.Count == 0)
                    {
                        writer.AppendLine("yield break;");
                    }
                }
            }
        }