private static void CheckMethodBase(MethodBase methodBase) { foreach (Instruction i in MethodBodyReader.GetInstructions(methodBase)) { MethodBase mb = i.Operand as MethodBase; MethodInfo mi = i.Operand as MethodInfo; FieldInfo fi = i.Operand as FieldInfo; if (mb != null) { foreach (var parameter in mb.GetParameters()) { CheckUsedType(parameter.ParameterType); } CheckUsedType(mb.DeclaringType); } if (mi != null) { CheckUsedType(mi.ReturnType); } if (fi != null) { CheckUsedType(fi.FieldType); } } }
/// <summary> /// Checks if the given method has a IL instruction that SETS a persistent field /// </summary> private static bool MethodSetsPersistentField(MethodBase partModuleMethod) { var method = DynamicTools.CreateDynamicMethod(partModuleMethod, "read"); var instructions = MethodBodyReader.GetInstructions(method.GetILGenerator(), partModuleMethod); //OpCodes.Stfld is the opcode for SETTING the value of a field foreach (var instruction in instructions.Where(i => i.opcode == OpCodes.Stfld)) { if (!(instruction.operand is FieldInfo operand)) { continue; } if (FieldIsIgnored(operand)) { continue; } var attributes = operand.GetCustomAttributes(typeof(KSPField), false).Cast <KSPField>().ToArray(); if (attributes.Any() && attributes.First().isPersistant) { return(true); } } return(false); }
static MethodBase TargetMethod() { var methods = (from type in ReflectionHelper.Types from prop in type.GetProperties() where prop.Name == "FileLocationWiiU" from method in prop.DeclaringType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) select method).GetEnumerator(); while (methods.MoveNext()) { var instructions = MethodBodyReader.GetInstructions(null, methods.Current); if (instructions.Any(x => x.opcode == OpCodes.Call && ((MethodInfo)x.operand).Name == "set_FileLocation3DS")) { textBoxes = (from instruction in instructions where instruction.opcode == OpCodes.Ldfld let field = (FieldInfo)instruction.operand where field.FieldType.Name == "RadTextBox" select field).ToList(); if (textBoxes.Count > 0) { break; } } } return(methods.Current); }
public static void PrintToils(JobDriver toilMaker) { var toils = PickToils(toilMaker); if (toils != null) { foreach (var toil in toils) { Console.WriteLine($"{toil.GetType()}"); foreach (var ci in MethodBodyReader.GetInstructions(toil.initAction.Method)) { var operand = ci.operand is Label ? ("Label " + ci.operand.GetHashCode()).ToString() : ci.operand; string labels = ""; if (ci.labels.Count > 0) { foreach (var label in ci.labels) { labels += $"Label {label.GetHashCode()}"; } } else { labels += "no labels"; } Console.WriteLine($" {ci.opcode} | {operand} | {labels}"); } } } else { Console.WriteLine("Null"); } }
public void Test_CanGetInstructionsWithNoILGenerator() { var method = typeof(Class12).GetMethod(nameof(Class12.FizzBuzz)); var instrsNoGen = MethodBodyReader.GetInstructions(generator: null, method); var dynamicMethod = MethodPatcher.CreateDynamicMethod(method, "_Patch", false); var instrsHasGen = MethodBodyReader.GetInstructions(dynamicMethod.GetILGenerator(), method); Assert.AreEqual(instrsNoGen.Count, instrsHasGen.Count); for (var i = 0; i < instrsNoGen.Count; i++) { var instrNoGen = instrsNoGen[i]; var instrHasGen = instrsHasGen[i]; Assert.AreEqual(instrNoGen.offset, instrHasGen.offset, "offset @ {0} ({1})", i, instrNoGen); Assert.AreEqual(instrNoGen.opcode, instrHasGen.opcode, "opcode @ {0} ({1})", i, instrNoGen); AssertAreEqual(instrNoGen.operand, instrHasGen.operand, "operand", i, instrNoGen); CollectionAssert.AreEqual(instrNoGen.labels, instrHasGen.labels, "labels @ {0}", i); CollectionAssert.AreEqual(instrNoGen.blocks, instrHasGen.blocks, "blocks @ {0}", i); AssertAreEqual(instrNoGen.argument, instrHasGen.argument, "argument", i, instrNoGen); // The only difference between w/o gen and w/ gen is this: var operandType = instrNoGen.opcode.OperandType; if ((operandType == OperandType.ShortInlineVar || operandType == OperandType.InlineVar) && instrNoGen.argument is object) { #if NETCOREAPP3_0 || NETCOREAPP3_1 Assert.AreEqual("System.Reflection.RuntimeLocalVariableInfo", instrNoGen.argument.GetType().FullName, "w/o generator argument type @ {0} ({1})", i, instrNoGen); #else Assert.AreEqual("System.Reflection.LocalVariableInfo", instrNoGen.argument.GetType().FullName, "w/o generator argument type @ {0} ({1})", i, instrNoGen); #endif Assert.AreEqual(typeof(LocalBuilder), instrHasGen.argument.GetType(), "w/ generator argument type @ {0} ({1})", i, instrNoGen); } } }
private string validate(MethodInfo mi) { if (mi.GetMethodBody() == null) { return(""); } var instructions = MethodBodyReader.GetInstructions(mi); foreach (Instruction instruction in instructions) { MethodInfo methodInfo = instruction.Operand as MethodInfo; if (methodInfo != null) { Type type = methodInfo.DeclaringType; ParameterInfo[] parameters = methodInfo.GetParameters(); String temp = string.Format("{0}.{1}({2});", type.FullName, methodInfo.Name, String.Join(", ", parameters.Select(p => p.ParameterType.FullName + " " + p.Name).ToArray()) ); foreach (string n in invalidNames) { if (temp.Contains(n)) { return(n); } } } } return(""); }
public void CanGetInstructionsWithNoILGenerator() { var method = typeof(Class12).GetMethod(nameof(Class12.FizzBuzz)); var instrsNoGen = MethodBodyReader.GetInstructions(generator: null, method); var dynamicMethod = DynamicTools.CreateDynamicMethod(method, "_Patch"); var instrsHasGen = MethodBodyReader.GetInstructions(dynamicMethod.GetILGenerator(), method); Assert.AreEqual(instrsNoGen.Count, instrsHasGen.Count); for (var i = 0; i < instrsNoGen.Count; i++) { var instrNoGen = instrsNoGen[i]; var instrHasGen = instrsHasGen[i]; Assert.AreEqual(instrNoGen.offset, instrHasGen.offset, "offset @ {0} ({1})", i, instrNoGen); Assert.AreEqual(instrNoGen.opcode, instrHasGen.opcode, "opcode @ {0} ({1})", i, instrNoGen); AssertAreEqual(instrNoGen.operand, instrHasGen.operand, "operand", i, instrNoGen); CollectionAssert.AreEqual(instrNoGen.labels, instrHasGen.labels, "labels @ {0}", i); CollectionAssert.AreEqual(instrNoGen.blocks, instrHasGen.blocks, "blocks @ {0}", i); AssertAreEqual(instrNoGen.operand, instrHasGen.operand, "argument", i, instrNoGen); // The only difference between w/o gen and w/ gen is this: var operandType = instrNoGen.opcode.OperandType; if ((operandType == OperandType.ShortInlineVar || operandType == OperandType.InlineVar) && !(instrNoGen.argument is null)) { Assert.AreEqual(typeof(LocalVariableInfo), instrNoGen.argument.GetType(), "w/o generator argument type @ {0} ({1})", i, instrNoGen); Assert.AreEqual(typeof(LocalBuilder), instrHasGen.argument.GetType(), "w/ generator argument type @ {0} ({1})", i, instrNoGen); } } }
/// <summary> /// Extract all methods that make use of GetContext method /// </summary> /// <param name="assembly"></param> /// <returns></returns> public static IEnumerable <MethodInfo> FindAllApmContextUsage(Assembly assembly) { var methodsToScan = assembly.GetTypes() .SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) .Where(methodInfo => MethodBodyReader.GetInstructions(methodInfo) .Any(instruction => instruction.Operand is MethodInfo && (instruction.Operand as MethodInfo).Name == "GetContext" && typeof(ApmContext).IsAssignableFrom((instruction.Operand as MethodInfo).DeclaringType)) ); return(methodsToScan); }
public static List <ILInstruction> GetInstructions(MethodBase method) { var dummy = new DynamicMethod("Dummy", typeof(void), new Type[] { }); if (method.GetMethodBody() is null) { return(null); } return(MethodBodyReader.GetInstructions(dummy.GetILGenerator(), method)); }
internal static MethodBase TargetMethod() { var shwGetter = ReflectionHelper.Settings.GetProperty("ShowHaxchiWarning").GetGetMethod(); return((from method in ReflectionHelper.NusGrabberForm.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) where method.GetParameters().Length == 1 && method.GetParameters()[0].ParameterType.IsAbstract let instructions = MethodBodyReader.GetInstructions(null, method) where instructions.Any(i => i.opcode == OpCodes.Callvirt && ((MethodInfo)i.operand) == shwGetter) select method).FirstOrDefault()); }
public static HashSet <int> ComputeLabels(MethodBase definition) { var labels = new HashSet <int>(); var body = definition.GetMethodBody(); if (body == null) { return(labels); } var instructions = MethodBodyReader.GetInstructions(definition); foreach (var instruction in instructions) { var opcodeStr = instruction.OpCode.Value; switch (opcodeStr) { case ObcodeIntValues.Beq: case ObcodeIntValues.BeqS: case ObcodeIntValues.Bne: case ObcodeIntValues.BneS: case ObcodeIntValues.Bge: case ObcodeIntValues.BgeS: case ObcodeIntValues.Bgt: case ObcodeIntValues.BgtS: case ObcodeIntValues.BrTrueS: case ObcodeIntValues.BrTrue: case ObcodeIntValues.BrZero: case ObcodeIntValues.BrZeroS: case ObcodeIntValues.Blt: case ObcodeIntValues.BltS: case ObcodeIntValues.BrS: case ObcodeIntValues.Br: case ObcodeIntValues.Leave: case ObcodeIntValues.LeaveS: { var offset = ((Instruction)instruction.Operand).Offset; AddLabelIfDoesntExist(offset, labels); } break; case ObcodeIntValues.Switch: { var offsets = (Instruction[])instruction.Operand; foreach (var offset in offsets) { AddLabelIfDoesntExist(offset.Offset, labels); } } break; } } return(labels); }
public static List <CodeInstruction> GetInstructionsFromMethod(MethodInfo targetMethod) { Validate.NotNull(targetMethod); List <CodeInstruction> instructions = new List <CodeInstruction>(); foreach (ILInstruction instruction in MethodBodyReader.GetInstructions(targetMethod)) { instructions.Add(instruction.GetCodeInstruction()); } return(instructions); }
public static IEnumerable <CodeInstruction> TranspileStart(IEnumerable <CodeInstruction> instructions) { MethodBase ReplacementMethod = typeof(CheckPlantGrowthStageActionPatch).GetMethod("Start"); List <ILInstruction> MethodILInstructions = MethodBodyReader.GetInstructions(ReplacementMethod); List <CodeInstruction> ReplacementMethodInstructions = new List <CodeInstruction>(); foreach (ILInstruction MethodILInstruction in MethodILInstructions) { ReplacementMethodInstructions.Add(MethodILInstruction.GetCodeInstruction()); } return(ReplacementMethodInstructions); }
internal static IEnumerable <CodeInstruction> LoadDefinitionsTranspiler( IEnumerable <CodeInstruction> instructions) { var replacementMethod = AccessTools.Method(typeof(TileDatabasePatches), nameof(LoadDefinitionsInit)); var methodIlInstructions = MethodBodyReader.GetInstructions(new DynamicMethod(Guid.NewGuid().ToString(), typeof(object), new Type[0]).GetILGenerator(), replacementMethod); var replacementMethodInstructions = new List <CodeInstruction>(); foreach (var methodIlInstruction in methodIlInstructions) { replacementMethodInstructions.Add(methodIlInstruction.GetCodeInstruction()); } return(replacementMethodInstructions); }
private static void ScanMethodsForLocalizerInvocations(IReflect callerType, IDictionary <string, List <string> > translations) { foreach (var callerMethod in callerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.CreateInstance)) { if (GetStringMethods.Contains(callerMethod)) { continue; // skip calling self } var callerBody = callerMethod.GetMethodBody(); if (callerBody == null) { continue; // no body } var instructions = MethodBodyReader.GetInstructions(callerMethod); foreach (Instruction instruction in instructions) { if (instruction.Operand is MethodInfo methodInfo && GetStringMethods.Contains(methodInfo) && instruction.Previous.Operand is string value) { string scope; if (instruction.Previous.Previous != null && instruction.Previous.Previous.Operand is FieldInfo field) { scope = field.DeclaringType != null && field.DeclaringType.IsGenericType ? field.DeclaringType.NormalizeResourceControllerName().Replace("_T", string.Empty) : field.DeclaringType?.Name ?? string.Empty; } else { scope = string.Empty; } if (!translations.TryGetValue(scope, out var list)) { translations.Add(scope, list = new List <string>()); } list.Add(value); } } } }
private void LookForMethodCallers() { AssemblyScanner.Register(type => { var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); foreach (MethodInfo methodInfo in methods) { if (methodInfo.IsAbstract) { continue; } MethodBody methodBody = methodInfo.GetMethodBody(); if (methodBody == null) { continue; } try{ List <Instruction> instructions = MethodBodyReader.GetInstructions(methodInfo); foreach (var instruction in instructions) { MethodInfo calledMethod = instruction.Operand as MethodInfo; if (calledMethod == null) { continue; } foreach (var users in allusers) { if (users.TryAddCall(type, methodInfo, calledMethod, instruction)) { break; } } } }catch (Exception e) { //Debug.LogError("On " + methodInfo.DeclaringType.Name+"."+methodInfo.Name); //Debug.LogException(e); } } }, AssemblyScanner.OnlyProject ); AssemblyScanner.Scan(); }
public static List <MethodInterpreter> ComputeDependencies(MethodBase definition) { var resultDict = new Dictionary <string, MethodInterpreter>(); var body = definition.GetMethodBody(); if (body == null) { return(new List <MethodInterpreter>()); } var instructions = MethodBodyReader.GetInstructions(definition); foreach (var instruction in instructions) { var opcodeStr = instruction.OpCode.Value; switch (opcodeStr) { case ObcodeIntValues.CallVirt: case ObcodeIntValues.Call: case ObcodeIntValues.CallInterface: { var operand = (MethodBase)instruction.Operand; if (operand == null) { break; } AddMethodIfNecessary(operand, resultDict); break; } case ObcodeIntValues.NewObj: { var operand = (ConstructorInfo)instruction.Operand; if (operand == null) { break; } AddMethodIfNecessary(operand, resultDict); break; } } } return(resultDict.Values.ToList()); }
static void ReadAssemblies(List <string> assemblyList) { assemblyList.ForEach(a => { Console.WriteLine("Reading {0}", a); Assembly assembly = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, a)); List <Type> types = assembly.GetTypes().ToList(); types.ForEach(t => { List <MethodInfo> methods = t.GetMethods().ToList(); methods.ForEach(m => { MethodBase methodBase = m; ParameterInfo[] parameters = m.GetParameters(); string functionName = string.Format("{0}({1})", m.Name, String.Join(", ", parameters.Select(p => p.ParameterType.FullName).ToArray()) ); int functId = db.WriteFunction(t.Namespace, t.Name, functionName); var instructions = MethodBodyReader.GetInstructions(methodBase); foreach (Instruction instruction in instructions) { MethodInfo methodInfo = instruction.Operand as MethodInfo; if (methodInfo != null) { Type type = methodInfo.DeclaringType; ParameterInfo[] instParameters = methodInfo.GetParameters(); string instName = string.Format("{0}({1})", methodInfo.Name, String.Join(", ", instParameters.Select(p => p.ParameterType.FullName).ToArray()) ); db.WriteReference(functId, type.Namespace, type.Name, instName); } } }); }); }); }
public void Process(CrRuntimeLibrary crRuntime) { if (Kind != MethodKind.Default) { return; } if (Interpreted) { return; } if (HandlePlatformInvokeMethod(Method)) { return; } if (Method.GetMethodBody() == null) { return; } var instructions = MethodBodyReader.GetInstructions(Method); var labelList = ComputeLabels(Method); MidRepresentation.Method = Method; MidRepresentation.Vars.SetupLocalVariables(Method); var evaluator = new EvaluatorStack(); var operationFactory = new MetaMidRepresentationOperationFactory(MidRepresentation, evaluator); for (var index = 0; index < instructions.Length; index++) { var instruction = instructions[index]; EvaluateInstuction(instruction, operationFactory, labelList, crRuntime); } //Ensure.IsTrue(evaluator.Count == 0, "Stack not empty!"); AnalyzeProperties.Setup(MidRepresentation.Vars.Arguments, MidRepresentation.Vars.VirtRegs, MidRepresentation.Vars.LocalVars); Interpreted = true; }
static void Check(Type checkType, string methodName) { MethodBase methodBase = checkType.GetMethod(methodName, new Type[] { typeof(Int32) }); var instructions = MethodBodyReader.GetInstructions(methodBase); foreach (Instruction instruction in instructions) { MethodInfo methodInfo = instruction.Operand as MethodInfo; if (methodInfo != null) { Type type = methodInfo.DeclaringType; ParameterInfo[] parameters = methodInfo.GetParameters(); Console.WriteLine("{0}.{1}({2});", type.FullName, methodInfo.Name, String.Join(", ", parameters.Select(p => p.ParameterType.FullName + " " + p.Name).ToArray()) ); } } }
private static HashSet <string> DumpKeybinds(MethodInfo method) { var instructions = new List <CodeInstruction> { }; MethodBodyReader.GetInstructions(GetILGenerator(), method).ForEach( (instr) => instructions.Add(instr.GetCodeInstruction()) ); // Uses Harmony's internals, because the proper method is not present in SRML's version of Harmony. var set = new HashSet <string> { }; foreach (CodeInstruction instr in instructions) { if (instr.opcode == OpCodes.Ldstr && ((string)instr.operand).StartsWith(Keybind.KEYBIND_PREFIX)) { set.Add((string)instr.operand); } } return(set);
/// <summary> /// Checks if the given method has a IL instruction that SETS (therefore, it changes the value) a customized field /// </summary> private static IEnumerable <FieldInfo> GetCustomizedFieldsChangedByMethod(MethodBase partModuleMethod, ModuleDefinition definition) { var listOfFields = new HashSet <FieldInfo>(); var method = DynamicTools.CreateDynamicMethod(partModuleMethod, "read"); var instructions = MethodBodyReader.GetInstructions(method.GetILGenerator(), partModuleMethod); //OpCodes.Stfld is the opcode for SETTING the value of a field foreach (var instruction in instructions.Where(i => i.opcode == OpCodes.Stfld)) { if (!(instruction.operand is FieldInfo operand)) { continue; } if (definition.Fields.Any(f => f.FieldName == operand.Name)) { listOfFields.Add(operand); } } return(listOfFields); }
public static List <ILInstruction> GetInstructions(MethodInfo method) { var dummy = new DynamicMethod("Dummy", typeof(void), new Type[] { }); return(MethodBodyReader.GetInstructions(dummy.GetILGenerator(), method)); }
public static IEnumerable <ILInstruction> GetILInstructions(DynamicMethod method) { return(MethodBodyReader.GetInstructions(method.GetILGenerator(), method)); }
public void CallCompiler(string inputAssemblyName, string outputExeName) { var commandLineParse = CommandLineParse.Instance; if (!String.IsNullOrEmpty(inputAssemblyName)) { commandLineParse.ApplicationInputAssembly = inputAssemblyName; } if (!String.IsNullOrEmpty(outputExeName)) { commandLineParse.ApplicationNativeExe = outputExeName; } var dir = Directory.GetCurrentDirectory(); inputAssemblyName = Path.Combine(dir, commandLineParse.ApplicationInputAssembly); var asm = Assembly.LoadFile(inputAssemblyName); var definition = asm.EntryPoint; var start = Environment.TickCount; OptimizationLevelBase.ClearOptimizations(); OptimizationLevelBase.Instance = new OptimizationLevels(); OptimizationLevelBase.Instance.EnabledCategories.Clear(); OptimizationLevelBase.Instance.EnabledCategories.AddRange(OptimizationList); OptimizationLevelBase.UpdateOptimizationsFromCategories(OptimizationLevelBase.OptimizationPasses); OptimizationLevelBase.SortOptimizations(); // OptimizationLevelBase.Instance = new OptimizationLevels(); // OptimizationLevelBase.OptimizerLevel = 2; // OptimizationLevelBase.Instance.EnabledCategories.Add(OptimizationCategories.All); var closureEntities = ClosureEntitiesUtils.BuildClosureEntities(definition, typeof(CrString).Assembly); var sb = closureEntities.BuildFullSourceCode(); var end = Environment.TickCount - start; CompilerErrors += String.Format("Compilation time: {0} ms", end); var opcodes = closureEntities.MethodImplementations; var intermediateOutput = "-------------IL:-------------\n"; foreach (var opcode in opcodes) { intermediateOutput += " " + opcode.Key + ": \n"; if (opcode.Value.Kind != MethodKind.CilInstructions) { intermediateOutput += "// Provided By Framework \n\n"; continue; } try { var instructions = MethodBodyReader.GetInstructions(((CilMethodInterpreter)opcode.Value).Method); foreach (var op in instructions) { var oper = string.Format("\t{0}", op);; intermediateOutput += " " + oper + "\n"; } } catch (Exception) { intermediateOutput += "// Method has no body \n\n"; } intermediateOutput += "\n"; } intermediateOutput += "\n-------------IR:-------------\n"; foreach (var opcode in opcodes) { intermediateOutput += " " + opcode.Key + ": \n"; if (opcode.Value.Kind != MethodKind.CilInstructions) { intermediateOutput += "// Provided By Framework \n\n"; continue; } var cilInterpreter = (CilMethodInterpreter)opcode.Value; foreach (var op in cilInterpreter.MidRepresentation.LocalOperations) { var oper = string.Format("{1}\t({0})", op.Kind, op);; intermediateOutput += " " + oper + "\n"; } intermediateOutput += "\n"; } OutputCode = sb.ToString(); ILCode = intermediateOutput; //TODO: Make this call all the different compilers i.e. TestHelloWorld_GCC.exe etc... }
public static IEnumerable <ILInstruction> GetILInstructions(MethodInfo method) { DynamicMethod dynMethod = new DynamicMethod(method.Name, method.ReturnType, method.GetParameters().Select(p => p.ParameterType).ToArray(), false); return(MethodBodyReader.GetInstructions(dynMethod.GetILGenerator(), method)); }
/// <summary> /// Saves localizable JSON data in the current working directory for the provided assembly. /// </summary> /// <param name="assembly">Assembly to save localization data from.</param> public static void ExportLocalizableForAssembly(Assembly assembly) { var types = assembly.GetTypes(); var outList = new Dictionary <string, LocEntry>(); foreach (var type in types.Where(x => x.IsClass || x.IsAbstract)) { var toParse = new List <MethodBase>(); toParse.AddRange(type.GetTypeInfo().DeclaredConstructors); toParse.AddRange(type.GetTypeInfo().DeclaredMethods); foreach (var method in toParse) { try { var instructions = MethodBodyReader.GetInstructions(method); foreach (var instruction in instructions) { if (instruction.OpCode == OpCodes.Call) { var methodInfo = instruction.Operand as MethodInfo; if (methodInfo != null && methodInfo.IsStatic) { var methodType = methodInfo.DeclaringType; var parameters = methodInfo.GetParameters(); if (!methodInfo.Name.Contains("Localize")) { continue; } Console.WriteLine("->({0}) {1}.{2}.{3}({4});", method.DeclaringType.Assembly.GetName().Name, type.FullName, methodType.Name, methodInfo.Name, string.Join(", ", parameters.Select(p => p.ParameterType.FullName + " " + p.Name).ToArray()) ); var entry = new LocEntry { Message = instruction.Previous.Operand as string, Description = $"{type.Name}.{method.Name}" }; var key = instruction.Previous.Previous.Operand as string; if (string.IsNullOrEmpty(key)) { throw new Exception( $"Key was empty for message: {entry.Message} (from {entry.Description})"); } if (outList.Any(x => x.Key == key)) { if (outList.Any(x => x.Key == key && x.Value.Message != entry.Message)) { throw new Exception( $"Message with key {key} has previous appearance but other fallback text in {entry.Description}"); } } else { Console.WriteLine($" ->{key} - {entry.Message} (from {entry.Description})"); outList.Add(key, entry); } } } } } catch (Exception ex) { Debug.WriteLine($"Couldn't parse {method.Name}:\n{ex}"); } } } File.WriteAllText($"{GetAssemblyName(assembly)}_Localizable.json", JsonConvert.SerializeObject(outList, new JsonSerializerSettings { Formatting = Formatting.Indented })); }