Ejemplo n.º 1
0
        /// <summary>
        /// Gets a value indicating whether this type is POD (plain old data).
        /// </summary>
        public bool IsPod(Builder.BuildData buildData, ApiTypeInfo caller)
        {
            var apiType = BindingsGenerator.FindApiTypeInfo(buildData, this, caller);

            if (apiType != null)
            {
                if (!apiType.IsInited)
                {
                    apiType.Init(buildData);
                }
                return(apiType.IsPod);
            }

            if (IsPtr || IsRef)
            {
                return(true);
            }

            // Hardcoded cases
            if (Type == "String" || Type == "Array")
            {
                return(false);
            }

            // Fallback to default
            return(true);
        }
Ejemplo n.º 2
0
        public override void AddChild(ApiTypeInfo apiTypeInfo)
        {
            if (apiTypeInfo.Namespace == null)
            {
                apiTypeInfo.Namespace = Namespace;
            }

            base.AddChild(apiTypeInfo);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Gets a value indicating whether this type is POD (plain old data).
        /// </summary>
        public bool IsPod(Builder.BuildData buildData, ApiTypeInfo caller)
        {
            // Fixed array in C++ is converted into managed array in C# by default (char Data[100] -> char[] Data)
            if (Type.IsArray && !NoArray)
            {
                return(false);
            }

            return(Type.IsPod(buildData, caller));
        }
Ejemplo n.º 4
0
 public override void AddChild(ApiTypeInfo apiTypeInfo)
 {
     if (apiTypeInfo is EnumInfo)
     {
         base.AddChild(apiTypeInfo);
     }
     else
     {
         throw new NotSupportedException("Structures in API can have only enums as sub-types.");
     }
 }
Ejemplo n.º 5
0
        private static void ProcessAndValidate(BuildData buildData, ApiTypeInfo apiTypeInfo)
        {
            if (apiTypeInfo is ClassInfo classInfo)
            {
                ProcessAndValidate(buildData, classInfo);
            }
            else if (apiTypeInfo is StructureInfo structureInfo)
            {
                ProcessAndValidate(buildData, structureInfo);
            }

            foreach (var child in apiTypeInfo.Children)
            {
                ProcessAndValidate(buildData, child);
            }
        }
Ejemplo n.º 6
0
        private static ApiTypeInfo FindApiTypeInfoInner(TypeInfo typeInfo, ApiTypeInfo parent)
        {
            foreach (var child in parent.Children)
            {
                if (child.Name == typeInfo.Type)
                {
                    return(child);
                }

                var result = FindApiTypeInfoInner(typeInfo, child);
                if (result != null)
                {
                    return(result);
                }
            }

            return(null);
        }
Ejemplo n.º 7
0
        private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent)
        {
            ApiTypeInfo result = null;

            foreach (var child in parent.Children)
            {
                if (child.Name == typeInfo.Type)
                {
                    result = child;

                    // Special case when searching for template types (use instantiated template if input type has generic arguments)
                    if (child is ClassStructInfo classStructInfo && classStructInfo.IsTemplate && typeInfo.GenericArgs != null)
                    {
                        // Support instantiated template type with instantiated arguments (eg. 'typedef Vector3Base<Real> Vector3' where 'typedef float Real')
                        var inflatedType = typeInfo.Inflate(buildData, child);

                        // Find any typedef which introduced this type (eg. 'typedef Vector3Base<float> Float3')
                        result = FindTypedef(buildData, inflatedType, parent.ParentModule);
                        if (result == TypedefInfo.Current)
                        {
                            result = child; // Use template type for the current typedef
                        }
                        else if (result == null)
                        {
                            continue; // Invalid template match
                        }
                    }

                    result.EnsureInited(buildData);
                    if (result is TypedefInfo typedef)
                    {
                        result = typedef.Typedef;
                    }
                    break;
                }

                result = FindApiTypeInfoInner(buildData, typeInfo, child);
                if (result != null)
                {
                    break;
                }
            }
            return(result);
        }
Ejemplo n.º 8
0
        private static ApiTypeInfo FindTypedef(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent)
        {
            ApiTypeInfo result = null;

            foreach (var child in parent.Children)
            {
                if (child is TypedefInfo typedef && typedef.Type.Equals(typeInfo))
                {
                    result = child;
                    break;
                }

                result = FindTypedef(buildData, typeInfo, child);
                if (result != null)
                {
                    break;
                }
            }
            return(result);
        }
