예제 #1
0
        private static string CreateCSharp(BaseSchemaMember rootNode)
        {
            if (ErrorContext.Current.Errors.Any())
            {
                throw new InvalidFbsFileException(ErrorContext.Current.Errors);
            }

            var tablesNeedingSerializers = new List <TableOrStructDefinition>();
            var rpcDefinitions           = new List <RpcDefinition>();

            FindItemsRequiringSecondCodePass(rootNode, tablesNeedingSerializers, rpcDefinitions);

            if (tablesNeedingSerializers.Count == 0 && rpcDefinitions.Count == 0)
            {
                // Hey, no serializers or RPCs. We're all done. Go ahead and return the code we already generated.
                CodeWriter tempWriter = new CodeWriter();
                rootNode.WriteCode(tempWriter, CodeWritingPass.SecondPass, rootNode.DeclaringFile, new Dictionary <string, string>());

                if (ErrorContext.Current.Errors.Any())
                {
                    throw new InvalidFbsFileException(ErrorContext.Current.Errors);
                }

                return(tempWriter.ToString());
            }

            // Compile the assembly so that we may generate serializers for the data contracts defined in this FBS file.;
            // Compile with firstpass here to include all data (even stuff from includes).
            CodeWriter writer = new CodeWriter();

            rootNode.WriteCode(writer, CodeWritingPass.FirstPass, rootNode.DeclaringFile, new Dictionary <string, string>());
            if (ErrorContext.Current.Errors.Any())
            {
                throw new InvalidFbsFileException(ErrorContext.Current.Errors);
            }

            string code = writer.ToString();

            var(assembly, _, _) = RoslynSerializerGenerator.CompileAssembly(code, true);

            Dictionary <string, string> generatedSerializers = new Dictionary <string, string>();

            foreach (var definition in tablesNeedingSerializers)
            {
                generatedSerializers[definition.FullName] = GenerateSerializerForType(assembly, definition);
            }

            writer = new CodeWriter();
            rootNode.WriteCode(writer, CodeWritingPass.SecondPass, rootNode.DeclaringFile, generatedSerializers);

            if (ErrorContext.Current.Errors.Any())
            {
                throw new InvalidFbsFileException(ErrorContext.Current.Errors);
            }

            string rawCode       = writer.ToString();
            string formattedCode = RoslynSerializerGenerator.GetFormattedText(rawCode);

            return(formattedCode);
        }
예제 #2
0
 public TableOrStructDefinition(
     string name,
     FlatBufferDeserializationOption?serializerFlags,
     BaseSchemaMember parent) : base(name, parent)
 {
     this.RequestedSerializer = serializerFlags;
 }
예제 #3
0
        private static string CreateCSharp(BaseSchemaMember rootNode, CompilerOptions options)
        {
            ErrorContext.Current.ThrowIfHasErrors();

            if (string.IsNullOrEmpty(rootNode.DeclaringFile))
            {
                throw new InvalidFbsFileException("FlatSharp.Internal: RootNode missing declaring file");
            }

            Assembly?  assembly = null;
            CodeWriter writer   = new CodeWriter();
            var        steps    = new[]
            {
                CodeWritingPass.Initialization,
                CodeWritingPass.PropertyModeling,
                CodeWritingPass.SerializerGeneration,
                CodeWritingPass.RpcGeneration,
            };

            foreach (var step in steps)
            {
                var localOptions = options;

                if (step <= CodeWritingPass.PropertyModeling)
                {
                    localOptions = localOptions with {
                        NullableWarnings = false
                    };
                }

                if (step > CodeWritingPass.Initialization)
                {
                    string code = writer.ToString();
                    (assembly, _, _) = RoslynSerializerGenerator.CompileAssembly(code, true);
                }

                writer = new CodeWriter();

                rootNode.WriteCode(
                    writer,
                    new CompileContext
                {
                    CompilePass        = step,
                    Options            = localOptions,
                    RootFile           = rootNode.DeclaringFile,
                    PreviousAssembly   = assembly,
                    TypeModelContainer = TypeModelContainer.CreateDefault(),
                });

                ErrorContext.Current.ThrowIfHasErrors();
            }

            string rawCode       = writer.ToString();
            string formattedCode = RoslynSerializerGenerator.GetFormattedText(rawCode);

            return(formattedCode);
        }
    }
