// TODO: consider moving to TableOrStructDefinition. private static string GenerateSerializerForType(Assembly assembly, TableOrStructDefinition tableOrStruct) { CSharpHelpers.ConvertProtectedInternalToProtected = false; try { Type type = assembly.GetType(tableOrStruct.FullName); var options = new FlatBufferSerializerOptions(tableOrStruct.RequestedSerializer.Value); var generator = new RoslynSerializerGenerator(options, TypeModelContainer.CreateDefault()); var method = generator .GetType() .GetMethod(nameof(RoslynSerializerGenerator.GenerateCSharp), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) .MakeGenericMethod(type); try { string code = (string)method.Invoke(generator, new[] { "private" }); return(code); } catch (TargetInvocationException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); throw; } } finally { CSharpHelpers.ConvertProtectedInternalToProtected = true; } }
public void WriteField(CodeWriter writer, TableOrStructDefinition parent) { ErrorContext.Current.WithScope(this.Name, (Action)(() => { bool isVector = this.VectorType != VectorType.None; EnumDefinition enumDefinition = null; if (parent.TryResolveName(this.FbsFieldType, out var typeDefinition)) { enumDefinition = typeDefinition as EnumDefinition; } string defaultValue = string.Empty; string clrType; bool isBuiltInType = SchemaDefinition.TryResolve(this.FbsFieldType, out ITypeModel builtInType); if (isBuiltInType) { clrType = builtInType.ClrType.FullName; } else { clrType = typeDefinition?.GlobalName ?? this.FbsFieldType; } if (!string.IsNullOrEmpty(this.DefaultValue)) { if (isBuiltInType && builtInType.TryFormatStringAsLiteral(this.DefaultValue, out defaultValue)) { // intentionally left blank. } else if (enumDefinition?.NameValuePairs.ContainsKey(this.DefaultValue) == true) { // Also ok. defaultValue = $"{clrType}.{this.DefaultValue}"; } else if (enumDefinition?.UnderlyingType.TryFormatStringAsLiteral(this.DefaultValue, out defaultValue) == true) { defaultValue = $"({clrType})({defaultValue})"; } else { ErrorContext.Current?.RegisterError($"Only primitive types and enums may have default values. Field '{this.Name}' declares a default value but has type '{this.FbsFieldType}'."); } } if (this.SetterKind == SetterKind.None && this.NonVirtual == true) { ErrorContext.Current?.RegisterError($"NonVirtual:true cannot be combined with setter:None."); } this.WriteField(writer, this.GetClrTypeName(parent), defaultValue, this.Name, parent); })); }
// TODO: consider moving to TableOrStructDefinition. private static string GenerateSerializerForType(Assembly assembly, TableOrStructDefinition tableOrStruct) { Type type = assembly.GetType(tableOrStruct.FullName); var options = new FlatBufferSerializerOptions(tableOrStruct.RequestedSerializer.Value); var generator = new RoslynSerializerGenerator(options); var method = generator .GetType() .GetMethod(nameof(RoslynSerializerGenerator.GenerateCSharp), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public) .MakeGenericMethod(type); string code = (string)method.Invoke(generator, new[] { "private" }); return(code); }
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}]);"); } } } } }
public FieldVisitor(TableOrStructDefinition parent) { this.parent = parent; }
private void WriteField( CodeWriter writer, string clrTypeName, string defaultValue, string name, TableOrStructDefinition parent) { string defaultValueAttribute = string.Empty; string defaultValueAssignment = string.Empty; string isKey = string.Empty; string sortedVector = string.Empty; string isDeprecated = string.Empty; if (!string.IsNullOrEmpty(defaultValue)) { defaultValueAttribute = $", DefaultValue = {defaultValue}"; defaultValueAssignment = $" = {defaultValue};"; } if (this.SortedVector) { sortedVector = ", SortedVector = true"; } if (this.IsKey) { isKey = ", Key = true"; } if (this.Deprecated) { isDeprecated = ", Deprecated = true"; } bool isNonVirtual = false; if (this.NonVirtual != null) { isNonVirtual = this.NonVirtual.Value; } else if (parent.NonVirtual != null) { isNonVirtual = parent.NonVirtual.Value; } string accessModifier = this.SetterKind switch { SetterKind.Public => string.Empty, // setter has same access as getter. SetterKind.Protected => "protected", SetterKind.ProtectedInternal => "protected internal", SetterKind.None => null, _ => string.Empty, }; string setter = $"{accessModifier} set;"; if (accessModifier == null) { setter = string.Empty; } writer.AppendLine($"[FlatBufferItem({this.Index}{defaultValueAttribute}{isDeprecated}{sortedVector}{isKey})]"); writer.AppendLine($"public {(isNonVirtual ? string.Empty : "virtual ")}{clrTypeName} {name} {{ get; {setter} }}{defaultValueAssignment}"); }
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;"); } } } }