Ejemplo n.º 9
0
 /// <summary>
 /// Inflates the type with typedefs for generic arguments.
 /// </summary>
 public TypeInfo Inflate(Builder.BuildData buildData, ApiTypeInfo caller)
 {
     if (GenericArgs != null && GenericArgs.Count != 0)
     {
         var inflated = new TypeInfo(this);
         for (int i = 0; i < inflated.GenericArgs.Count; i++)
         {
             var arg     = new TypeInfo(inflated.GenericArgs[i]);
             var argType = BindingsGenerator.FindApiTypeInfo(buildData, arg, caller);
             if (argType != null)
             {
                 arg.Type        = argType.Name;
                 arg.GenericArgs = null;
             }
             inflated.GenericArgs[i] = arg;
         }
         return(inflated);
     }
     return(this);
 }
Ejemplo n.º 10
0
        public string GetFullNameNative(Builder.BuildData buildData, ApiTypeInfo caller)
        {
            var type = BindingsGenerator.FindApiTypeInfo(buildData, this, caller);

            if (type == null)
            {
                return(ToString());
            }

            var sb = new StringBuilder(64);

            if (IsConst)
            {
                sb.Append("const ");
            }
            sb.Append(type.FullNameNative);
            if (GenericArgs != null)
            {
                sb.Append('<');
                for (var i = 0; i < GenericArgs.Count; i++)
                {
                    if (i != 0)
                    {
                        sb.Append(", ");
                    }
                    sb.Append(GenericArgs[i]);
                }
                sb.Append('>');
            }
            if (IsPtr)
            {
                sb.Append('*');
            }
            if (IsRef)
            {
                sb.Append('&');
            }
            return(sb.ToString());
        }
Ejemplo n.º 11
0
 public void LeaveScope()
 {
     ScopeTypeStack.Pop();
     ScopeInfo          = ScopeTypeStack.Peek();
     CurrentAccessLevel = ScopeAccessStack.Pop();
 }
Ejemplo n.º 12
0
 public void EnterScope(ApiTypeInfo type)
 {
     ScopeAccessStack.Push(CurrentAccessLevel);
     ScopeTypeStack.Push(type);
     ScopeInfo = ScopeTypeStack.Peek();
 }