예제 #4
0
        private static string CreateCSharp(string fbsSchema, string inputHash)
        {
            BaseSchemaMember rootNode = ParseSyntax(fbsSchema, inputHash);

            if (ErrorContext.Current.Errors.Any())
            {
                throw new InvalidFbsFileException(ErrorContext.Current.Errors);
            }

            // Create the first pass of the code. This pass includes the data contracts from the FBS file.
            // If the schema requests a pregenerated serializer, then we'll need to load this code, generate
            // the serializer, and then rebuild it.
            CodeWriter writer = new CodeWriter();

            rootNode.WriteCode(writer, CodeWritingPass.FirstPass, null);

            if (ErrorContext.Current.Errors.Any())
            {
                throw new InvalidFbsFileException(ErrorContext.Current.Errors);
            }

            string code = writer.ToString();

            var tablesNeedingSerializers = new List <TableOrStructDefinition>();
            var rpcDefinitions           = new List <RpcDefinition>();

            FindItemsRequiringSecondCodePass(rootNode, tablesNeedingSerializers, rpcDefinitions);

            if (tablesNeedingSerializers.Count == 0 && rpcDefinitions.Count == 0)
            {
                // Hey, no serializers or RPCs. We're all done. Go ahead and return the code we already generated.
                return(code);
            }

            // Compile the assembly so that we may generate serializers for the data contracts defined in this FBS file.
            var(assembly, _, _) = RoslynSerializerGenerator.CompileAssembly(code, true);

            Dictionary <string, string> generatedSerializers = new Dictionary <string, string>();

            foreach (var definition in tablesNeedingSerializers)
            {
                generatedSerializers[definition.FullName] = GenerateSerializerForType(assembly, definition);
            }

            writer = new CodeWriter();
            rootNode.WriteCode(writer, CodeWritingPass.SecondPass, generatedSerializers);

            if (ErrorContext.Current.Errors.Any())
            {
                throw new InvalidFbsFileException(ErrorContext.Current.Errors);
            }

            string rawCode       = writer.ToString();
            string formattedCode = RoslynSerializerGenerator.GetFormattedText(rawCode);

            return(formattedCode);
        }
예제 #5
0
        protected BaseSchemaMember(string name, BaseSchemaMember parent)
        {
            this.children = new Dictionary <string, BaseSchemaMember>();
            this.Parent   = parent;
            this.Name     = name;
            this.FullName = string.Empty;

            if (this.Parent != null)
            {
                this.FullName = this.Parent.GetType() != typeof(RootNodeDefinition) ? $"{this.Parent.FullName}.{this.Name}" : this.Name;
            }
        }
예제 #6
0
 private string GetLinqSelectStatement(bool isBuiltIn, BaseSchemaMember nodeType)
 {
     if (isBuiltIn)
     {
         return(string.Empty);
     }
     else
     {
         string cloneStatement = nodeType.GetCopyExpression("x");
         return($".Select(x => {cloneStatement})");
     }
 }
