Exemple #1
0
        // Parse a block of C++ source code, adding any types found
        public void AddFromDeclarationText(string text)
        {
            using StringReader lines = new StringReader(text);

            var rgxForwardDecl  = new Regex(@"(struct|union)\s+(\S+);");
            var rgxTypedefAlias = new Regex(@"typedef\s+(?:(struct|union)\s+)?(\S+)\s+(\S+);");
            var rgxTypedefFnPtr = new Regex(@"typedef\s+(?:struct\s+)?" + CppFnPtrType.Regex + ";");
            var rgxDefinition   = new Regex(@"^(typedef\s+)?(struct|union|enum)");
            var rgxFieldFnPtr   = new Regex(CppFnPtrType.Regex + @";");
            var rgxField        = new Regex(@"^(?:struct\s+|enum\s+)?(\S+?\s*\**)\s*((?:\S|\s*,\s*)+)(?:\s*:\s*([0-9]+))?;");
            var rgxEnumValue    = new Regex(@"^\s*([A-Za-z0-9_]+)(?:\s*=\s*(.+?))?,?\s*$");
            var rgxIsConst      = new Regex(@"\bconst\b");

            var rgxStripKeywords = new Regex(@"\b(?:const|unsigned|volatile)\b");
            var rgxCompressPtrs  = new Regex(@"\*\s+\*");

            var rgxArrayField = new Regex(@"(\S+?)\[([0-9]+)\]");

            var rgxAlignment         = new Regex(@"__attribute__\(\(aligned\(([0-9]+)\)\)\)\s+");
            var rgxIsBitDirective    = new Regex(@"#ifdef\s+IS_(32|64)BIT");
            var rgxSingleLineComment = new Regex(@"/\*.*?\*/");

            var    currentType = new Stack <CppComplexType>();
            bool   falseIfBlock = false;
            bool   inComment = false;
            bool   inMethod = false;
            bool   inTypedef = false;
            var    nextEnumValue = 0ul;
            string rawLine, line;

            while ((rawLine = line = lines.ReadLine()) != null)
            {
                // Remove comments
                if (line.Contains("//"))
                {
                    line = line.Substring(0, line.IndexOf("//", StringComparison.Ordinal));
                }

                // End of multi-line comment?
                if (line.Contains("*/") && inComment)
                {
                    inComment = false;
                    line      = line.Substring(line.IndexOf("*/", StringComparison.Ordinal) + 2);
                }

                if (inComment)
                {
                    Debug.WriteLine($"[COMMENT      ] {line}");
                    continue;
                }

                // Remove all single-line comments
                line = rgxSingleLineComment.Replace(line, "");

                // Start of multi-line comment?
                if (line.Contains("/*") && !inComment)
                {
                    inComment = true;
                    line      = line.Substring(0, line.IndexOf("/*"));
                }

                // Ignore global variables
                if (line.StartsWith("const ") && currentType.Count == 0)
                {
                    Debug.WriteLine($"[GLOBAL       ] {line}");
                    continue;
                }

                // Ignore methods
                // Note: This is a very lazy way of processing early version IL2CPP headers
                if (line != "}" && inMethod)
                {
                    Debug.WriteLine($"[METHOD       ] {line}");
                    continue;
                }

                if (line == "}" && inMethod)
                {
                    inMethod = false;

                    Debug.WriteLine($"[METHOD END   ] {line}");
                    continue;
                }

                if (line.StartsWith("static inline "))
                {
                    inMethod = true;

                    Debug.WriteLine($"[METHOD START ] {line}");
                    continue;
                }

                // Remove keywords we don't care about
                line = rgxStripKeywords.Replace(line, "");

                // Remove whitespace in multiple indirections
                line = rgxCompressPtrs.Replace(line, "**");

                // Process __attribute((aligned(x)))
                var alignment      = 0;
                var alignmentMatch = rgxAlignment.Match(line);
                if (alignmentMatch.Success)
                {
                    alignment = int.Parse(alignmentMatch.Groups[1].Captures[0].ToString());
                    line      = rgxAlignment.Replace(line, "");
                }

                line = line.Trim();

                // Ignore blank lines
                if (line.Length == 0)
                {
                    continue;
                }

                // Ignore defines
                if (line.StartsWith("#define"))
                {
                    continue;
                }

                // Process #ifdefs before anything else
                // Doesn't handle nesting but we probably don't need to (use a Stack if we do)
                var ifdef = rgxIsBitDirective.Match(line);
                if (ifdef.Success)
                {
                    var bits = int.Parse(ifdef.Groups[1].Captures[0].ToString());
                    if (bits != WordSize)
                    {
                        falseIfBlock = true;
                    }

                    Debug.WriteLine($"[IF           ] {line}");
                    continue;
                }
                // Other #ifdef
                if (line.StartsWith("#ifdef") || line.StartsWith("#if "))
                {
                    falseIfBlock = true;

                    Debug.WriteLine($"[IF           ] {line}");
                    continue;
                }
                if (line.StartsWith("#ifndef"))
                {
                    falseIfBlock = false;

                    Debug.WriteLine($"[IF           ] {line}");
                    continue;
                }
                if (line == "#else")
                {
                    falseIfBlock = !falseIfBlock;

                    Debug.WriteLine($"[ELSE         ] {line}");
                    continue;
                }
                if (line == "#endif")
                {
                    falseIfBlock = false;

                    Debug.WriteLine($"[ENDIF        ] {line}");
                    continue;
                }

                if (falseIfBlock)
                {
                    Debug.WriteLine($"[FALSE        ] {line}");
                    continue;
                }

                // Forward declaration
                // <struct|union> <external-type>;
                var externDecl = rgxForwardDecl.Match(line);
                if (externDecl.Success)
                {
                    var complexType = complexTypeMap[externDecl.Groups[1].Captures[0].ToString()];
                    var declType    = externDecl.Groups[2].Captures[0].ToString();

                    switch (complexType)
                    {
                    case ComplexValueType.Struct: Struct(declType); break;

                    case ComplexValueType.Union: Union(declType); break;
                    }

                    Debug.WriteLine($"[FORWARD DECL ] {line}");
                    continue;
                }

                // Typedef alias
                // typedef <struct|union> <existing-type> <alias>
                var typedef = rgxTypedefAlias.Match(line);
                if (typedef.Success)
                {
                    //var complexType = complexTypeMap[typedef.Groups[1].Captures[0].ToString()];
                    var existingType = typedef.Groups[2].Captures[0].ToString();
                    var alias        = typedef.Groups[3].Captures[0].ToString();

                    // Sometimes we might get multiple forward declarations for the same type
                    // Potential multiple indirection
                    var type = GetType(existingType);

                    // C++ allows the same typedef to be defined more than once
                    TypedefAliases.TryAdd(alias, type);

                    var pointers = line.Count(c => c == '*');
                    Debug.WriteLine($"[TYPEDEF {(pointers > 0 ? "PTR" : "VAL")}  ] {line}  --  Adding typedef from {type.Name} to {alias}");
                    continue;
                }

                // Function pointer alias
                // typedef <retType> (*<alias>)(<args>);
                typedef = rgxTypedefFnPtr.Match(line);
                if (typedef.Success)
                {
                    var alias = typedef.Groups[2].Captures[0].ToString();

                    var fnPtrType = CppFnPtrType.FromSignature(this, line);
                    fnPtrType.Group = currentGroup;

                    TypedefAliases.Add(alias, fnPtrType);

                    Debug.WriteLine($"[TYPEDEF FNPTR] {line}  --  Adding method pointer typedef to {alias}");
                    continue;
                }

                // Start of struct/union/enum
                // [typedef] <struct|union|enum> [optional-tag-name]
                var definition = rgxDefinition.Match(line);
                if (definition.Success && line.IndexOf(";", StringComparison.Ordinal) == -1 && currentType.Count == 0)
                {
                    // Must have a name if not a typedef, might have a name if it is
                    var split = line.Split(' ');

                    if (split[0] == "typedef")
                    {
                        split = split.Skip(1).ToArray();
                    }

                    var name = split.Length > 1 && split[1] != "{" ? split[1] : "";

                    currentType.Push(complexTypeMap[split[0]] switch {
                        ComplexValueType.Struct => Struct(name),
                        ComplexValueType.Union => Union(name),
                        ComplexValueType.Enum => NewDefaultEnum(name),
                        _ => throw new InvalidOperationException("Unknown complex type")
                    });
Exemple #2
0
        // Parse a block of C++ source code, adding any types found
        public void AddFromDeclarationText(string text)
        {
            using StringReader lines = new StringReader(text);

            var rgxExternDecl         = new Regex(@"struct (\S+);");
            var rgxTypedefForwardDecl = new Regex(@"typedef struct (\S+) (\S+);");
            var rgxTypedefFnPtr       = new Regex(@"typedef\s+(?:struct )?" + CppFnPtrType.Regex + ";");
            var rgxTypedef            = new Regex(@"typedef (\S+?)\s*\**\s*(\S+);");
            var rgxFieldFnPtr         = new Regex(CppFnPtrType.Regex + @";");
            var rgxField     = new Regex(@"^(?:struct |enum )?(\S+?)\s*\**\s*((?:\S|\s*,\s*)+)(?:\s*:\s*([0-9]+))?;");
            var rgxEnumValue = new Regex(@"^\s*([A-Za-z0-9_]+)(?:\s*=\s*(.+?))?,?\s*$");

            var rgxStripKeywords = new Regex(@"\b(?:const|unsigned|volatile)\b");
            var rgxCompressPtrs  = new Regex(@"\*\s+\*");

            var rgxArrayField = new Regex(@"(\S+?)\[([0-9]+)\]");

            var rgxAlignment         = new Regex(@"__attribute__\(\(aligned\(([0-9]+)\)\)\)");
            var rgxIsBitDirective    = new Regex(@"#ifdef\s+IS_(32|64)BIT");
            var rgxSingleLineComment = new Regex(@"/\*.*?\*/");

            var    currentType   = new Stack <CppComplexType>();
            bool   falseIfBlock  = false;
            bool   inComment     = false;
            bool   inMethod      = false;
            var    nextEnumValue = 0ul;
            string line;

            while ((line = lines.ReadLine()) != null)
            {
                // Remove comments
                if (line.Contains("//"))
                {
                    line = line.Substring(0, line.IndexOf("//", StringComparison.Ordinal));
                }

                // End of multi-line comment?
                if (line.Contains("*/") && inComment)
                {
                    inComment = false;
                    line      = line.Substring(line.IndexOf("*/", StringComparison.Ordinal) + 2);
                }

                if (inComment)
                {
                    Debug.WriteLine($"[COMMENT      ] {line}");
                    continue;
                }

                // Remove all single-line comments
                line = rgxSingleLineComment.Replace(line, "");

                // Start of multi-line comment?
                if (line.Contains("/*") && !inComment)
                {
                    inComment = true;
                    line      = line.Substring(0, line.IndexOf("/*"));
                }

                // Ignore global variables
                if (line.StartsWith("const ") && currentType.Count == 0)
                {
                    Debug.WriteLine($"[GLOBAL       ] {line}");
                    continue;
                }

                // Ignore methods
                // Note: This is a very lazy way of processing early version IL2CPP headers
                if (line != "}" && inMethod)
                {
                    Debug.WriteLine($"[METHOD       ] {line}");
                    continue;
                }

                if (line == "}" && inMethod)
                {
                    inMethod = false;

                    Debug.WriteLine($"[METHOD END   ] {line}");
                    continue;
                }

                if (line.StartsWith("static inline "))
                {
                    inMethod = true;

                    Debug.WriteLine($"[METHOD START ] {line}");
                    continue;
                }

                // Remove keywords we don't care about
                line = rgxStripKeywords.Replace(line, "");

                // Remove whitespace in multiple indirections
                line = rgxCompressPtrs.Replace(line, "**");

                // Process __attribute((aligned(x)))
                var alignment      = 0;
                var alignmentMatch = rgxAlignment.Match(line);
                if (alignmentMatch.Success)
                {
                    alignment = int.Parse(alignmentMatch.Groups[1].Captures[0].ToString());
                    line      = rgxAlignment.Replace(line, "");
                }

                line = line.Trim();

                // Ignore blank lines
                if (line.Length == 0)
                {
                    continue;
                }

                // Process #ifs before anything else
                // Doesn't handle nesting but we probably don't need to (use a Stack if we do)
                var ifdef = rgxIsBitDirective.Match(line);
                if (ifdef.Success)
                {
                    var bits = int.Parse(ifdef.Groups[1].Captures[0].ToString());
                    if (bits != WordSize)
                    {
                        falseIfBlock = true;
                    }

                    Debug.WriteLine($"[IF           ] {line}");
                    continue;
                }
                if (line == "#else")
                {
                    falseIfBlock = !falseIfBlock;

                    Debug.WriteLine($"[ELSE         ] {line}");
                    continue;
                }
                if (line == "#endif")
                {
                    falseIfBlock = false;

                    Debug.WriteLine($"[ENDIF        ] {line}");
                    continue;
                }

                if (falseIfBlock)
                {
                    Debug.WriteLine($"[FALSE        ] {line}");
                    continue;
                }

                // External declaration
                // struct <external-type>;
                // NOTE: Unfortunately we're not going to ever know the size of this type
                var externDecl = rgxExternDecl.Match(line);
                if (externDecl.Success)
                {
                    var declType = externDecl.Groups[1].Captures[0].ToString();

                    Types.Add(declType, CppType.NewStruct(declType));

                    Debug.WriteLine($"[EXTERN DECL  ] {line}");
                    continue;
                }

                // Forward declaration
                // typedef struct <struct-type> <alias>
                var typedef = rgxTypedefForwardDecl.Match(line);
                if (typedef.Success)
                {
                    var alias    = typedef.Groups[2].Captures[0].ToString();
                    var declType = typedef.Groups[1].Captures[0].ToString();

                    // Sometimes we might get multiple forward declarations for the same type
                    if (!Types.ContainsKey(declType))
                    {
                        Types.Add(declType, CppType.NewStruct(declType));
                    }

                    // Sometimes the alias might be the same name as the type (this is usually the case)
                    if (!Types.ContainsKey(alias))
                    {
                        Types.Add(alias, Types[declType].AsAlias(alias));
                    }

                    Debug.WriteLine($"[FORWARD DECL ] {line}");
                    continue;
                }

                // Function pointer
                // typedef <retType> (*<alias>)(<args>);
                typedef = rgxTypedefFnPtr.Match(line);
                if (typedef.Success)
                {
                    var alias = typedef.Groups[2].Captures[0].ToString();

                    var fnPtrType = CppFnPtrType.FromSignature(this, line);
                    fnPtrType.Name = alias;

                    Types.Add(alias, fnPtrType);

                    Debug.WriteLine($"[TYPEDEF FNPTR] {line}  --  Adding method pointer typedef to {alias}");
                    continue;
                }

                // Alias
                // typedef <targetType>[*..] <alias>;
                typedef = rgxTypedef.Match(line);
                if (typedef.Success)
                {
                    var alias        = typedef.Groups[2].Captures[0].ToString();
                    var existingType = typedef.Groups[1].Captures[0].ToString();

                    // Potential multiple indirection
                    var type     = Types[existingType];
                    var pointers = line.Count(c => c == '*');
                    for (int i = 0; i < pointers; i++)
                    {
                        type = type.AsPointer(WordSize);
                    }

                    Types.Add(alias, type.AsAlias(alias));

                    Debug.WriteLine($"[TYPEDEF {(pointers > 0? "PTR":"VAL")}  ] {line}  --  Adding typedef from {type.Name} to {alias}");
                    continue;
                }

                // Start of struct
                // typedef struct <optional-type-name>
                if ((line.StartsWith("typedef struct") || line.StartsWith("struct ")) && line.IndexOf(";", StringComparison.Ordinal) == -1 &&
                    currentType.Count == 0)
                {
                    currentType.Push(CppType.NewStruct());

                    if (line.StartsWith("struct "))
                    {
                        currentType.Peek().Name = line.Split(' ')[1];
                    }

                    Debug.WriteLine($"\n[STRUCT START ] {line}");
                    continue;
                }

                // Start of union
                // typedef union <optional-type-name>
                if (line.StartsWith("typedef union") && line.IndexOf(";", StringComparison.Ordinal) == -1)
                {
                    currentType.Push(CppType.NewUnion());

                    Debug.WriteLine($"\n[UNION START  ] {line}");
                    continue;
                }

                // Start of enum
                // typedef enum <optional-type-name>
                if (line.StartsWith("typedef enum") && line.IndexOf(";", StringComparison.Ordinal) == -1)
                {
                    currentType.Push(NewDefaultEnum());
                    nextEnumValue = 0;

                    Debug.WriteLine($"\n[ENUM START   ] {line}");
                    continue;
                }

                // Nested complex field
                // struct <optional-type-name>
                // union <optional-type-name>
                var words = line.Split(' ');
                if ((words[0] == "union" || words[0] == "struct") && words.Length <= 2)
                {
                    currentType.Push(words[0] == "struct" ? CppType.NewStruct() : CppType.NewUnion());

                    Debug.WriteLine($"[FIELD START   ] {line}");
                    continue;
                }

                // End of already named struct
                if (line == "};" && currentType.Count == 1)
                {
                    var ct = currentType.Pop();
                    if (!Types.ContainsKey(ct.Name))
                    {
                        Types.Add(ct.Name, ct);
                    }
                    else
                    {
                        ((CppComplexType)Types[ct.Name]).Fields = ct.Fields;
                    }

                    Debug.WriteLine($"[STRUCT END   ] {line}  --  {ct.Name}\n");
                    continue;
                }

                // End of complex field, complex type or enum
                // end of [typedef] struct/union/enum
                if (line.StartsWith("}") && line.EndsWith(";"))
                {
                    var name = line[1..^ 1].Trim();