Ejemplo n.º 13
0
        private static ModuleInfo ParseModuleInner(BuildData buildData, Module module, BuildOptions moduleOptions = null)
        {
            if (buildData.ModulesInfo.TryGetValue(module, out var moduleInfo))
            {
                return(moduleInfo);
            }

            // Setup bindings module info descriptor
            moduleInfo = new ModuleInfo
            {
                Module    = module,
                Name      = module.BinaryModuleName,
                Namespace = string.Empty,
                Children  = new List <ApiTypeInfo>(),
            };
            if (string.IsNullOrEmpty(moduleInfo.Name))
            {
                throw new Exception("Module name cannot be empty.");
            }
            buildData.ModulesInfo.Add(module, moduleInfo);

            // Skip for modules that cannot have API bindings
            if (module is ThirdPartyModule || !module.BuildNativeCode)
            {
                return(moduleInfo);
            }

            if (moduleOptions == null)
            {
                throw new Exception($"Cannot parse module {module.Name} without options.");
            }

            // Collect all header files that can have public symbols for API
            var headerFiles = moduleOptions.SourceFiles.Where(x => x.EndsWith(".h")).ToList();

            // Skip if no header files to process
            if (headerFiles.Count == 0)
            {
                return(moduleInfo);
            }

            // Find and load files with API tags
            string[] headerFilesContents = null;
            //using (new ProfileEventScope("LoadHeaderFiles"))
            {
                var anyApi = false;
                for (int i = 0; i < headerFiles.Count; i++)
                {
                    // Skip scripting types definitions file
                    if (headerFiles[i].Replace('\\', '/').EndsWith("Engine/Core/Config.h", StringComparison.Ordinal) ||
                        headerFiles[i].EndsWith("EditorContextAPI.h", StringComparison.Ordinal))
                    {
                        continue;
                    }

                    // Check if file contains any valid API tag
                    var contents = File.ReadAllText(headerFiles[i]);
                    for (int j = 0; j < ApiTokens.SearchTags.Length; j++)
                    {
                        if (contents.Contains(ApiTokens.SearchTags[j]))
                        {
                            if (headerFilesContents == null)
                            {
                                headerFilesContents = new string[headerFiles.Count];
                            }
                            headerFilesContents[i] = contents;
                            anyApi = true;
                            break;
                        }
                    }
                }

                if (!anyApi)
                {
                    return(moduleInfo);
                }
            }

            // Skip if none of the headers was modified after last time generated C++ file was edited
            // TODO: skip parsing if module has API cache file -> then load it from disk

            /*if (!forceGenerate)
             * {
             *  var lastGenerateTime = File.GetLastWriteTime(bindings.GeneratedCppFilePath);
             *  var anyModified = false;
             *  for (int i = 0; i < headerFiles.Count; i++)
             *  {
             *      if (File.GetLastWriteTime(headerFiles[i]) > lastGenerateTime)
             *      {
             *          anyModified = true;
             *          break;
             *      }
             *  }
             *
             *  if (!anyModified)
             *      return;
             * }*/

            Log.Verbose($"Parsing API bindings for {module.Name} ({moduleInfo.Name})");

            // Process all header files to generate the module API code reflection
            var context = new ParsingContext
            {
                CurrentAccessLevel  = AccessLevel.Public,
                ScopeTypeStack      = new Stack <ApiTypeInfo>(),
                ScopeAccessStack    = new Stack <AccessLevel>(),
                PreprocessorDefines = new Dictionary <string, string>(),
            };

            for (int i = 0; i < headerFiles.Count; i++)
            {
                if (headerFilesContents[i] == null)
                {
                    continue;
                }
                var fileInfo = new FileInfo
                {
                    Parent    = null,
                    Children  = new List <ApiTypeInfo>(),
                    Name      = headerFiles[i],
                    Namespace = moduleInfo.Name,
                };
                moduleInfo.AddChild(fileInfo);

                try
                {
                    // Tokenize the source
                    var tokenizer = new Tokenizer();
                    tokenizer.Tokenize(headerFilesContents[i]);

                    // Init the context
                    context.Tokenizer = tokenizer;
                    context.File      = fileInfo;
                    context.ScopeInfo = null;
                    context.ScopeTypeStack.Clear();
                    context.ScopeAccessStack.Clear();
                    context.PreprocessorDefines.Clear();
                    context.EnterScope(fileInfo);

                    // Process the source code
                    ApiTypeInfo scopeType = null;
                    Token       prevToken = null;
                    while (true)
                    {
                        // Move to the next token
                        var token = tokenizer.NextToken();
                        if (token == null)
                        {
                            continue;
                        }
                        if (token.Type == TokenType.EndOfFile)
                        {
                            break;
                        }

                        // Parse API_.. tags in source code
                        if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal))
                        {
                            if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal))
                            {
                                if (!(context.ScopeInfo is FileInfo))
                                {
                                    throw new NotImplementedException("TODO: add support for nested classes in scripting API");
                                }

                                var classInfo = ParseClass(ref context);
                                scopeType = classInfo;
                                context.ScopeInfo.AddChild(scopeType);
                                context.CurrentAccessLevel = AccessLevel.Public;
                            }
                            else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal))
                            {
                                var propertyInfo = ParseProperty(ref context);
                            }
                            else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal))
                            {
                                var functionInfo = ParseFunction(ref context);

                                if (context.ScopeInfo is ClassInfo classInfo)
                                {
                                    classInfo.Functions.Add(functionInfo);
                                }
                                else if (context.ScopeInfo is StructureInfo structureInfo)
                                {
                                    structureInfo.Functions.Add(functionInfo);
                                }
                                else
                                {
                                    throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
                                }
                            }
                            else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal))
                            {
                                var enumInfo = ParseEnum(ref context);
                                context.ScopeInfo.AddChild(enumInfo);
                            }
                            else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal))
                            {
                                var structureInfo = ParseStructure(ref context);
                                scopeType = structureInfo;
                                context.ScopeInfo.AddChild(scopeType);
                                context.CurrentAccessLevel = AccessLevel.Public;
                            }
                            else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal))
                            {
                                var fieldInfo = ParseField(ref context);
                                var scopeInfo = context.ValidScopeInfoFromStack;

                                if (scopeInfo is ClassInfo classInfo)
                                {
                                    classInfo.Fields.Add(fieldInfo);
                                }
                                else if (scopeInfo is StructureInfo structureInfo)
                                {
                                    structureInfo.Fields.Add(fieldInfo);
                                }
                                else
                                {
                                    throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it.");
                                }
                            }
                            else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal))
                            {
                                var eventInfo = ParseEvent(ref context);
                                var scopeInfo = context.ValidScopeInfoFromStack;

                                if (scopeInfo is ClassInfo classInfo)
                                {
                                    classInfo.Events.Add(eventInfo);
                                }
                                else
                                {
                                    throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
                                }
                            }
                            else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal))
                            {
                                var injectCppCodeInfo = ParseInjectCppCode(ref context);
                                fileInfo.AddChild(injectCppCodeInfo);
                            }
                            else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal))
                            {
                                if (context.ScopeInfo is ClassInfo classInfo)
                                {
                                    classInfo.IsAutoSerialization = true;
                                }
                                else if (context.ScopeInfo is StructureInfo structureInfo)
                                {
                                    structureInfo.IsAutoSerialization = true;
                                }
                                else
                                {
                                    throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings.");
                                }
                            }
                        }

                        // Track access level inside class
                        if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier)
                        {
                            if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal))
                            {
                                context.CurrentAccessLevel = AccessLevel.Public;
                            }
                            else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal))
                            {
                                context.CurrentAccessLevel = AccessLevel.Protected;
                            }
                            else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal))
                            {
                                context.CurrentAccessLevel = AccessLevel.Private;
                            }
                        }

                        // Handle preprocessor blocks
                        if (token.Type == TokenType.Preprocessor)
                        {
                            token = tokenizer.NextToken();
                            switch (token.Value)
                            {
                            case "define":
                            {
                                token = tokenizer.NextToken();
                                var name  = token.Value;
                                var value = string.Empty;
                                token = tokenizer.NextToken(true);
                                while (token.Type != TokenType.Newline)
                                {
                                    value += token.Value;
                                    token  = tokenizer.NextToken(true);
                                }
                                value = value.Trim();
                                context.PreprocessorDefines[name] = value;
                                break;
                            }

                            case "if":
                            {
                                // Parse condition
                                var condition = string.Empty;
                                token = tokenizer.NextToken(true);
                                while (token.Type != TokenType.Newline)
                                {
                                    condition += token.Value;
                                    token      = tokenizer.NextToken(true);
                                }

                                // Replace contents with defines
                                condition = condition.Trim();
                                condition = ReplacePreProcessorDefines(condition, context.PreprocessorDefines.Keys);
                                condition = ReplacePreProcessorDefines(condition, moduleOptions.PublicDefinitions);
                                condition = ReplacePreProcessorDefines(condition, moduleOptions.PrivateDefinitions);
                                condition = ReplacePreProcessorDefines(condition, moduleOptions.CompileEnv.PreprocessorDefinitions);
                                condition = condition.Replace("false", "0");
                                condition = condition.Replace("true", "1");

                                // Check condition
                                // TODO: support expressions in preprocessor defines in API headers?
                                if (condition != "1")
                                {
                                    // Skip chunk of code
                                    ParsePreprocessorIf(fileInfo, tokenizer, ref token);
                                }

                                break;
                            }

                            case "ifdef":
                            {
                                // Parse condition
                                var define = string.Empty;
                                token = tokenizer.NextToken(true);
                                while (token.Type != TokenType.Newline)
                                {
                                    define += token.Value;
                                    token   = tokenizer.NextToken(true);
                                }

                                // Check condition
                                define = define.Trim();
                                if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define))
                                {
                                    ParsePreprocessorIf(fileInfo, tokenizer, ref token);
                                }

                                break;
                            }
                            }
                        }

                        // Scope tracking
                        if (token.Type == TokenType.LeftCurlyBrace)
                        {
                            context.ScopeTypeStack.Push(scopeType);
                            context.ScopeInfo = context.ScopeTypeStack.Peek();
                            scopeType         = null;
                        }
                        else if (token.Type == TokenType.RightCurlyBrace)
                        {
                            context.ScopeTypeStack.Pop();
                            if (context.ScopeTypeStack.Count == 0)
                            {
                                throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}.");
                            }
                            context.ScopeInfo = context.ScopeTypeStack.Peek();
                            if (context.ScopeInfo is FileInfo)
                            {
                                context.CurrentAccessLevel = AccessLevel.Public;
                            }
                        }

                        prevToken = token;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings.");
                    Log.Exception(ex);
                    throw;
                }
            }

            // Initialize API
            moduleInfo.Init(buildData);

            return(moduleInfo);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Finds the API type information.
        /// </summary>
        /// <param name="buildData">The build data.</param>
        /// <param name="typeInfo">The type information.</param>
        /// <param name="caller">The calling type. It's parent module types and references are used to find the given API type.</param>
        /// <returns>The found API type inf oor null.</returns>
        public static ApiTypeInfo FindApiTypeInfo(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
        {
            if (typeInfo == null)
            {
                return(null);
            }
            var result = FindApiTypeInfoInner(typeInfo, caller);

            if (result != null)
            {
                return(result);
            }
            if (buildData.TypeCache.TryGetValue(typeInfo, out result))
            {
                return(result);
            }

            // Find across in-build types
            if (InBuildTypes.TryGetValue(typeInfo, out result))
            {
                return(result);
            }

            // Find across all loaded modules for this build
            foreach (var e in buildData.ModulesInfo)
            {
                result = FindApiTypeInfoInner(typeInfo, e.Value);
                if (result != null)
                {
                    buildData.TypeCache.Add(typeInfo, result);
                    return(result);
                }
            }

            // Check if using nested typename
            if (typeInfo.Type.Contains("::"))
            {
                var nesting = typeInfo.Type.Split(new[] { "::" }, StringSplitOptions.None);
                result = FindApiTypeInfo(buildData, new TypeInfo {
                    Type = nesting[0],
                }, caller);
                for (int i = 1; i < nesting.Length; i++)
                {
                    if (result == null)
                    {
                        return(null);
                    }
                    result = FindApiTypeInfoInner(new TypeInfo {
                        Type = nesting[i],
                    }, result);
                }
                return(result);
            }

            //buildData.TypeCache.Add(typeInfo, null);
            //Log.Warning("Unknown API type: " + typeInfo);
            return(null);
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Checks if use reference when passing values of this type to via scripting API methods.
        /// </summary>
        /// <param name="buildData">The build data.</param>
        /// <param name="typeInfo">The value type information.</param>
        /// <param name="caller">The calling type. It's parent module types and references are used to find the given API type.</param>
        /// <returns>True if use reference when passing the values of this type, otherwise false.</returns>
        public static bool UsePassByReference(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
        {
            // Skip for pointers
            if (typeInfo.IsPtr)
            {
                return(false);
            }

            // Skip for strings
            if ((typeInfo.Type == "String" || typeInfo.Type == "StringView" || typeInfo.Type == "StringAnsi" || typeInfo.Type == "StringAnsiView") && typeInfo.GenericArgs == null)
            {
                return(false);
            }

            // Skip for collections
            if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null)
            {
                return(false);
            }

            // Skip for BytesContainer
            if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null)
            {
                return(false);
            }

            // Skip for Variant
            if (typeInfo.Type == "Variant" && typeInfo.GenericArgs == null)
            {
                return(false);
            }

            // Skip for VariantType
            if (typeInfo.Type == "VariantType" && typeInfo.GenericArgs == null)
            {
                return(false);
            }

            // Find API type info
            var apiType = FindApiTypeInfo(buildData, typeInfo, caller);

            if (apiType != null)
            {
                // Skip for scripting objects
                if (apiType.IsScriptingObject)
                {
                    return(false);
                }

                // Skip for classes
                if (apiType.IsClass)
                {
                    return(false);
                }

                // Force for structures
                if (apiType.IsStruct)
                {
                    return(true);
                }
            }

            // True for references
            if (typeInfo.IsRef)
            {
                return(true);
            }

            // Force for in-build types
            if (InBuildRefTypes.Contains(typeInfo.Type))
            {
                return(true);
            }

            return(false);
        }
Ejemplo n.º 16
0
 public virtual void AddChild(ApiTypeInfo apiTypeInfo)
 {
     apiTypeInfo.Parent = this;
     Children.Add(apiTypeInfo);
 }
Ejemplo n.º 17
0
        public override void Init(Builder.BuildData buildData)
        {
            base.Init(buildData);

            // Remove previous typedef (if any)
            if (Typedef == null)
            {
                foreach (var e in Parent.Children)
                {
                    if (e != this && e.Name == Name)
                    {
                        Typedef = e;
                        break;
                    }
                }
            }
            if (Typedef != null)
            {
                Typedef.Parent = null;
                Parent.Children.Remove(Typedef);
                Typedef = null;
            }

            // Find typedef type
            var current = Current;

            Current = this;
            var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, Type, Parent);

            Current = current;
            if (apiTypeInfo == null)
            {
                throw new Exception(string.Format("Unknown type '{0}' for typedef '{1}'.", Type, Name));
            }
            apiTypeInfo.EnsureInited(buildData);
            TypeInfo = apiTypeInfo;

            // Alias type without introducing any new type
            if (IsAlias || apiTypeInfo is LangType)
            {
                Typedef = apiTypeInfo;
                return;
            }

            try
            {
                // Duplicate type
                var typedef = (ApiTypeInfo)apiTypeInfo.Clone();
                typedef.Instigator = this;
                typedef.NativeName = NativeName ?? Name;
                typedef.Name       = Name;
                typedef.Namespace  = Namespace;
                if (!string.IsNullOrEmpty(Attributes))
                {
                    if (string.IsNullOrEmpty(typedef.Attributes))
                    {
                        typedef.Attributes = Attributes;
                    }
                    else
                    {
                        typedef.Attributes += ',' + Attributes;
                    }
                }
                if (Comment != null && Comment.Length != 0)
                {
                    typedef.Comment = Comment;
                }
                typedef.IsInBuild    |= IsInBuild;
                typedef.IsDeprecated |= IsDeprecated;
                if (typedef is ClassStructInfo typedefClassStruct && typedefClassStruct.IsTemplate)
                {
                    // Inflate template type
                    typedefClassStruct.IsTemplate = false;
                    if (typedefClassStruct is ClassInfo typedefClass)
                    {
                        foreach (var fieldInfo in typedefClass.Fields)
                        {
                            InflateType(buildData, typedefClassStruct, ref fieldInfo.Type);
                        }
                    }
                    else if (typedefClassStruct is StructureInfo typedefStruct)
                    {
                        foreach (var fieldInfo in typedefStruct.Fields)
                        {
                            InflateType(buildData, typedefStruct, ref fieldInfo.Type);
                        }
                    }
                }

                // Add to the hierarchy
                typedef.Parent = Parent;
                Parent.Children.Add(typedef);
                typedef.EnsureInited(buildData);
                Typedef = typedef;
            }
            catch (Exception)
            {
                Log.Error($"Failed to typedef '{Type}' as '{Name}'.");
                throw;
            }
        }