예제 #7
0
        public void WriteField(CodeWriter writer, BaseSchemaMember schemaDefinition)
        {
            ErrorContext.Current.WithScope(this.Name, () =>
            {
                bool isVector = this.VectorType != VectorType.None;
                EnumDefinition enumDefinition = null;

                if (schemaDefinition.TryResolveName(this.FbsFieldType, out var typeDefinition))
                {
                    enumDefinition = typeDefinition as EnumDefinition;
                }

                string defaultValue = string.Empty;
                string clrType;
                bool isPrimitive = SchemaDefinition.TryResolveBuiltInScalarType(this.FbsFieldType, out IBuiltInScalarType builtInType);

                if (isPrimitive)
                {
                    clrType = builtInType.CSharpTypeName;
                }
                else
                {
                    clrType = typeDefinition?.GlobalName ?? this.FbsFieldType;
                }

                if (!string.IsNullOrEmpty(this.DefaultValue))
                {
                    if (isPrimitive)
                    {
                        defaultValue = builtInType.FormatLiteral(this.DefaultValue);
                    }
                    else if (enumDefinition != null)
                    {
                        if (enumDefinition.NameValuePairs.ContainsKey(this.DefaultValue))
                        {
                            // Referenced by name.
                            defaultValue = $"{clrType}.{this.DefaultValue}";
                        }
                        else
                        {
                            defaultValue = $"({clrType}){enumDefinition.UnderlyingType.FormatLiteral(this.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}'.");
                    }
                }

                this.WriteField(writer, this.GetClrTypeName(schemaDefinition), defaultValue, this.Name);
            });
        }
예제 #8
0
        public string GetClrTypeName(BaseSchemaMember baseMember)
        {
            string clrType;

            if (SchemaDefinition.TryResolveBuiltInScalarType(this.FbsFieldType, out IBuiltInScalarType builtInType))
            {
                clrType = builtInType.CSharpTypeName;
            }
            else
            {
                if (baseMember.TryResolveName(this.FbsFieldType, out var typeDefinition))
                {
                    if (typeDefinition is UnionDefinition unionDef)
                    {
                        clrType = unionDef.ClrTypeName;
                    }
                    else
                    {
                        clrType = typeDefinition.GlobalName;
                    }
                }
                else
                {
                    clrType = this.FbsFieldType;
                }
            }

            switch (this.VectorType)
            {
            case VectorType.Array:
                return($"{clrType}[]");

            case VectorType.IList:
                return($"IList<{clrType}>");

            case VectorType.IReadOnlyList:
                return($"IReadOnlyList<{clrType}>");

            case VectorType.Memory:
                return($"Memory<{clrType}>");

            case VectorType.ReadOnlyMemory:
                return($"ReadOnlyMemory<{clrType}>");

            case VectorType.None:
                return(clrType);

            default:
                throw new InvalidOperationException($"Unexpected value for vectortype: '{this.VectorType}'");
            }
        }
예제 #9
0
        private static BaseSchemaMember ParseSyntax(string fbsSchema, string inputHash)
        {
            AntlrInputStream  input       = new AntlrInputStream(fbsSchema);
            FlatBuffersLexer  lexer       = new FlatBuffersLexer(input);
            CommonTokenStream tokenStream = new CommonTokenStream(lexer);
            FlatBuffersParser parser      = new FlatBuffersParser(tokenStream);

            parser.AddErrorListener(new CustomErrorListener());

            SchemaVisitor    visitor  = new SchemaVisitor(inputHash);
            BaseSchemaMember rootNode = visitor.Visit(parser.schema());

            return(rootNode);
        }
