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