Ejemplo n.º 18
0
        private static void ParseModuleInnerAsync(ModuleInfo moduleInfo, BuildOptions moduleOptions, List <string> headerFiles, int workIndex)
        {
            // Find and load files with API tags
            bool   hasApi             = false;
            string headerFileContents = File.ReadAllText(headerFiles[workIndex]);

            for (int j = 0; j < ApiTokens.SearchTags.Length; j++)
            {
                if (headerFileContents.Contains(ApiTokens.SearchTags[j]))
                {
                    hasApi = true;
                    break;
                }
            }
            if (!hasApi)
            {
                return;
            }

            // Process header file to generate the module API code reflection
            var fileInfo = new FileInfo
            {
                Parent    = null,
                Name      = headerFiles[workIndex],
                Namespace = moduleInfo.Name,
            };

            lock (moduleInfo)
            {
                moduleInfo.AddChild(fileInfo);
            }

            try
            {
                // Tokenize the source
                var tokenizer = new Tokenizer();
                tokenizer.Tokenize(headerFileContents);

                // Init the context
                var context = new ParsingContext
                {
                    File                = fileInfo,
                    Tokenizer           = tokenizer,
                    ScopeInfo           = null,
                    CurrentAccessLevel  = AccessLevel.Public,
                    ScopeTypeStack      = new Stack <ApiTypeInfo>(),
                    ScopeAccessStack    = new Stack <AccessLevel>(),
                    PreprocessorDefines = new Dictionary <string, string>(),
                };
                context.PreprocessorDefines.Add("FLAX_BUILD_BINDINGS", "1");
                context.EnterScope(fileInfo);

                // Process the source code
                ApiTypeInfo scopeType = null;
                Token       prevToken = null;
                while (true)
                {
                    // Move to the next token
                    var token = tokenizer.NextToken();
                    if (token == null)
                    {
                        continue;
                    }
                    if (token.Type == TokenType.EndOfFile)
                    {
                        break;
                    }

                    // Parse API_.. tags in source code
                    if (token.Type == TokenType.Identifier && token.Value.StartsWith("API_", StringComparison.Ordinal))
                    {
                        if (string.Equals(token.Value, ApiTokens.Class, StringComparison.Ordinal))
                        {
                            if (!(context.ScopeInfo is FileInfo))
                            {
                                throw new NotImplementedException("TODO: add support for nested classes in scripting API");
                            }

                            var classInfo = ParseClass(ref context);
                            scopeType = classInfo;
                            context.ScopeInfo.AddChild(scopeType);
                            context.CurrentAccessLevel = AccessLevel.Public;
                        }
                        else if (string.Equals(token.Value, ApiTokens.Property, StringComparison.Ordinal))
                        {
                            var propertyInfo = ParseProperty(ref context);
                        }
                        else if (string.Equals(token.Value, ApiTokens.Function, StringComparison.Ordinal))
                        {
                            var functionInfo = ParseFunction(ref context);

                            if (context.ScopeInfo is ClassInfo classInfo)
                            {
                                classInfo.Functions.Add(functionInfo);
                            }
                            else if (context.ScopeInfo is StructureInfo structureInfo)
                            {
                                structureInfo.Functions.Add(functionInfo);
                            }
                            else if (context.ScopeInfo is InterfaceInfo interfaceInfo)
                            {
                                interfaceInfo.Functions.Add(functionInfo);
                            }
                            else
                            {
                                throw new Exception($"Not supported free-function {functionInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
                            }
                        }
                        else if (string.Equals(token.Value, ApiTokens.Enum, StringComparison.Ordinal))
                        {
                            var enumInfo = ParseEnum(ref context);
                            context.ScopeInfo.AddChild(enumInfo);
                        }
                        else if (string.Equals(token.Value, ApiTokens.Struct, StringComparison.Ordinal))
                        {
                            var structureInfo = ParseStructure(ref context);
                            scopeType = structureInfo;
                            context.ScopeInfo.AddChild(scopeType);
                            context.CurrentAccessLevel = AccessLevel.Public;
                        }
                        else if (string.Equals(token.Value, ApiTokens.Field, StringComparison.Ordinal))
                        {
                            var fieldInfo = ParseField(ref context);
                            var scopeInfo = context.ValidScopeInfoFromStack;

                            if (scopeInfo is ClassInfo classInfo)
                            {
                                classInfo.Fields.Add(fieldInfo);
                            }
                            else if (scopeInfo is StructureInfo structureInfo)
                            {
                                structureInfo.Fields.Add(fieldInfo);
                            }
                            else
                            {
                                throw new Exception($"Not supported location for field {fieldInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class or structure to use API bindings for it.");
                            }
                        }
                        else if (string.Equals(token.Value, ApiTokens.Event, StringComparison.Ordinal))
                        {
                            var eventInfo = ParseEvent(ref context);
                            var scopeInfo = context.ValidScopeInfoFromStack;

                            if (scopeInfo is ClassInfo classInfo)
                            {
                                classInfo.Events.Add(eventInfo);
                            }
                            else
                            {
                                throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
                            }
                        }
                        else if (string.Equals(token.Value, ApiTokens.Typedef, StringComparison.Ordinal))
                        {
                            var typeInfo = ParseTypedef(ref context);
                            fileInfo.AddChild(typeInfo);
                        }
                        else if (string.Equals(token.Value, ApiTokens.InjectCode, StringComparison.Ordinal))
                        {
                            var injectCodeInfo = ParseInjectCode(ref context);
                            fileInfo.AddChild(injectCodeInfo);
                        }
                        else if (string.Equals(token.Value, ApiTokens.Interface, StringComparison.Ordinal))
                        {
                            if (!(context.ScopeInfo is FileInfo))
                            {
                                throw new NotImplementedException("TODO: add support for nested interfaces in scripting API");
                            }

                            var interfaceInfo = ParseInterface(ref context);
                            scopeType = interfaceInfo;
                            context.ScopeInfo.AddChild(scopeType);
                            context.CurrentAccessLevel = AccessLevel.Public;
                        }
                        else if (string.Equals(token.Value, ApiTokens.AutoSerialization, StringComparison.Ordinal))
                        {
                            if (context.ScopeInfo is ClassInfo classInfo)
                            {
                                classInfo.IsAutoSerialization = true;
                            }
                            else if (context.ScopeInfo is StructureInfo structureInfo)
                            {
                                structureInfo.IsAutoSerialization = true;
                            }
                            else
                            {
                                throw new Exception($"Not supported location for {ApiTokens.AutoSerialization} at line {tokenizer.CurrentLine}. Place it in the class or structure that uses API bindings.");
                            }
                        }
                    }

                    // Track access level inside class
                    if (context.ScopeInfo != null && token.Type == TokenType.Colon && prevToken != null && prevToken.Type == TokenType.Identifier)
                    {
                        if (string.Equals(prevToken.Value, "public", StringComparison.Ordinal))
                        {
                            context.CurrentAccessLevel = AccessLevel.Public;
                        }
                        else if (string.Equals(prevToken.Value, "protected", StringComparison.Ordinal))
                        {
                            context.CurrentAccessLevel = AccessLevel.Protected;
                        }
                        else if (string.Equals(prevToken.Value, "private", StringComparison.Ordinal))
                        {
                            context.CurrentAccessLevel = AccessLevel.Private;
                        }
                    }

                    // Handle preprocessor blocks
                    if (token.Type == TokenType.Preprocessor)
                    {
                        token = tokenizer.NextToken();
                        switch (token.Value)
                        {
                        case "define":
                        {
                            token = tokenizer.NextToken();
                            var name  = token.Value;
                            var value = string.Empty;
                            token = tokenizer.NextToken(true);
                            while (token.Type != TokenType.Newline)
                            {
                                value += token.Value;
                                token  = tokenizer.NextToken(true);
                            }
                            value = value.Trim();
                            context.PreprocessorDefines[name] = value;
                            break;
                        }

                        case "if":
                        case "elif":
                        {
                            // Parse condition
                            var condition = string.Empty;
                            token = tokenizer.NextToken(true);
                            while (token.Type != TokenType.Newline)
                            {
                                var tokenValue = token.Value.Trim();
                                if (tokenValue.Length == 0)
                                {
                                    token = tokenizer.NextToken(true);
                                    continue;
                                }

                                // Very simple defines processing
                                var negate = tokenValue[0] == '!';
                                if (negate)
                                {
                                    tokenValue = tokenValue.Substring(1);
                                }
                                tokenValue = ReplacePreProcessorDefines(tokenValue, context.PreprocessorDefines);
                                tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PublicDefinitions);
                                tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.PrivateDefinitions);
                                tokenValue = ReplacePreProcessorDefines(tokenValue, moduleOptions.CompileEnv.PreprocessorDefinitions);
                                tokenValue = tokenValue.Replace("false", "0");
                                tokenValue = tokenValue.Replace("true", "1");
                                tokenValue = tokenValue.Replace("||", "|");
                                if (tokenValue.Length != 0 && tokenValue != "1" && tokenValue != "0" && tokenValue != "|")
                                {
                                    tokenValue = "0";
                                }
                                if (negate)
                                {
                                    tokenValue = tokenValue == "1" ? "0" : "1";
                                }

                                condition += tokenValue;
                                token      = tokenizer.NextToken(true);
                            }

                            // Filter condition
                            bool modified;
                            do
                            {
                                modified = false;
                                if (condition.Contains("1|1"))
                                {
                                    condition = condition.Replace("1|1", "1");
                                    modified  = true;
                                }
                                if (condition.Contains("1|0"))
                                {
                                    condition = condition.Replace("1|0", "1");
                                    modified  = true;
                                }
                                if (condition.Contains("0|1"))
                                {
                                    condition = condition.Replace("0|1", "1");
                                    modified  = true;
                                }
                            } while (modified);

                            // Skip chunk of code of condition fails
                            if (condition != "1")
                            {
                                ParsePreprocessorIf(fileInfo, tokenizer, ref token);
                            }

                            break;
                        }

                        case "ifdef":
                        {
                            // Parse condition
                            var define = string.Empty;
                            token = tokenizer.NextToken(true);
                            while (token.Type != TokenType.Newline)
                            {
                                define += token.Value;
                                token   = tokenizer.NextToken(true);
                            }

                            // Check condition
                            define = define.Trim();
                            if (!context.PreprocessorDefines.ContainsKey(define) && !moduleOptions.CompileEnv.PreprocessorDefinitions.Contains(define))
                            {
                                ParsePreprocessorIf(fileInfo, tokenizer, ref token);
                            }

                            break;
                        }
                        }
                    }

                    // Scope tracking
                    if (token.Type == TokenType.LeftCurlyBrace)
                    {
                        context.ScopeTypeStack.Push(scopeType);
                        context.ScopeInfo = context.ScopeTypeStack.Peek();
                        scopeType         = null;
                    }
                    else if (token.Type == TokenType.RightCurlyBrace)
                    {
                        context.ScopeTypeStack.Pop();
                        if (context.ScopeTypeStack.Count == 0)
                        {
                            throw new Exception($"Mismatch of the {{}} braces pair in file '{fileInfo.Name}' at line {tokenizer.CurrentLine}.");
                        }
                        context.ScopeInfo = context.ScopeTypeStack.Peek();
                        if (context.ScopeInfo is FileInfo)
                        {
                            context.CurrentAccessLevel = AccessLevel.Public;
                        }
                    }

                    prevToken = token;
                }
            }
            catch (Exception ex)
            {
                Log.Error($"Failed to parse '{fileInfo.Name}' file to generate bindings.");
                Log.Exception(ex);
                throw;
            }
        }
Ejemplo n.º 19
0
 public override void AddChild(ApiTypeInfo apiTypeInfo)
 {
     throw new NotSupportedException("API enums cannot have sub-types.");
 }
Ejemplo n.º 20
0
        public override void AddChild(ApiTypeInfo apiTypeInfo)
        {
            apiTypeInfo.Namespace = null;

            base.AddChild(apiTypeInfo);
        }
Ejemplo n.º 21
0
        private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent)
        {
            foreach (var child in parent.Children)
            {
                if (child.Name == typeInfo.Type)
                {
                    child.EnsureInited(buildData);
                    return(child);
                }

                var result = FindApiTypeInfoInner(buildData, typeInfo, child);
                if (result != null)
                {
                    return(result);
                }
            }

            return(null);
        }