예제 #10
0
        public void WriteCopyConstructorLine(CodeWriter writer, string sourceName, BaseSchemaMember parent)
        {
            bool isBuiltIn = SchemaDefinition.TryResolve(this.FbsFieldType, out _);

            bool foundNodeType = parent.TryResolveName(this.FbsFieldType, out var nodeType);

            if (!isBuiltIn && !foundNodeType)
            {
                ErrorContext.Current.RegisterError($"Unable to resolve type '{this.FbsFieldType}' as a built in or defined type");
                return;
            }

            string selectStatement = this.GetLinqSelectStatement(isBuiltIn, nodeType);

            switch (this.VectorType)
            {
            case VectorType.IList:
            case VectorType.IReadOnlyList:
                writer.AppendLine($"this.{this.Name} = {sourceName}.{this.Name}?{selectStatement}.ToList();");
                break;

            case VectorType.Array:
                writer.AppendLine($"this.{this.Name} = {sourceName}.{this.Name}?{selectStatement}.ToArray();");
                break;

            case VectorType.Memory:
            case VectorType.ReadOnlyMemory:
                writer.AppendLine($"this.{this.Name} = {sourceName}.{this.Name}.ToArray();");
                break;

            case VectorType.IIndexedVector:
                writer.AppendLine($"this.{this.Name} = {sourceName}.{this.Name}?.Clone(x => {nodeType.GetCopyExpression("x")});");
                break;

            case VectorType.None:
            {
                if (isBuiltIn)
                {
                    writer.AppendLine($"this.{this.Name} = {sourceName}.{this.Name};");
                }
                else
                {
                    string cloneStatement = nodeType.GetCopyExpression($"{sourceName}.{this.Name}");
                    writer.AppendLine($"this.{this.Name} = {cloneStatement};");
                }
            }
            break;
            }
        }
예제 #11
0
        public void WriteField(CodeWriter writer, BaseSchemaMember schemaDefinition)
        {
            ErrorContext.Current.WithScope(this.Name, (Action)(() =>
            {
                bool isVector = this.VectorType != VectorType.None;
                EnumDefinition enumDefinition = null;

                if (schemaDefinition.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}'.");
                    }
                }

                this.WriteField(writer, this.GetClrTypeName(schemaDefinition), defaultValue, this.Name);
            }));
        }
예제 #12
0
        private bool TryResolveDescendentsFromNode(BaseSchemaMember startNode, Span <string> parts, out BaseSchemaMember node)
        {
            node = startNode;
            while (node.Children.TryGetValue(parts[0], out node))
            {
                if (parts.Length == 1)
                {
                    return(true);
                }

                parts = parts.Slice(1);
            }

            return(false);
        }
예제 #13
0
        public void AddChild(BaseSchemaMember child)
        {
            ErrorContext.Current.WithScope(this.Name, () =>
            {
                if (!this.SupportsChildren)
                {
                    ErrorContext.Current?.RegisterError($"Unable to add child to current context.");
                }

                if (this.children.ContainsKey(child.Name))
                {
                    ErrorContext.Current?.RegisterError($"Duplicate member name '{child.Name}'.");
                }

                this.children[child.Name] = child;
            });
        }
예제 #14
0
        /// <summary>
        /// Recursively find tables for which the schema has asked for us to generate serializers.
        /// </summary>
        private static void FindItemsRequiringSecondCodePass(
            BaseSchemaMember node,
            List <TableOrStructDefinition> tables,
            List <RpcDefinition> rpcs)
        {
            if (node is TableOrStructDefinition tableOrStruct)
            {
                if (tableOrStruct.RequestedSerializer != null)
                {
                    tables.Add(tableOrStruct);
                }
            }
            else if (node is RpcDefinition rpc)
            {
                rpcs.Add(rpc);
            }

            foreach (var childNode in node.Children.Values)
            {
                FindItemsRequiringSecondCodePass(childNode, tables, rpcs);
            }
        }
예제 #15
0
        /// <summary>
        /// Resolves a name according to the relative namespace path.
        /// </summary>
        public bool TryResolveName(string name, out BaseSchemaMember node)
        {
            Span <string> parts = name.Split('.');

            // Go up to the first namespace node in the tree.
            BaseSchemaMember rootNode = this;

            while (!(rootNode is NamespaceDefinition) && !(rootNode is RootNodeDefinition))
            {
                rootNode = rootNode.Parent;
            }

            if (this.TryResolveDescendentsFromNode(rootNode, parts, out node))
            {
                return(true);
            }

            while (rootNode.Parent != null)
            {
                rootNode = rootNode.Parent;
            }

            return(this.TryResolveDescendentsFromNode(rootNode, parts, out node));
        }
