public override void VisitEnumDeclaration(EnumDeclarationSyntax node) { base.VisitEnumDeclaration(node); EnumObject enumObject = new EnumObject(); enumObject.@namespace = this.currentNamespace; enumObject.name = node.Identifier.ValueText; enumObject.flags = node.AttributeLists.Any(list => list.Attributes.Any(attr => attr.Name.ToString().StartsWith("Flags"))); enumObject.FromCSharp = true; foreach (EnumMemberDeclarationSyntax member in node.Members) { EnumObject.Member m = new EnumObject.Member(); m.name = member.Identifier.ValueText; if (member.EqualsValue != null) { m.value = member.EqualsValue.Value.ToString(); } enumObject.members.Add(m); } this.enumObjects.Add(enumObject); }
private bool TryMergingWithExistingObject(EnumObject enumObject) { if (enumObject.finished) { return(false); } bool merged = false; foreach (var member in enumObject.members) { if (this.enumMemberNameToEnumObj.TryGetValue(member.name, out var matchedObj)) { merged = this.MergeObjIntoOther(enumObject, matchedObj); if (merged) { break; } } } foreach (var use in enumObject.uses) { if (this.enumMemberNameToEnumObj.TryGetValue(use.ToString(), out var matchedObj)) { merged = this.MergeObjIntoOther(enumObject, matchedObj); if (merged) { break; } } } return(merged); }
private void LoadEnumsFromSourcesFiles(string sourcesDir) { foreach (var file in Directory.GetFiles(sourcesDir, "*.cs").Where(f => !f.EndsWith(".enums.cs") && !f.EndsWith(".constants.cs"))) { foreach (var enumObj in EnumObject.LoadFromFile(file)) { this.LoadObjMembersIntoMap(enumObj); } } }
public static EnumObject Normalize(EnumObject enumObject) { EnumObject fixedObject = Newtonsoft.Json.JsonConvert.DeserializeObject <EnumObject>(Newtonsoft.Json.JsonConvert.SerializeObject(enumObject)); if (fixedObject.name != null && fixedObject.name.Contains('.')) { fixedObject.name = fixedObject.name.Replace('.', '_'); } var firstUse = fixedObject.uses.FirstOrDefault(); if (firstUse == null) { return(fixedObject); } if (fixedObject.name == null || !fixedObject.name.Contains('_')) { List <string> namesToMatch = new List <string>(); const int MaxToCheck = 4; for (int i = 0; i < fixedObject.uses.Count; i++) { var use = fixedObject.uses[i]; if (use.method != null) { namesToMatch.Add(use.method); if (namesToMatch.Count == MaxToCheck) { break; } } } string varName = firstUse.method != null ? firstUse.parameter : firstUse.field; string matchedName = GetCommonCamelCaseName(namesToMatch.ToArray()); if (string.IsNullOrEmpty(matchedName)) { matchedName = firstUse.method != null ? firstUse.method : firstUse.@struct; } fixedObject.name = $"{matchedName}_{varName}"; } foreach (var member in fixedObject.members) { if (int.TryParse(member.name, out var value)) { string name = firstUse.parameter != null ? firstUse.parameter : firstUse.field; member.value = member.name; member.name = $"{name}{value}"; } } return(fixedObject); }
private static List <EnumObject> LoadEnumsFromSourceFiles(IEnumerable <string> fileNames) { List <EnumObject> enumObjects = new List <EnumObject>(); foreach (var file in fileNames) { enumObjects.AddRange(EnumObject.LoadFromFile(file)); } return(enumObjects); }
public bool AddEnum(EnumObject enumObject) { if (enumObject.name == null || !enumObject.members.Any(m => m.value != null)) { // Skip if we have no name or values to write return(false); } this.EnsureStarted(); if (enumObject.flags) { this.writer.WriteLine( $" [Flags]"); } string type = enumObject.type ?? "uint"; bool forceUnsigned = enumObject.flags || (enumObject.type != null && (enumObject.type == "uint" || enumObject.type == "ulong" || enumObject.type == "ushort")); StringWriter enumBodyWriter = new StringWriter(); foreach (var member in enumObject.members.Where(m => m.value != null)) { var currentType = type; string valueText = ConstantWriter.FixIntValueText(forceUnsigned, ref currentType, member.value); enumBodyWriter.Write( $@" {member.name} = {valueText}, "); // Make the type more specific if it's signed if (currentType != type && (type != "int" && type != "long")) { type = currentType; } } if (enumObject.type != null) { type = enumObject.type; } string typePart = type != "int" ? $" : {type}" : string.Empty; this.writer.WriteLine( $@" public enum {enumObject.name}{typePart} {{"); this.writer.Write(enumBodyWriter.ToString()); this.writer.WriteLine( $@" }} "); return(true); }
private List <EnumObject> LoadEnumsFromJsonFiles(string[] enumJsonFiles) { List <EnumObject> enumObjects = new List <EnumObject>(); if (enumJsonFiles != null) { foreach (var enumJsonFile in enumJsonFiles) { enumObjects.AddRange(EnumObject.LoadFromFile(enumJsonFile)); } } return(enumObjects); }
private static List <EnumObject> LoadEnumsFromSourceFiles(string sourcesDir) { List <EnumObject> enumObjects = new List <EnumObject>(); if (Directory.Exists(sourcesDir)) { foreach (var file in Directory.GetFiles(sourcesDir, "*.cs")) { enumObjects.AddRange(EnumObject.LoadFromFile(file)); } } return(enumObjects); }
private bool MergeObjIntoOther(EnumObject src, EnumObject dest) { // If the dest is from C# code, make src point to dest. In effect, // it will give its uses to the C# version if (dest.FromCSharp) { src.name = null; src.members.Clear(); src.addUsesTo = dest.name; return(true); } // Don't mess with an object that was declared to be finished in the json if (src.finished) { return(false); } foreach (var m in src.members) { if (StringComparer.OrdinalIgnoreCase.Equals(m.name, "None")) { continue; } dest.AddIfNotSet(m.name, m.value); if (this.enumMemberNameToEnumObj.TryGetValue(m.name, out var memberOwner)) { if (memberOwner != dest && memberOwner != src) { this.enumMemberNameToEnumObj[m.name] = dest; this.MergeObjIntoOther(memberOwner, dest); } } this.enumMemberNameToEnumObj[m.name] = dest; } foreach (var u in src.uses) { dest.AddUse(u); } return(true); }
private void AddEnumWarningAndSuggestedMapping(string message, EnumObject enumObject) { this.output.Add(message); StringBuilder suggestedName = new StringBuilder(); foreach (var match in NamePartsRegex.Matches(enumObject.name)) { if (suggestedName.Length != 0) { suggestedName.Append('_'); } suggestedName.Append(match.ToString().ToUpperInvariant()); } if (suggestedName.Length != 0) { this.suggestedEnumRenames.Add($"{enumObject.name}={suggestedName}"); } }
private void LoadObjMembersIntoMap(EnumObject obj) { foreach (var member in obj.members) { if (StringComparer.OrdinalIgnoreCase.Equals(member.name, "None")) { continue; } if (!this.enumMemberNameToEnumObj.ContainsKey(member.name)) { this.enumMemberNameToEnumObj[member.name] = obj; } } foreach (var use in obj.uses) { if (!this.enumMemberNameToEnumObj.ContainsKey(use.ToString())) { this.enumMemberNameToEnumObj[use.ToString()] = obj; } } }
private void ScrapeConstantsFromTraversedFiles(Dictionary <string, string> traversedFileMap) { Dictionary <string, string> autoReplacements = GetAutoValueReplacements(); HashSet <string> manualEnumMemberNames = this.GetManualEnumMemberNames(); // For each traversed header, scrape the constants foreach (KeyValuePair <string, string> item in traversedFileMap) { var header = item.Key; var currentNamespace = item.Value; if (!File.Exists(header)) { continue; } string currentHeaderName = Path.GetFileName(header).ToLowerInvariant(); List <EnumObject> autoEnumObjsForCurrentHeader = new List <EnumObject>(this.enumObjectsFromJsons.Where(e => e.autoPopulate != null && e.autoPopulate.header.ToLowerInvariant() == currentHeaderName)); Regex autoPopulateReg = null; if (autoEnumObjsForCurrentHeader.Count != 0) { StringBuilder autoPopulateRegexPattern = new StringBuilder(); foreach (EnumObject autoEnumObj in autoEnumObjsForCurrentHeader) { if (autoPopulateRegexPattern.Length != 0) { autoPopulateRegexPattern.Append('|'); } autoPopulateRegexPattern.Append($"(^{autoEnumObj.autoPopulate.filter})"); } autoPopulateReg = new Regex(autoPopulateRegexPattern.ToString()); } string continuation = null; bool processingGuidMultiLine = false; string defineGuidKeyword = null; foreach (string currentLine in File.ReadAllLines(header)) { string fixedCurrentLine = currentLine; if (continuation != null && continuation.EndsWith('"') && currentLine.StartsWith('"')) { continuation = continuation.Substring(0, continuation.Length - 1); fixedCurrentLine = currentLine.Substring(1); } string line = continuation == null ? fixedCurrentLine : continuation + fixedCurrentLine; if (line.EndsWith("\\")) { continuation = line.Substring(0, line.Length - 1); continue; } if (processingGuidMultiLine) { continuation = StripComments(line).Trim(); if (continuation.EndsWith(';')) { processingGuidMultiLine = false; this.AddConstantGuid(defineGuidKeyword, currentNamespace, continuation); continuation = null; } continue; } continuation = null; Match defineGuidMatch = DefineGuidConstRegex.Match(line); if (defineGuidMatch.Success) { defineGuidKeyword = defineGuidMatch.Groups[1].Value; line = defineGuidMatch.Groups[2].Value; line = StripComments(line).Trim(); if (line.EndsWith(';')) { this.AddConstantGuid(defineGuidKeyword, currentNamespace, line); } else { continuation = line; processingGuidMultiLine = true; } continue; } Match defineAviGuidMatch = DefineAviGuidConstRegex.Match(line); if (defineAviGuidMatch.Success) { defineGuidKeyword = defineAviGuidMatch.Groups[1].Value; var guidName = defineAviGuidMatch.Groups[2].Value; var l = defineAviGuidMatch.Groups[3].Value; var w1 = defineAviGuidMatch.Groups[4].Value; var w2 = defineAviGuidMatch.Groups[5].Value; var defineGuidLine = $"{guidName}, {l}, {w1}, {w2}, 0xC0,0,0,0,0,0,0,0x46)"; this.AddConstantGuid(defineGuidKeyword, currentNamespace, defineGuidLine); continue; } Match defineMediaTypeGuidMatch = DefineMediaTypeGuidConstRegex.Match(line); if (defineMediaTypeGuidMatch.Success) { defineGuidKeyword = defineMediaTypeGuidMatch.Groups[1].Value; var guidName = defineMediaTypeGuidMatch.Groups[2].Value; var value = defineMediaTypeGuidMatch.Groups[3].Value; var fccMatch = FccRegex.Match(value); if (fccMatch.Success) { var fccValue = fccMatch.Groups[1].Value.ToArray(); uint convertedValue = (uint)(fccValue[0]) | (uint)(fccValue[1] << 8) | (uint)(fccValue[2] << 16) | (uint)(fccValue[3] << 24); value = $"0x{convertedValue:x}"; } var defineGuidLine = $"{guidName}, {value}, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71)"; this.AddConstantGuid(defineGuidKeyword, currentNamespace, defineGuidLine); continue; } Match defineMatch = DefineRegex.Match(line); // Skip if not #define ... if (!defineMatch.Success) { this.TryScrapingEnumFlags(line); continue; } string name = defineMatch.Groups[1].Value; if (this.ShouldExclude(name)) { continue; } #if DEBUG if (name == "SOME_CONST_NAME") { } #endif // Get rid of trailing comments string rawValue = StripComments(defineMatch.Groups[2].Value.Trim()); if (autoReplacements.TryGetValue(rawValue, out var updatedRawValue)) { rawValue = updatedRawValue; } string fixedRawValue = rawValue; // Get rid of enclosing parens. Makes it easier to parse with regex if (fixedRawValue.StartsWith('(') && fixedRawValue.EndsWith(')')) { fixedRawValue = fixedRawValue.Substring(1, rawValue.Length - 2).Trim(); } Match ctlCodeMatch = CtlCodeRegex.Match(fixedRawValue); if (ctlCodeMatch.Success) { var parts = ctlCodeMatch.Groups[1].Value.Split(','); if (parts.Length == 4) { this.AddCtlCodeConstant(currentNamespace, name, parts[0].Trim(), parts[1].Trim(), parts[2].Trim(), parts[3].Trim()); continue; } } Match usageMatch = HidUsageRegex.Match(fixedRawValue); if (usageMatch.Success) { this.AddConstantValue(currentNamespace, "ushort", name, usageMatch.Groups[1].Value); continue; } if (fixedRawValue.StartsWith("AUDCLNT_ERR(")) { fixedRawValue = fixedRawValue.Replace("AUDCLNT_ERR(", "MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, "); } else if (fixedRawValue.StartsWith("AUDCLNT_SUCCESS(")) { fixedRawValue = fixedRawValue.Replace("AUDCLNT_SUCCESS(", "MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_AUDCLNT, "); } Match makeHresultMatch = MakeHresultRegex.Match(fixedRawValue); if (makeHresultMatch.Success) { var parts = makeHresultMatch.Groups[1].Value.Split(','); if (parts.Length == 3) { this.AddMakeHresultConstant(currentNamespace, name, parts[0].Trim(), parts[1].Trim(), parts[2].Trim()); continue; } } Match intCastToLpcstrMatch = IntCastToLpcstrRegex.Match(fixedRawValue); if (intCastToLpcstrMatch.Success) { var nativeStrType = intCastToLpcstrMatch.Groups[1].Value; var value = intCastToLpcstrMatch.Groups[2].Value; this.AddConstantInteger(currentNamespace, nativeStrType, name, value); continue; } if (this.regexConstHelper.TryProcessingLine(currentNamespace, name, fixedRawValue)) { continue; } // See if matches one of our well known constants formats Match match = DefineConstantRegex.Match(fixedRawValue); string valueText; string nativeTypeName = null; string matchedConstantType = null; bool matchedToOtherName = false; if (match.Success) { // #define E_UNEXPECTED _HRESULT_TYPEDEF_(0x8000FFFF) if (!string.IsNullOrEmpty(match.Groups[2].Value)) { if (match.Groups[2].Value == "_HRESULT_TYPEDEF_") { nativeTypeName = "HRESULT"; } valueText = match.Groups[3].Value; } // #define E_UNEXPECTED ((HRESULT)0x8000FFFF) else if (!string.IsNullOrEmpty(match.Groups[5].Value)) { nativeTypeName = "HRESULT"; valueText = match.Groups[5].Value; } // #define DXGI_RESOURCE_PRIORITY_MINIMUM ( 0x28000000 ) else if (!string.IsNullOrEmpty(match.Groups[7].Value)) { valueText = match.Groups[7].Value; } // 1.0, -2.0f else if (!string.IsNullOrEmpty(match.Groups[6].Value)) { valueText = match.Groups[6].Value; string type = valueText.EndsWith('f') ? "float" : "double"; this.AddConstantValue(currentNamespace, type, name, valueText); continue; } // 1 << 5 else if (!string.IsNullOrEmpty(match.Groups[8].Value)) { string part1 = match.Groups[9].Value + "u"; string part2 = match.Groups[10].Value; valueText = part1 + part2; } // MAKEINTRESOURCE(-4), MAKEINTRESOURCEA(-1), MAKEINTRESOURCEW(42) else if (!string.IsNullOrEmpty(match.Groups[11].Value)) { if (match.Groups[11].Value.StartsWith("MAKEINTRESOURCEA")) { nativeTypeName = "LPCSTR"; } else { nativeTypeName = "LPCWSTR"; } valueText = match.Groups[12].Value; this.AddConstantInteger(currentNamespace, nativeTypeName, name, valueText); continue; } // (HWND)-4 else if (!string.IsNullOrEmpty(match.Groups[13].Value)) { nativeTypeName = "HWND"; valueText = match.Groups[14].Value; this.AddConstantInteger(currentNamespace, nativeTypeName, name, valueText); continue; } // (IDENT_FOO + 4) else if (match.Groups[15].Success) { valueText = match.Groups[15].Value; } // (NTSTATUS)0x00000000L else if (match.Groups[17].Success) { nativeTypeName = "NTSTATUS"; valueText = match.Groups[18].Value; } // (DWORD)-1 else if (match.Groups[20].Success) { valueText = "0xFFFFFFFF"; } // (BCRYPT_ALG_HANDLE) 0x000001a1 else if (match.Groups[21].Success) { nativeTypeName = "BCRYPT_ALG_HANDLE"; valueText = match.Groups[22].Value; } // SOME_OTHER_CONSTANT else if (match.Groups[23].Success) { string otherName = match.Groups[23].Value; matchedToOtherName = true; // Only use a constant as the value if we have seen the constant before // and we know its type if (this.writtenConstants.TryGetValue(otherName, out var otherType)) { // Skip guids for now if (otherType != "Guid") { matchedConstantType = otherType; } } // If we didn't match it to another constant, keep going as we may be setting an enum valueText = otherName; } else { continue; } } else { valueText = rawValue; if (valueText.StartsWith("__TEXT(")) { valueText = valueText.Substring(2); } if (valueText.StartsWith("TEXT(")) { valueText = valueText.Substring("TEXT(".Length); if (valueText.EndsWith(')')) { valueText = valueText.Substring(0, valueText.Length - 1); } } else if (valueText.StartsWith("L\"")) { valueText = valueText.Substring(1); } // Strings can't be part of enums so go ahead and add the constant directly if (valueText.StartsWith('"')) { this.AddConstantValue(currentNamespace, "string", name, valueText); continue; } valueText = valueText.Replace("(DWORD)", "(uint)"); valueText = valueText.Replace("(ULONG)", "(uint)"); } bool updatedEnum = false; // If we see the member is part of an enum, update the member value if (this.enumMemberNameToEnumObj.TryGetValue(name, out var enumObjList)) { foreach (EnumObject enumObj in enumObjList) { enumObj.AddIfNotSet(name, valueText); updatedEnum = true; } } if (autoPopulateReg != null && nativeTypeName == null) { Match autoPopulate = autoPopulateReg.Match(name); if (autoPopulate.Success) { for (int i = 1; i < autoPopulate.Groups.Count; i++) { if (!string.IsNullOrEmpty(autoPopulate.Groups[i].Value)) { EnumObject foundObjEnum = autoEnumObjsForCurrentHeader[i - 1]; foundObjEnum.AddIfNotSet(name, valueText); updatedEnum = true; if (!this.enumMemberNameToEnumObj.TryGetValue(name, out var list)) { list = new List <EnumObject>(); this.enumMemberNameToEnumObj.Add(name, list); } list.Add(foundObjEnum); break; } } } } // If we haven't used the member to update an enum, skip it... // ...unless it's an HRESULT. Always emit them as constants too if (match.Success && (!updatedEnum || nativeTypeName != null)) { // Only add the constant if it's not part of a manual enum if (!manualEnumMemberNames.Contains(name)) { if (matchedConstantType != null) { this.AddConstantValue(currentNamespace, matchedConstantType, name, valueText); } else { // Only add as an int if it didn't match some other constant/enum name if (!matchedToOtherName) { this.AddConstantInteger(currentNamespace, nativeTypeName, name, valueText); } } } } } } }