예제 #16
0
 public EnumDefinition(string typeName, string underlyingTypeName, BaseSchemaMember parent)
     : base(typeName, parent)
 {
     this.FbsUnderlyingType = underlyingTypeName;
     this.ClrUnderlyingType = SchemaDefinition.ResolvePrimitiveType(underlyingTypeName);
 }
예제 #17
0
 public EnumVisitor(BaseSchemaMember parent)
 {
     this.parent = parent;
 }
예제 #18
0
 public NamespaceDefinition(string name, BaseSchemaMember parent) : base(name, parent)
 {
 }
예제 #19
0
 public FileIdentifierVisitor(BaseSchemaMember parent)
 {
     this.parent = parent;
 }
예제 #20
0
 public UnionVisitor(BaseSchemaMember parent)
 {
     this.parent = parent;
 }
예제 #21
0
 public EnumDefinition(string typeName, string underlyingTypeName, BaseSchemaMember parent)
     : base(typeName, parent)
 {
     this.FbsUnderlyingType = underlyingTypeName;
     this.UnderlyingType    = SchemaDefinition.ResolveBuiltInScalarType(underlyingTypeName);
 }
예제 #22
0
        public string GetClrTypeName(BaseSchemaMember baseMember)
        {
            string clrType;
            string sortKeyType = null;

            if (SchemaDefinition.TryResolve(this.FbsFieldType, out ITypeModel builtInType))
            {
                clrType = builtInType.ClrType.FullName;
            }
            else
            {
                if (baseMember.TryResolveName(this.FbsFieldType, out var typeDefinition))
                {
                    if (typeDefinition is UnionDefinition unionDef)
                    {
                        clrType = unionDef.ClrTypeName;
                    }
                    else
                    {
                        clrType = typeDefinition.GlobalName;
                    }

                    if (typeDefinition is TableOrStructDefinition tableOrStruct && tableOrStruct.IsTable)
                    {
                        sortKeyType = tableOrStruct.Fields.FirstOrDefault(x => x.IsKey)?.GetClrTypeName(baseMember);
                    }
                }
                else
                {
                    clrType = this.FbsFieldType;
                }
            }

            if (this.IsOptionalScalar)
            {
                // nullable.
                clrType = $"System.Nullable<{clrType}>";
            }
            else if (this.SharedString)
            {
                clrType = $"global::{typeof(SharedString).FullName}";
            }

            switch (this.VectorType)
            {
            case VectorType.Array:
                return($"{clrType}[]");

            case VectorType.IList:
                return($"IList<{clrType}>");

            case VectorType.IReadOnlyList:
                return($"IReadOnlyList<{clrType}>");

            case VectorType.Memory:
                return($"Memory<{clrType}>");

            case VectorType.ReadOnlyMemory:
                return($"ReadOnlyMemory<{clrType}>");

            case VectorType.IIndexedVector:
                if (string.IsNullOrWhiteSpace(sortKeyType))
                {
                    ErrorContext.Current.RegisterError($"Unable to determine key type for table {clrType}. Please make sure a property has the 'Key' metadata.");
                }
                return($"IIndexedVector<{sortKeyType}, {clrType}>");

            case VectorType.None:
                return(clrType);

            default:
                throw new InvalidOperationException($"Unexpected value for vectortype: '{this.VectorType}'");
            }
        }
예제 #23
0
 public FileIdentifierDefinition(string name, BaseSchemaMember parent) : base(name, parent)
 {
 }
예제 #24
0
 public TypeVisitor(BaseSchemaMember parent)
 {
     this.parent = parent;
 }
예제 #25
0
 public TableOrStructDefinition(
     string name,
     BaseSchemaMember parent) : base(name, parent)
 {
 }
예제 #26
0
 public UnionDefinition(string name, BaseSchemaMember parent) : base(name, parent)
 {
 }
예제 #27
0
 public EnumDefinition(string typeName, string underlyingTypeName, BaseSchemaMember parent)
     : base(typeName, parent)
 {
     this.FbsUnderlyingType = underlyingTypeName;
 }