/// <summary> /// Extracts all fields from the user type. /// </summary> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> protected override IEnumerable<UserTypeField> ExtractFields(UserTypeFactory factory, UserTypeGenerationFlags generationFlags) { ExportStaticFields = true; var fields = Symbol.Fields.OrderBy(s => s.Name).ToArray(); bool useThisClass = generationFlags.HasFlag(UserTypeGenerationFlags.UseClassFieldsFromDiaSymbolProvider); string previousName = ""; foreach (var field in fields) { if (string.IsNullOrEmpty(field.Type.Name)) continue; if (IsFieldFiltered(field) || field.Name == previousName) continue; if (field.Name.Contains("@")) { // Skip names contaings '@' continue; } // Skip fields that are actual values of enum values if (field.Type.Tag == Dia2Lib.SymTagEnum.SymTagEnum && field.Type.GetEnumValues().Any(t => t.Item1 == field.Name)) continue; var userField = ExtractField(field, factory, generationFlags, forceIsStatic: true); if (field.Type.Tag == Dia2Lib.SymTagEnum.SymTagPointerType) { // Do not use const values for pointers. // We do not allow user type implicit conversion from integers. userField.ConstantValue = string.Empty; } userField.FieldName = NormalizeSymbolNamespace(userField.FieldName); userField.PropertyName = NormalizeSymbolNamespace(userField.PropertyName); yield return userField; previousName = field.Name; } foreach (var field in GetAutoGeneratedFields(false, useThisClass)) yield return field; }
/// <summary> /// Adds the symbol to user type factory and generates the user type. /// </summary> /// <param name="symbol">The non-template symbol.</param> /// <param name="type">The XML type description.</param> /// <param name="nameSpace">The namespace.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <returns>Generated user type for the specified symbol.</returns> internal UserType AddSymbol(Symbol symbol, XmlType type, string nameSpace, UserTypeGenerationFlags generationFlags) { UserType userType; if (symbol.Tag == SymTagEnum.SymTagEnum) { userType = new EnumUserType(symbol, nameSpace); } else if (symbol.Tag == SymTagEnum.SymTagExe) { userType = new GlobalsUserType(symbol, type, nameSpace); } else if (generationFlags.HasFlag(UserTypeGenerationFlags.GeneratePhysicalMappingOfUserTypes)) { userType = new PhysicalUserType(symbol, type, nameSpace); } else { userType = new UserType(symbol, type, nameSpace); } symbol.UserType = userType; return userType; }
/// <summary> /// Writes the code for this user type to the specified output. /// </summary> /// <param name="output">The output.</param> /// <param name="error">The error text writer.</param> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="indentation">The current indentation.</param> public override void WriteCode(IndentedWriter output, TextWriter error, UserTypeFactory factory, UserTypeGenerationFlags generationFlags, int indentation = 0) { // Check if we need to write namespace string nameSpace = (DeclaredInType as NamespaceUserType)?.FullClassName ?? Namespace; if ((DeclaredInType == null || (!generationFlags.HasFlag(UserTypeGenerationFlags.SingleFileExport) && DeclaredInType is NamespaceUserType)) && !string.IsNullOrEmpty(nameSpace)) { output.WriteLine(indentation, "namespace {0}", nameSpace); output.WriteLine(indentation++, "{{"); } // Write beginning of the enumeration if (generationFlags.HasFlag(UserTypeGenerationFlags.GenerateFieldTypeInfoComment)) output.WriteLine(indentation, "// {0} (original name: {1})", ClassName, Symbol.Name); if (AreValuesFlags()) output.WriteLine(indentation, @"[System.Flags]"); if (Symbol.Size != 0) output.WriteLine(indentation, @"public enum {0} : {1}", ClassName, GetEnumBasicType(Symbol)); else output.WriteLine(indentation, @"public enum {0}", ClassName); output.WriteLine(indentation++, @"{{"); // Write values foreach (var enumValue in Symbol.GetEnumValues()) { output.WriteLine(indentation, "{0} = {1},", enumValue.Item1, enumValue.Item2); } // Enumeration end output.WriteLine(--indentation, @"}}"); if ((DeclaredInType == null || (!generationFlags.HasFlag(UserTypeGenerationFlags.SingleFileExport) && DeclaredInType is NamespaceUserType)) && !string.IsNullOrEmpty(nameSpace)) { output.WriteLine(--indentation, "}}"); } }
/// <summary> /// Adds the symbols to user type factory and generates the user types. /// </summary> /// <param name="symbols">The template symbols grouped around the same template type.</param> /// <param name="type">The XML type description.</param> /// <param name="nameSpace">The namespace.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <returns>Generated user types for the specified symbols.</returns> internal IEnumerable<UserType> AddSymbols(IEnumerable<Symbol> symbols, XmlType type, string nameSpace, UserTypeGenerationFlags generationFlags) { if (!type.IsTemplate && symbols.Count() > 1) throw new Exception("Type has more than one symbol for " + type.Name); if (!type.IsTemplate) { yield return AddSymbol(symbols.First(), type, nameSpace, generationFlags); } else { // Bucketize template user types based on number of template arguments var buckets = new Dictionary<int, List<TemplateUserType>>(); foreach (Symbol symbol in symbols) { UserType userType = null; try { // We want to ignore "empty" generic classes (for now) if (symbol.Name == null || symbol.Size == 0) continue; // Generate template user type TemplateUserType templateType = new TemplateUserType(symbol, type, nameSpace, this); #if false // TODO: Verify if we want to use simple user type instead of template user type if (templateType.AllTemplateArguments.Count == 0) { // Template does not have arguments that can be used by generic // Make it specialized type userType = this.AddSymbol(symbol, null, moduleName, generationOptions); } else #endif { List<TemplateUserType> templates; symbol.UserType = templateType; if (!buckets.TryGetValue(templateType.AllTemplateArguments.Count, out templates)) buckets.Add(templateType.AllTemplateArguments.Count, templates = new List<TemplateUserType>()); templates.Add(templateType); } } catch (Exception ex) { // TODO: Verify if we need to add this as specialization if (ex.Message != "Wrongly formed template argument") throw; } if (userType != null) yield return userType; } // Add newly generated types foreach (List<TemplateUserType> templatesInBucket in buckets.Values) { // TODO: Verify that all templates in the list can be described by the same class (also do check for inner-types) // Sort Templates by Class Name. // This removes ambiguity caused by parallel type processing. // List<TemplateUserType> templates = templatesInBucket.OrderBy(t => t.Symbol.Name.Count(c => c == '*')) .ThenBy(t => t.Symbol.Name.Count(c => c == '<')) .ThenBy(t => t.Symbol.Name).ToList(); // Select best suited type for template TemplateUserType template = templates.First(); foreach (var specializedTemplate in templates) { var arguments = specializedTemplate.AllTemplateArguments; // Check if all arguments are different if (arguments.Distinct().Count() == arguments.Count()) { // Check if all arguments are simple user type bool simpleUserType = true; foreach (var argument in arguments) { var argumentSymbol = GlobalCache.GetSymbol(argument, specializedTemplate.Module); if (argumentSymbol.Tag != SymTagEnum.SymTagUDT || argumentSymbol.Name.Contains("<")) { simpleUserType = false; break; } } if (simpleUserType) { template = specializedTemplate; break; } // Check if none of the arguments is template user type bool noneIsTemplate = true; foreach (var argument in arguments) { var argumentSymbol = GlobalCache.GetSymbol(argument, specializedTemplate.Module); if (argumentSymbol.Tag == SymTagEnum.SymTagUDT && argumentSymbol.Name.Contains("<")) { noneIsTemplate = false; break; } } if (noneIsTemplate) { template = specializedTemplate; continue; } } // This one is as good as any... } // Move all types under the selected type foreach (var specializedTemplate in templates) { template.SpecializedTypes.Add(specializedTemplate); specializedTemplate.TemplateType = template; } yield return template; } } }
/// <summary> /// Adds template symbols to user type factory and generates template user types. /// </summary> /// <param name="symbols">The template symbols grouped around the same template type.</param> /// <param name="nameSpace">The namespace.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <returns>Generated user types for the specified symbols.</returns> internal IEnumerable <UserType> AddTemplateSymbols(IEnumerable <Symbol> symbols, string nameSpace, UserTypeGenerationFlags generationFlags) { // Bucketize template user types based on number of template arguments var buckets = new Dictionary <int, List <SpecializedTemplateUserType> >(); foreach (Symbol symbol in symbols) { UserType userType = null; // We want to ignore "empty" generic classes (for now) if (symbol.Name == null || symbol.Size == 0) { continue; } // Generate template user type SpecializedTemplateUserType templateType = new SpecializedTemplateUserType(symbol, null, nameSpace, this); if (!templateType.WronglyFormed) { List <SpecializedTemplateUserType> templates; symbol.UserType = templateType; if (!buckets.TryGetValue(templateType.AllTemplateArguments.Count, out templates)) { buckets.Add(templateType.AllTemplateArguments.Count, templates = new List <SpecializedTemplateUserType>()); } templates.Add(templateType); } if (userType != null) { yield return(userType); } } // Add newly generated types foreach (List <SpecializedTemplateUserType> templatesInBucket in buckets.Values) { // TODO: Verify that all templates in the list can be described by the same class (also do check for inner-types) // Sort Templates by Class Name. // This removes ambiguity caused by parallel type processing. // List <SpecializedTemplateUserType> templates = templatesInBucket.OrderBy(t => t.Symbol.Name.Count(c => c == '*')) .ThenBy(t => t.Symbol.Name.Count(c => c == '<')) .ThenBy(t => t.Symbol.Name).ToList(); // Select best suited type for template SpecializedTemplateUserType template = templates.First(); int bestScore = int.MaxValue; foreach (var specializedTemplate in templates) { var arguments = specializedTemplate.AllTemplateArguments; int score = 0; for (int i = 0; i < arguments.Count; i++) { var argument = arguments[i]; // Check if this is repeated type bool repeated = false; for (int j = 0; j < i && !repeated; j++) { repeated = argument == arguments[j]; } if (repeated) { score += 100; } // Check if argument is constant if (argument.Tag == CodeTypeTag.TemplateArgumentConstant) { continue; } // Check if argument is simple user type if ((argument.Tag == CodeTypeTag.Class || argument.Tag == CodeTypeTag.Structure || argument.Tag == CodeTypeTag.Union || argument.Tag == CodeTypeTag.Enum) && !argument.Name.Contains("<")) { score += 1; continue; } // Check if argments is function if (argument.Tag == CodeTypeTag.Function) { score += 2; continue; } // Check if argument is template if (argument.Name.Contains("<")) { score += 20; continue; } // All others fall into the same category score += 5; } // Check if this is the best one if (score < bestScore) { bestScore = score; template = specializedTemplate; } } yield return(new TemplateUserType(template, templates, this)); } }
/// <summary> /// Generates user types using the specified XML configuration. /// </summary> /// <param name="xmlConfig">The XML configuration.</param> /// <param name="logger">The logger text writer. If set to null, Console.Out will be used.</param> /// <param name="errorLogger">The error logger text writer. If set to null, Console.Error will be used.</param> public static void Generate(XmlConfig xmlConfig, TextWriter logger = null, TextWriter errorLogger = null) { var sw = System.Diagnostics.Stopwatch.StartNew(); XmlModule[] xmlModules = xmlConfig.Modules; XmlType[] typeNames = xmlConfig.Types; XmlIncludedFile[] includedFiles = xmlConfig.IncludedFiles; XmlReferencedAssembly[] referencedAssemblies = xmlConfig.ReferencedAssemblies; UserTypeGenerationFlags generationOptions = xmlConfig.GetGenerationFlags(); ConcurrentDictionary <string, string> generatedFiles = new ConcurrentDictionary <string, string>(); var syntaxTrees = new ConcurrentBag <SyntaxTree>(); string currentDirectory = Directory.GetCurrentDirectory(); string outputDirectory = currentDirectory + "\\output\\"; // Check logger and error logger if (errorLogger == null) { errorLogger = Console.Error; } if (logger == null) { logger = Console.Out; } // Create output directory Directory.CreateDirectory(outputDirectory); // Verify that included files exist if (!string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName)) { foreach (var file in includedFiles) { if (!File.Exists(file.Path)) { throw new FileNotFoundException("", file.Path); } } } // Loading modules ConcurrentDictionary <Module, XmlModule> modules = new ConcurrentDictionary <Module, XmlModule>(); ConcurrentDictionary <XmlModule, Symbol[]> globalTypesPerModule = new ConcurrentDictionary <XmlModule, Symbol[]>(); logger.Write("Loading modules..."); Parallel.ForEach(xmlModules, (xmlModule) => { Module module = Module.Open(xmlModule); modules.TryAdd(module, xmlModule); }); logger.WriteLine(" {0}", sw.Elapsed); // Enumerating symbols logger.Write("Enumerating symbols..."); Parallel.ForEach(modules, (mm) => { XmlModule xmlModule = mm.Value; Module module = mm.Key; string moduleName = xmlModule.Name; string nameSpace = xmlModule.Namespace; List <Symbol> symbols = new List <Symbol>(); foreach (var type in typeNames) { Symbol[] foundSymbols = module.FindGlobalTypeWildcard(type.NameWildcard); if (foundSymbols.Length == 0) { errorLogger.WriteLine("Symbol not found: {0}", type.Name); } else { symbols.AddRange(foundSymbols); } } symbols.AddRange(module.GetAllTypes()); globalTypesPerModule.TryAdd(xmlModule, symbols.ToArray()); }); List <Symbol> allSymbols = new List <Symbol>(); Symbol[][] symbolsPerModule = globalTypesPerModule.Select(ss => ss.Value).ToArray(); int maxSymbols = symbolsPerModule.Max(ss => ss.Length); for (int i = 0; i < maxSymbols; i++) { for (int j = 0; j < symbolsPerModule.Length; j++) { if (i < symbolsPerModule[j].Length) { allSymbols.Add(symbolsPerModule[j][i]); } } } logger.WriteLine(" {0}", sw.Elapsed); #if false // Initialize symbol fields and base classes logger.Write("Initializing symbol values..."); Parallel.ForEach(Partitioner.Create(allSymbols), (symbol) => { var fields = symbol.Fields; var baseClasses = symbol.BaseClasses; }); logger.WriteLine(" {0}", sw.Elapsed); #endif logger.Write("Deduplicating symbols..."); // Group duplicated symbols Dictionary <string, List <Symbol> > symbolsByName = new Dictionary <string, List <Symbol> >(); Dictionary <Symbol, List <Symbol> > duplicatedSymbols = new Dictionary <Symbol, List <Symbol> >(); foreach (var symbol in allSymbols) { List <Symbol> symbols; if (!symbolsByName.TryGetValue(symbol.Name, out symbols)) { symbolsByName.Add(symbol.Name, symbols = new List <Symbol>()); } bool found = false; foreach (var s in symbols.ToArray()) { if (s.Size != 0 && symbol.Size != 0 && s.Size != symbol.Size) { #if DEBUG logger.WriteLine("{0}!{1} ({2}) {3}!{4} ({5})", s.Module.Name, s.Name, s.Size, symbol.Module.Name, symbol.Name, symbol.Size); #endif continue; } if (s.Size == 0 && symbol.Size != 0) { List <Symbol> duplicates; if (!duplicatedSymbols.TryGetValue(s, out duplicates)) { duplicatedSymbols.Add(s, duplicates = new List <Symbol>()); } duplicatedSymbols.Remove(s); duplicates.Add(s); duplicatedSymbols.Add(symbol, duplicates); symbols.Remove(s); symbols.Add(symbol); } else { List <Symbol> duplicates; if (!duplicatedSymbols.TryGetValue(s, out duplicates)) { duplicatedSymbols.Add(s, duplicates = new List <Symbol>()); } duplicates.Add(symbol); } found = true; break; } if (!found) { symbols.Add(symbol); } } // Unlink duplicated symbols if two or more are named the same foreach (var symbols in symbolsByName.Values) { if (symbols.Count <= 1) { continue; } foreach (var s in symbols.ToArray()) { List <Symbol> duplicates; if (!duplicatedSymbols.TryGetValue(s, out duplicates)) { continue; } symbols.AddRange(duplicates); duplicatedSymbols.Remove(s); } } // Extracting deduplicated symbols Dictionary <string, Symbol[]> deduplicatedSymbols = new Dictionary <string, Symbol[]>(); Dictionary <Symbol, string> symbolNamespaces = new Dictionary <Symbol, string>(); foreach (var symbols in symbolsByName.Values) { if (symbols.Count != 1) { foreach (var s in symbols) { symbolNamespaces.Add(s, modules[s.Module].Namespace); } } else { Symbol symbol = symbols.First(); List <Symbol> duplicates; if (!duplicatedSymbols.TryGetValue(symbol, out duplicates)) { duplicates = new List <Symbol>(); } duplicates.Insert(0, symbol); deduplicatedSymbols.Add(symbol.Name, duplicates.ToArray()); foreach (var s in duplicates) { symbolNamespaces.Add(s, xmlConfig.CommonTypesNamespace); } } } var globalTypes = symbolsByName.SelectMany(s => s.Value).ToArray(); logger.WriteLine(" {0}", sw.Elapsed); logger.WriteLine(" Total symbols: {0}", globalTypesPerModule.Sum(gt => gt.Value.Length)); logger.WriteLine(" Unique symbol names: {0}", symbolsByName.Count); logger.WriteLine(" Dedupedlicated symbols: {0}", globalTypes.Length); // Initialize GlobalCache with deduplicatedSymbols GlobalCache.Update(deduplicatedSymbols); // Collecting types logger.Write("Collecting types..."); var factory = new UserTypeFactory(xmlConfig.Transformations); List <UserType> userTypes = new List <UserType>(); foreach (var module in modules.Keys) { userTypes.Add(factory.AddSymbol(module.GlobalScope, new XmlType() { Name = "ModuleGlobals" }, modules[module].Namespace, generationOptions)); } ConcurrentBag <Symbol> simpleSymbols = new ConcurrentBag <Symbol>(); Dictionary <Tuple <string, string>, List <Symbol> > templateSymbols = new Dictionary <Tuple <string, string>, List <Symbol> >(); Parallel.ForEach(Partitioner.Create(globalTypes), (symbol) => { string symbolName = symbol.Name; // TODO: Add configurable filter // if (symbolName.StartsWith("$") || symbolName.StartsWith("__vc_attributes") || symbolName.Contains("`anonymous-namespace'") || symbolName.Contains("`anonymous namespace'") || symbolName.Contains("::$") || symbolName.Contains("`")) { return; } // Do not handle template referenced arguments if (symbolName.Contains("&")) { // TODO: Convert this to function pointer return; } // TODO: For now remove all unnamed-type symbols string scopedClassName = symbol.Namespaces.Last(); if (scopedClassName.StartsWith("<") || symbolName.Contains("::<")) { return; } // Check if symbol contains template type. if (SymbolNameHelper.ContainsTemplateType(symbolName) && symbol.Tag == SymTagEnum.SymTagUDT) { List <string> namespaces = symbol.Namespaces; string className = namespaces.Last(); var symbolId = Tuple.Create(symbolNamespaces[symbol], SymbolNameHelper.CreateLookupNameForSymbol(symbol)); lock (templateSymbols) { if (templateSymbols.ContainsKey(symbolId) == false) { templateSymbols[symbolId] = new List <Symbol>() { symbol } } ; else { templateSymbols[symbolId].Add(symbol); } } // TODO: // Do not add physical types for template specialization (not now) // do if types contains static fields // nested in templates } else { simpleSymbols.Add(symbol); } }); logger.WriteLine(" {0}", sw.Elapsed); // Populate Templates logger.Write("Populating templates..."); foreach (List <Symbol> symbols in templateSymbols.Values) { Symbol symbol = symbols.First(); string symbolName = SymbolNameHelper.CreateLookupNameForSymbol(symbol); XmlType type = new XmlType() { Name = symbolName }; userTypes.AddRange(factory.AddSymbols(symbols, type, symbolNamespaces[symbol], generationOptions)); } logger.WriteLine(" {0}", sw.Elapsed); // Specialized class logger.Write("Populating specialized classes..."); foreach (Symbol symbol in simpleSymbols) { userTypes.Add(factory.AddSymbol(symbol, null, symbolNamespaces[symbol], generationOptions)); } logger.WriteLine(" {0}", sw.Elapsed); // To solve template dependencies. Update specialization arguments once all the templates has been populated. logger.Write("Updating template arguments..."); foreach (TemplateUserType templateUserType in userTypes.OfType <TemplateUserType>()) { foreach (TemplateUserType specializedTemplateUserType in templateUserType.SpecializedTypes) { if (!specializedTemplateUserType.UpdateTemplateArguments(factory)) { #if DEBUG logger.WriteLine("Template user type cannot be updated: {0}", specializedTemplateUserType.Symbol.Name); #endif } } } logger.WriteLine(" {0}", sw.Elapsed); // Post processing user types (filling DeclaredInType) logger.Write("Post processing user types..."); var namespaceTypes = factory.ProcessTypes(userTypes, symbolNamespaces).ToArray(); userTypes.AddRange(namespaceTypes); logger.WriteLine(" {0}", sw.Elapsed); // Code generation and saving it to disk logger.Write("Saving code to disk..."); if (!generationOptions.HasFlag(UserTypeGenerationFlags.SingleFileExport)) { // Generate Code Parallel.ForEach(userTypes, (symbolEntry) => { Tuple <string, string> result = GenerateCode(symbolEntry, factory, outputDirectory, errorLogger, generationOptions, generatedFiles); string text = result.Item1; string filename = result.Item2; if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName) && !string.IsNullOrEmpty(text)) { lock (syntaxTrees) { syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, path: filename, encoding: System.Text.UTF8Encoding.Default)); } } }); } else { string filename = string.Format(@"{0}\everything.exported.cs", outputDirectory); HashSet <string> usings = new HashSet <string>(); foreach (var symbolEntry in userTypes) { foreach (var u in symbolEntry.Usings) { usings.Add(u); } } generatedFiles.TryAdd(filename.ToLowerInvariant(), filename); using (StringWriter stringOutput = new StringWriter()) using (TextWriter masterOutput = !xmlConfig.DontSaveGeneratedCodeFiles ? new StreamWriter(filename, false /* append */, System.Text.Encoding.UTF8, 16 * 1024 * 1024) : TextWriter.Null) { foreach (var u in usings.OrderBy(s => s)) { masterOutput.WriteLine("using {0};", u); if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName)) { stringOutput.WriteLine("using {0};", u); } } masterOutput.WriteLine(); if (xmlConfig.GenerateAssemblyWithRoslyn) { stringOutput.WriteLine(); } ObjectPool <StringWriter> stringWriterPool = new ObjectPool <StringWriter>(() => new StringWriter()); Parallel.ForEach(userTypes, (symbolEntry) => { var output = stringWriterPool.GetObject(); output.GetStringBuilder().Clear(); GenerateCodeInSingleFile(output, symbolEntry, factory, errorLogger, generationOptions); string text = output.ToString(); if (!string.IsNullOrEmpty(text)) { lock (masterOutput) { masterOutput.WriteLine(text); if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName)) { stringOutput.WriteLine(text); } } } stringWriterPool.PutObject(output); }); if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName)) { syntaxTrees.Add(CSharpSyntaxTree.ParseText(stringOutput.ToString(), path: filename, encoding: UTF8Encoding.Default)); } } } logger.WriteLine(" {0}", sw.Elapsed); // Compiling the code string binFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName)) { List <MetadataReference> references = new List <MetadataReference> { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) }; references.AddRange(xmlConfig.ReferencedAssemblies.Select(r => MetadataReference.CreateFromFile(r.Path))); foreach (var includedFile in includedFiles) { syntaxTrees.Add(CSharpSyntaxTree.ParseText(File.ReadAllText(includedFile.Path), path: includedFile.Path, encoding: System.Text.UTF8Encoding.Default)); } CSharpCompilation compilation = CSharpCompilation.Create( Path.GetFileNameWithoutExtension(xmlConfig.GeneratedAssemblyName), syntaxTrees: syntaxTrees, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, platform: Platform.AnyCpu)); logger.WriteLine("Syntax trees: {0}", syntaxTrees.Count); string dllFilename = Path.Combine(outputDirectory, xmlConfig.GeneratedAssemblyName); string pdbFilename = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(dllFilename) + ".pdb"); using (var dllStream = new FileStream(dllFilename, FileMode.Create)) using (var pdbStream = new FileStream(pdbFilename, FileMode.Create)) { var result = compilation.Emit(dllStream, !xmlConfig.DisablePdbGeneration ? pdbStream : null); if (!result.Success) { IEnumerable <Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); errorLogger.WriteLine("Compile errors (top 1000):"); foreach (var diagnostic in failures.Take(1000)) { errorLogger.WriteLine(diagnostic); } } else { logger.WriteLine("DLL size: {0}", dllStream.Position); logger.WriteLine("PDB size: {0}", pdbStream.Position); } } logger.WriteLine("Compiling: {0}", sw.Elapsed); } // Check whether we should generate assembly if (!xmlConfig.GenerateAssemblyWithRoslyn && !string.IsNullOrEmpty(xmlConfig.GeneratedAssemblyName)) { var codeProvider = new CSharpCodeProvider(); var compilerParameters = new CompilerParameters() { IncludeDebugInformation = !xmlConfig.DisablePdbGeneration, OutputAssembly = outputDirectory + xmlConfig.GeneratedAssemblyName, }; compilerParameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.Location).ToArray()); //compilerParameters.ReferencedAssemblies.AddRange(referencedAssemblies); const string MicrosoftCSharpDll = "Microsoft.CSharp.dll"; if (!compilerParameters.ReferencedAssemblies.Cast <string>().Where(a => a.Contains(MicrosoftCSharpDll)).Any()) { compilerParameters.ReferencedAssemblies.Add(MicrosoftCSharpDll); } compilerParameters.ReferencedAssemblies.Add(Path.Combine(binFolder, "CsDebugScript.Engine.dll")); compilerParameters.ReferencedAssemblies.Add(Path.Combine(binFolder, "CsDebugScript.CommonUserTypes.dll")); var filesToCompile = generatedFiles.Values.Union(includedFiles.Select(f => f.Path)).ToArray(); var compileResult = codeProvider.CompileAssemblyFromFile(compilerParameters, filesToCompile); if (compileResult.Errors.Count > 0) { errorLogger.WriteLine("Compile errors (top 1000):"); foreach (CompilerError err in compileResult.Errors.Cast <CompilerError>().Take(1000)) { errorLogger.WriteLine(err); } } logger.WriteLine("Compiling: {0}", sw.Elapsed); } // Generating props file if (!string.IsNullOrEmpty(xmlConfig.GeneratedPropsFileName)) { using (TextWriter output = new StreamWriter(outputDirectory + xmlConfig.GeneratedPropsFileName, false /* append */, System.Text.Encoding.UTF8, 16 * 1024 * 1024)) { output.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>"); output.WriteLine(@"<Project xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">"); output.WriteLine(@" <ItemGroup>"); foreach (var file in generatedFiles.Values) { output.WriteLine(@" <Compile Include=""{0}"" />", file); } output.WriteLine(@" </ItemGroup>"); output.WriteLine(@"</Project>"); } } logger.WriteLine("Total time: {0}", sw.Elapsed); }
/// <summary> /// Writes the field code. /// </summary> /// <param name="output">The output.</param> /// <param name="indentation">The indentation.</param> /// <param name="options">The options.</param> public override void WriteFieldCode(IndentedWriter output, int indentation, UserTypeGenerationFlags options) { output.WriteLine(indentation, "{0} {1}();", FieldType, FieldName); }
/// <summary> /// Generates the constructors. /// </summary> /// <param name="generationFlags">The user type generation flags.</param> protected override IEnumerable<UserTypeConstructor> GenerateConstructors(UserTypeGenerationFlags generationFlags) { yield return new UserTypeConstructor() { ContainsFieldDefinitions = true, Static = true, }; }
/// <summary> /// Adds the symbols to user type factory and generates the user types. /// </summary> /// <param name="symbols">The template symbols grouped around the same template type.</param> /// <param name="type">The XML type description.</param> /// <param name="nameSpace">The namespace.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <returns>Generated user types for the specified symbols.</returns> internal IEnumerable <UserType> AddSymbols(IEnumerable <Symbol> symbols, XmlType type, string nameSpace, UserTypeGenerationFlags generationFlags) { if (!type.IsTemplate && symbols.Count() > 1) { throw new Exception("Type has more than one symbol for " + type.Name); } if (!type.IsTemplate) { yield return(AddSymbol(symbols.First(), type, nameSpace, generationFlags)); } else { // Bucketize template user types based on number of template arguments var buckets = new Dictionary <int, List <TemplateUserType> >(); foreach (Symbol symbol in symbols) { UserType userType = null; try { // We want to ignore "empty" generic classes (for now) if (symbol.Name == null || symbol.Size == 0) { continue; } // Generate template user type TemplateUserType templateType = new TemplateUserType(symbol, type, nameSpace, this); #if false // TODO: Verify if we want to use simple user type instead of template user type if (templateType.NumberOfTemplateArguments == 0) { // Template does not have arguments that can be used by generic // Make it specialized type userType = this.AddSymbol(symbol, null, moduleName, generationOptions); } else #endif { List <TemplateUserType> templates; symbol.UserType = templateType; if (!buckets.TryGetValue(templateType.NumberOfTemplateArguments, out templates)) { buckets.Add(templateType.NumberOfTemplateArguments, templates = new List <TemplateUserType>()); } templates.Add(templateType); } } catch (Exception ex) { // TODO: Verify if we need to add this as specialization if (ex.Message != "Wrongly formed template argument") { throw; } } if (userType != null) { yield return(userType); } } // Add newly generated types foreach (var templates in buckets.Values) { // TODO: Verify that all templates in the list can be described by the same class (also do check for inner-types) // Sort Templates by Class Name. // This removes ambiguity caused by parallel type processing. // templates.Sort((a, b) => string.Compare(a.Symbol.Name, b.Symbol.Name, StringComparison.InvariantCulture)); // Select best suited type for template TemplateUserType template = templates.First(); foreach (var specializedTemplate in templates) { var arguments = specializedTemplate.TemplateArguments; // Check if all arguments are different if (arguments.Distinct().Count() == arguments.Count()) { // Check if all arguments are simple user type bool simpleUserType = true; foreach (var argument in arguments) { var argumentSymbol = GlobalCache.GetSymbol(argument, specializedTemplate.Module); if (argumentSymbol.Tag != SymTagEnum.SymTagUDT || argumentSymbol.Name.Contains("<")) { simpleUserType = false; break; } } if (simpleUserType) { template = specializedTemplate; break; } // Check if none of the arguments is template user type bool noneIsTemplate = true; foreach (var argument in arguments) { var argumentSymbol = GlobalCache.GetSymbol(argument, specializedTemplate.Module); if (argumentSymbol.Tag == SymTagEnum.SymTagUDT && argumentSymbol.Name.Contains("<")) { noneIsTemplate = false; break; } } if (noneIsTemplate) { template = specializedTemplate; continue; } } // This one is good as any... } // Move all types under the selected type foreach (var specializedTemplate in templates) { template.SpecializedTypes.Add(specializedTemplate); specializedTemplate.TemplateType = template; } yield return(template); } } }
/// <summary> /// Writes the code for this user type to the specified output. /// </summary> /// <param name="output">The output.</param> /// <param name="error">The error text writer.</param> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="indentation">The current indentation.</param> public override void WriteCode(IndentedWriter output, TextWriter error, UserTypeFactory factory, UserTypeGenerationFlags generationFlags, int indentation = 0) { // Declared In Type with namespace if (DeclaredInType != null) { foreach (string innerClass in namespaces) { output.WriteLine(indentation, "public static partial class {0}", innerClass); output.WriteLine(indentation++, @"{{"); } } else { output.WriteLine(indentation, "namespace {0}", Namespace); output.WriteLine(indentation++, @"{{"); } // Inner types foreach (var innerType in InnerTypes) { output.WriteLine(); innerType.WriteCode(output, error, factory, generationFlags, indentation); } // Declared In Type with namespace if (DeclaredInType != null) { foreach (string innerClass in namespaces) { output.WriteLine(--indentation, "}}"); } } else { output.WriteLine(--indentation, "}}"); } }
/// <summary> /// Writes the property code to the specified output. /// </summary> /// <param name="output">The output.</param> /// <param name="indentation">The current indentation.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="firstField">if set to <c>true</c> this is the first field in the user type.</param> public virtual void WritePropertyCode(IndentedWriter output, int indentation, UserTypeGenerationFlags generationFlags, ref bool firstField) { if (string.IsNullOrEmpty(ConstantValue)) { if (generationFlags.HasFlag(UserTypeGenerationFlags.SingleLineProperty)) { if (firstField) { output.WriteLine(); firstField = false; } if (!UseUserMember && !CacheResult && generationFlags.HasFlag(UserTypeGenerationFlags.GenerateFieldTypeInfoComment) && !string.IsNullOrEmpty(FieldTypeInfoComment)) { output.WriteLine(indentation, FieldTypeInfoComment); } if (UseUserMember && CacheResult) { output.WriteLine(indentation, "public {0}{1} {2} {{ get {{ return {3}.Value; }} }}", Static ? "static " : "", FieldType, PropertyName, FieldName); } else if (CacheResult) { output.WriteLine(indentation, "public {0}{1} {2} {{ get {{ return {3}; }} }}", Static ? "static " : "", FieldType, PropertyName, FieldName); } else { output.WriteLine(indentation, "public {0}{1} {2} {{ get {{ return {3}; }} }}", Static ? "static " : "", FieldType, PropertyName, ConstructorText); } } else { output.WriteLine(); if (!UseUserMember && !CacheResult && generationFlags.HasFlag(UserTypeGenerationFlags.GenerateFieldTypeInfoComment) && !string.IsNullOrEmpty(FieldTypeInfoComment)) { output.WriteLine(indentation, FieldTypeInfoComment); } output.WriteLine(indentation, "public {0}{1} {2}", Static ? "static " : "", FieldType, PropertyName); output.WriteLine(indentation++, "{{"); output.WriteLine(indentation, "get"); output.WriteLine(indentation++, "{{"); if (UseUserMember && CacheResult) { output.WriteLine(indentation, "return {0}.Value;", FieldName); } else if (CacheResult) { output.WriteLine(indentation, "return {0};", FieldName); } else { output.WriteLine(indentation, "return {0};", ConstructorText); } output.WriteLine(--indentation, "}}"); output.WriteLine(--indentation, "}}"); } } }
/// <summary> /// Writes the code for this user type to the specified output. /// </summary> /// <param name="output">The output.</param> /// <param name="error">The error text writer.</param> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="indentation">The current indentation.</param> public override void WriteCode(IndentedWriter output, TextWriter error, UserTypeFactory factory, UserTypeGenerationFlags generationFlags, int indentation = 0) { string[] namespaces = this.namespaces; if (generationFlags.HasFlag(UserTypeGenerationFlags.GenerateNamespaceAsStaticClass)) { namespaces = NamespaceSymbol.Split(".".ToCharArray()); } // Declared In Type with namespace if (DeclaredInType != null || generationFlags.HasFlag(UserTypeGenerationFlags.GenerateNamespaceAsStaticClass)) { foreach (string innerClass in namespaces) { output.WriteLine(indentation, "public static partial class {0}", innerClass); output.WriteLine(indentation++, @"{{"); } } else { output.WriteLine(indentation, "namespace {0}", Namespace); output.WriteLine(indentation++, @"{{"); } // Inner types foreach (var innerType in InnerTypes) { output.WriteLine(); innerType.WriteCode(output, error, factory, generationFlags, indentation); } // Declared In Type with namespace if (DeclaredInType != null || generationFlags.HasFlag(UserTypeGenerationFlags.GenerateNamespaceAsStaticClass)) { foreach (string innerClass in namespaces) { output.WriteLine(--indentation, "}}"); } } else { output.WriteLine(--indentation, "}}"); } }
/// <summary> /// Generates user type field based on the specified symbol field and all other fields that are prepared for this function. /// Do not use this function directly, unless you are calling it from overridden function. /// </summary> /// <param name="field">The symbol field.</param> /// <param name="fieldType">The field tree type.</param> /// <param name="factory">The user type factory.</param> /// <param name="simpleFieldValue">The code foe "simple field value" used when creating transformation.</param> /// <param name="gettingField">The code for getting field variable.</param> /// <param name="isStatic">if set to <c>true</c> generated field should be static.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="extractingBaseClass">if set to <c>true</c> user type field is being generated for getting base class.</param> protected override UserTypeField ExtractFieldInternal(SymbolField field, TypeTree fieldType, UserTypeFactory factory, string simpleFieldValue, string gettingField, bool isStatic, UserTypeGenerationFlags generationFlags, bool extractingBaseClass) { // Physical code generation make sense only for non-static fields if (!isStatic) { bool lazyCacheUserTypeFields = generationFlags.HasFlag(UserTypeGenerationFlags.LazyCacheUserTypeFields); bool cacheUserTypeFields = generationFlags.HasFlag(UserTypeGenerationFlags.CacheUserTypeFields); bool cacheStaticUserTypeFields = generationFlags.HasFlag(UserTypeGenerationFlags.CacheStaticUserTypeFields); string constructorText = ""; string fieldName = field.Name; string fieldTypeString = fieldType.GetTypeString(); BasicTypeTree baseType = fieldType as BasicTypeTree; ArrayTypeTree codeArrayType = fieldType as ArrayTypeTree; UserTypeTree userType = fieldType as UserTypeTree; TransformationTypeTree transformationType = fieldType as TransformationTypeTree; bool isEmbedded = field.Type.Tag != CodeTypeTag.Pointer; // Specialization for basic types if (baseType != null) { if (baseType.BasicType == "string") { int charSize = field.Type.ElementType.Size; constructorText = string.Format("ReadString(GetCodeType().Module.Process, ReadPointer(memoryBuffer, memoryBufferOffset + {0}, {1}), {2})", field.Offset, field.Type.Size, charSize); } else if (baseType.BasicType != "NakedPointer") { if (field.LocationType == LocationType.BitField) { constructorText = string.Format("Read{0}(memoryBuffer, memoryBufferOffset + {1}, {2}, {3})", baseType.GetTypeString().UppercaseFirst(), field.Offset, field.Size, field.BitPosition); } else { constructorText = string.Format("Read{0}(memoryBuffer, memoryBufferOffset + {1})", baseType.GetTypeString().UppercaseFirst(), field.Offset); } } } // Specialization for arrays else if (codeArrayType != null) { if (codeArrayType.ElementType is BasicTypeTree) { baseType = (BasicTypeTree)codeArrayType.ElementType; if (baseType != null && baseType.BasicType != "string" && baseType.BasicType != "NakedPointer") { int arraySize = field.Type.Size; int elementSize = field.Type.ElementType.Size; if (baseType.BasicType == "char") { constructorText = string.Format("Read{0}Array(memoryBuffer, memoryBufferOffset + {1}, {2}, {3})", baseType.GetTypeString().UppercaseFirst(), field.Offset, arraySize / elementSize, elementSize); } else { constructorText = string.Format("Read{0}Array(memoryBuffer, memoryBufferOffset + {1}, {2})", baseType.GetTypeString().UppercaseFirst(), field.Offset, arraySize / elementSize); } fieldTypeString = baseType.GetTypeString() + "[]"; } } } // Specialization for user types else if (userType != null && !extractingBaseClass) { if (!(userType.UserType is EnumUserType)) { string thisClassCodeType; if (IsTypeUsingStaticCodeType(this)) { thisClassCodeType = ClassCodeType; } else { thisClassCodeType = "thisClass.Value.GetCodeType()"; usedThisClass = true; } // Check if type is embedded if (!isEmbedded) { // If user type is not embedded, we do have pointer inside of our memory buffer that we can read directly if (IsTypeUsingStaticCodeType(this)) { constructorText = string.Format("ReadPointer<{0}>({4}, \"{1}\", memoryBuffer, memoryBufferOffset + {2}, {3})", fieldTypeString, fieldName, field.Offset, field.Type.Size, ClassCodeType); } else { constructorText = string.Format("ReadPointer<{0}>(thisClass, \"{1}\", memoryBuffer, memoryBufferOffset + {2}, {3})", fieldTypeString, fieldName, field.Offset, field.Type.Size); usedThisClass = true; } // Do downcasting if field has vtable if (userType.UserType.Symbol.HasVTable() && userType.UserType.DerivedClasses.Count > 0) { constructorText += ".DowncastObject()"; } } else { // If user type is embedded, we can reuse memory buffer that we already have in this class string fieldAddress = string.Format("memoryBufferAddress + (ulong)(memoryBufferOffset + {0})", field.Offset); string fieldCodeType = string.Format("{0}.GetClassFieldType(\"{1}\")", thisClassCodeType, fieldName); if (IsTypeUsingStaticCodeType(userType.UserType)) { fieldCodeType = string.Format("{0}.{1}", userType.UserType.FullClassName, ClassCodeType); } else if (IsTypeUsingStaticCodeType(this)) { fieldCodeType = AddFieldCodeType(fieldName); } constructorText = string.Format("new {0}(memoryBuffer, memoryBufferOffset + {1}, memoryBufferAddress, {2}, {3}, \"{4}\")", fieldTypeString, field.Offset, fieldCodeType, fieldAddress, fieldName); } } else { // TODO: This is enum. Read how much enum base type is big and just cast to enum type... } } // Specialization for transformations else if (transformationType != null) { if (!isEmbedded) { string thisClassCodeType; if (IsTypeUsingStaticCodeType(this)) { thisClassCodeType = ClassCodeType; } else { thisClassCodeType = "thisClass.Value.GetCodeType()"; usedThisClass = true; } string fieldAddress = string.Format("memoryBufferAddress + (ulong)(memoryBufferOffset + {0})", field.Offset); string fieldVariable = string.Format("Variable.CreateNoCast({0}.GetClassFieldType(\"{1}\"), {2}, \"{1}\")", thisClassCodeType, fieldName, fieldAddress); if (transformationType.Transformation.Transformation.HasPhysicalConstructor) { fieldVariable = string.Format("{0}, memoryBuffer, memoryBufferOffset + {1}, memoryBufferAddress", fieldVariable, field.Offset); } simpleFieldValue = fieldVariable; constructorText = string.Format("new {0}({1})", fieldTypeString, fieldVariable); } } // If we found suitable physical representation, generate the field if (!string.IsNullOrEmpty(constructorText)) { return new UserTypeField() { ConstructorText = constructorText, FieldName = "_" + fieldName, FieldType = fieldTypeString, FieldTypeInfoComment = string.Format("// {0} {1};", field.Type.Name, fieldName), PropertyName = UserTypeField.GetPropertyName(field, this), Static = isStatic, UseUserMember = lazyCacheUserTypeFields, CacheResult = cacheUserTypeFields || (isStatic && cacheStaticUserTypeFields), SimpleFieldValue = simpleFieldValue, } } ; } return(base.ExtractFieldInternal(field, fieldType, factory, simpleFieldValue, gettingField, isStatic, generationFlags, extractingBaseClass)); }
/// <summary> /// Generates the code for user type in single file. /// </summary> /// <param name="output">The output text writer.</param> /// <param name="userType">The user type.</param> /// <param name="factory">The user type factory.</param> /// <param name="errorOutput">The error output.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <returns><c>true</c> if code was generated for the type; otherwise <c>false</c></returns> private static bool GenerateCodeInSingleFile(TextWriter output, UserType userType, UserTypeFactory factory, TextWriter errorOutput, UserTypeGenerationFlags generationFlags) { Symbol symbol = userType.Symbol; if (symbol != null && symbol.Tag == SymTagEnum.SymTagBaseType) { // ignore Base (Primitive) types. return(false); } if (userType.DeclaredInType != null) { return(false); } userType.WriteCode(new IndentedWriter(output, generationFlags.HasFlag(UserTypeGenerationFlags.CompressedOutput)), errorOutput, factory, generationFlags); return(true); }
/// <summary> /// Generates the code for user type and creates a file for it. /// </summary> /// <param name="userType">The user type.</param> /// <param name="factory">The user type factory.</param> /// <param name="outputDirectory">The output directory where code file will be stored.</param> /// <param name="errorOutput">The error output.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="generatedFiles">The list of already generated files.</param> /// <returns>Tuple of generated code and filename</returns> private static Tuple <string, string> GenerateCode(UserType userType, UserTypeFactory factory, string outputDirectory, TextWriter errorOutput, UserTypeGenerationFlags generationFlags, ConcurrentDictionary <string, string> generatedFiles) { Symbol symbol = userType.Symbol; if (symbol != null && symbol.Tag == SymTagEnum.SymTagBaseType) { // ignore Base (Primitive) types. return(Tuple.Create("", "")); } bool allParentsAreNamespaces = true; for (UserType parentType = userType.DeclaredInType; parentType != null && allParentsAreNamespaces; parentType = parentType.DeclaredInType) { allParentsAreNamespaces = parentType is NamespaceUserType; } if (userType is NamespaceUserType || !allParentsAreNamespaces) { return(Tuple.Create("", "")); } string classOutputDirectory = outputDirectory; string nameSpace = (userType.DeclaredInType as NamespaceUserType)?.FullClassName ?? userType.Namespace; if (!string.IsNullOrEmpty(nameSpace)) { classOutputDirectory = Path.Combine(classOutputDirectory, nameSpace.Replace(".", "\\").Replace(":", ".")); } Directory.CreateDirectory(classOutputDirectory); bool isEnum = userType is EnumUserType; string filename = string.Format(@"{0}\{1}{2}.exported.cs", classOutputDirectory, userType.ConstructorName, isEnum ? "_enum" : ""); int index = 1; while (true) { if (generatedFiles.TryAdd(filename.ToLowerInvariant(), filename)) { break; } filename = string.Format(@"{0}\{1}{2}_{3}.exported.cs", classOutputDirectory, userType.ConstructorName, isEnum ? "_enum" : "", index++); } using (TextWriter output = new StreamWriter(filename)) using (StringWriter stringOutput = new StringWriter()) { userType.WriteCode(new IndentedWriter(stringOutput, generationFlags.HasFlag(UserTypeGenerationFlags.CompressedOutput)), errorOutput, factory, generationFlags); string text = stringOutput.ToString(); output.WriteLine(text); return(Tuple.Create(text, filename)); } }
/// <summary> /// Generates the constructors. /// </summary> /// <param name="generationFlags">The user type generation flags.</param> protected override IEnumerable<UserTypeConstructor> GenerateConstructors(UserTypeGenerationFlags generationFlags) { yield return new UserTypeConstructor() { ContainsFieldDefinitions = true, Static = true, }; yield return new UserTypeConstructor() { Arguments = "Variable variable", BaseClassInitialization = string.Format("this(variable.GetBaseClass(baseClassString), Debugger.ReadMemory(variable.GetCodeType().Module.Process, variable.GetBaseClass(baseClassString).GetPointerAddress(), {0}), 0, variable.GetBaseClass(baseClassString).GetPointerAddress())", Symbol.Size), ContainsFieldDefinitions = false, Static = false, }; yield return new UserTypeConstructor() { Arguments = "Variable variable, CsDebugScript.Engine.Utility.MemoryBuffer buffer, int offset, ulong bufferAddress", BaseClassInitialization = string.Format("base(variable, buffer, offset{0}, bufferAddress)", baseClassOffset > 0 ? " + " + baseClassOffset : ""), ContainsFieldDefinitions = true, Static = false, }; yield return new UserTypeConstructor() { Arguments = "CsDebugScript.Engine.Utility.MemoryBuffer buffer, int offset, ulong bufferAddress, CodeType codeType, ulong address, string name = Variable.ComputedName, string path = Variable.UnknownPath", BaseClassInitialization = string.Format("base(buffer, offset{0}, bufferAddress, codeType, address, name, path)", baseClassOffset > 0 ? " + " + baseClassOffset : ""), ContainsFieldDefinitions = true, Static = false, }; }
/// <summary> /// Generates user type field based on the specified symbol field and all other fields that are prepared for this function. /// Do not use this function directly, unless you are calling it from overridden function. /// </summary> /// <param name="field">The symbol field.</param> /// <param name="fieldType">The field tree type.</param> /// <param name="factory">The user type factory.</param> /// <param name="simpleFieldValue">The code foe "simple field value" used when creating transformation.</param> /// <param name="gettingField">The code for getting field variable.</param> /// <param name="isStatic">if set to <c>true</c> generated field should be static.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="extractingBaseClass">if set to <c>true</c> user type field is being generated for getting base class.</param> protected override UserTypeField ExtractFieldInternal(SymbolField field, TypeTree fieldType, UserTypeFactory factory, string simpleFieldValue, string gettingField, bool isStatic, UserTypeGenerationFlags generationFlags, bool extractingBaseClass) { // Physical code generation make sense only for non-static fields if (!isStatic) { bool lazyCacheUserTypeFields = generationFlags.HasFlag(UserTypeGenerationFlags.LazyCacheUserTypeFields); bool cacheUserTypeFields = generationFlags.HasFlag(UserTypeGenerationFlags.CacheUserTypeFields); bool cacheStaticUserTypeFields = generationFlags.HasFlag(UserTypeGenerationFlags.CacheStaticUserTypeFields); string constructorText = ""; string fieldName = field.Name; string fieldTypeString = fieldType.GetTypeString(); BasicTypeTree baseType = fieldType as BasicTypeTree; ArrayTypeTree codeArrayType = fieldType as ArrayTypeTree; UserTypeTree userType = fieldType as UserTypeTree; TransformationTypeTree transformationType = fieldType as TransformationTypeTree; bool isEmbedded = field.Type.Tag != SymTagEnum.SymTagPointerType; // Specialization for basic types if (baseType != null) { if (baseType.BasicType == "string") { int charSize = field.Type.ElementType.Size; constructorText = string.Format("ReadString(GetCodeType().Module.Process, ReadPointer(memoryBuffer, memoryBufferOffset + {0}, {1}), {2})", field.Offset, field.Type.Size, charSize); } else if (baseType.BasicType != "NakedPointer") { if (field.LocationType == LocationType.BitField) constructorText = string.Format("Read{0}(memoryBuffer, memoryBufferOffset + {1}, {2}, {3})", baseType.GetTypeString().UppercaseFirst(), field.Offset, field.Size, field.BitPosition); else constructorText = string.Format("Read{0}(memoryBuffer, memoryBufferOffset + {1})", baseType.GetTypeString().UppercaseFirst(), field.Offset); } } // Specialization for arrays else if (codeArrayType != null) { if (codeArrayType.ElementType is BasicTypeTree) { baseType = (BasicTypeTree)codeArrayType.ElementType; if (baseType != null && baseType.BasicType != "string" && baseType.BasicType != "NakedPointer") { int arraySize = field.Type.Size; int elementSize = field.Type.ElementType.Size; if (baseType.BasicType == "char") constructorText = string.Format("Read{0}Array(memoryBuffer, memoryBufferOffset + {1}, {2}, {3})", baseType.GetTypeString().UppercaseFirst(), field.Offset, arraySize / elementSize, elementSize); else constructorText = string.Format("Read{0}Array(memoryBuffer, memoryBufferOffset + {1}, {2})", baseType.GetTypeString().UppercaseFirst(), field.Offset, arraySize / elementSize); fieldTypeString = baseType.GetTypeString() + "[]"; } } } // Specialization for user types else if (userType != null && !extractingBaseClass) { if (!(userType.UserType is EnumUserType)) { string thisClassCodeType; if (IsTypeUsingStaticCodeType(this)) thisClassCodeType = ClassCodeType; else { thisClassCodeType = "thisClass.Value.GetCodeType()"; usedThisClass = true; } // Check if type is embedded if (!isEmbedded) { // If user type is not embedded, we do have pointer inside of our memory buffer that we can read directly if (IsTypeUsingStaticCodeType(this)) constructorText = string.Format("ReadPointer<{0}>({4}, \"{1}\", memoryBuffer, memoryBufferOffset + {2}, {3})", fieldTypeString, fieldName, field.Offset, field.Type.Size, ClassCodeType); else { constructorText = string.Format("ReadPointer<{0}>(thisClass, \"{1}\", memoryBuffer, memoryBufferOffset + {2}, {3})", fieldTypeString, fieldName, field.Offset, field.Type.Size); usedThisClass = true; } // Do downcasting if field has vtable if (userType.UserType.Symbol.HasVTable() && userType.UserType.DerivedClasses.Count > 0) constructorText += ".DowncastObject()"; } else { // If user type is embedded, we can reuse memory buffer that we already have in this class string fieldAddress = string.Format("memoryBufferAddress + (ulong)(memoryBufferOffset + {0})", field.Offset); string fieldCodeType = string.Format("{0}.GetClassFieldType(\"{1}\")", thisClassCodeType, fieldName); if (IsTypeUsingStaticCodeType(userType.UserType)) { fieldCodeType = string.Format("{0}.{1}", userType.UserType.FullClassName, ClassCodeType); } else if (IsTypeUsingStaticCodeType(this)) { fieldCodeType = AddFieldCodeType(fieldName); } constructorText = string.Format("new {0}(memoryBuffer, memoryBufferOffset + {1}, memoryBufferAddress, {2}, {3}, \"{4}\")", fieldTypeString, field.Offset, fieldCodeType, fieldAddress, fieldName); } } else { // TODO: This is enum. Read how much enum base type is big and just cast to enum type... } } // Specialization for transformations else if (transformationType != null) { if (!isEmbedded) { string thisClassCodeType; if (IsTypeUsingStaticCodeType(this)) thisClassCodeType = ClassCodeType; else { thisClassCodeType = "thisClass.Value.GetCodeType()"; usedThisClass = true; } string fieldAddress = string.Format("memoryBufferAddress + (ulong)(memoryBufferOffset + {0})", field.Offset); string fieldVariable = string.Format("Variable.CreateNoCast({0}.GetClassFieldType(\"{1}\"), {2}, \"{1}\")", thisClassCodeType, fieldName, fieldAddress); if (transformationType.Transformation.Transformation.HasPhysicalConstructor) { fieldVariable = string.Format("{0}, memoryBuffer, memoryBufferOffset + {1}, memoryBufferAddress", fieldVariable, field.Offset); } simpleFieldValue = fieldVariable; constructorText = string.Format("new {0}({1})", fieldTypeString, fieldVariable); } } // If we found suitable physical representation, generate the field if (!string.IsNullOrEmpty(constructorText)) return new UserTypeField() { ConstructorText = constructorText, FieldName = "_" + fieldName, FieldType = fieldTypeString, FieldTypeInfoComment = string.Format("// {0} {1};", field.Type.Name, fieldName), PropertyName = UserTypeField.GetPropertyName(fieldName, this), Static = isStatic, UseUserMember = lazyCacheUserTypeFields, CacheResult = cacheUserTypeFields || (isStatic && cacheStaticUserTypeFields), SimpleFieldValue = simpleFieldValue, }; } return base.ExtractFieldInternal(field, fieldType, factory, simpleFieldValue, gettingField, isStatic, generationFlags, extractingBaseClass); }
/// <summary> /// Writes the code for this user type to the specified output. /// </summary> /// <param name="output">The output.</param> /// <param name="error">The error text writer.</param> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="indentation">The current indentation.</param> public override void WriteCode(IndentedWriter output, TextWriter error, UserTypeFactory factory, UserTypeGenerationFlags generationFlags, int indentation = 0) { // Check if we need to write namespace string nameSpace = (DeclaredInType as NamespaceUserType)?.FullClassName ?? Namespace; string enumBasicType = GetEnumBasicType(Symbol); if ((DeclaredInType == null || (!generationFlags.HasFlag(UserTypeGenerationFlags.SingleFileExport) && DeclaredInType is NamespaceUserType)) && !string.IsNullOrEmpty(nameSpace)) { output.WriteLine(indentation, "namespace {0}", nameSpace); output.WriteLine(indentation++, "{{"); } // Write beginning of the enumeration if (generationFlags.HasFlag(UserTypeGenerationFlags.GenerateFieldTypeInfoComment)) { output.WriteLine(indentation, "// {0} (original name: {1})", ClassName, Symbol.Name); } if (AreValuesFlags()) { output.WriteLine(indentation, @"[System.Flags]"); } if (Symbol.Size != 0) { output.WriteLine(indentation, @"public enum {0} : {1}", ClassName, enumBasicType); } else { output.WriteLine(indentation, @"public enum {0}", ClassName); } output.WriteLine(indentation++, @"{{"); // Write values foreach (var enumValue in Symbol.EnumValues) { string value = enumValue.Item2; if (!FitsBasicType(enumBasicType, ref value)) { output.WriteLine(indentation, "{0} = ({1}){2},", enumValue.Item1, enumBasicType, value); } else { output.WriteLine(indentation, "{0} = {1},", enumValue.Item1, value); } } // Enumeration end output.WriteLine(--indentation, @"}}"); if ((DeclaredInType == null || (!generationFlags.HasFlag(UserTypeGenerationFlags.SingleFileExport) && DeclaredInType is NamespaceUserType)) && !string.IsNullOrEmpty(nameSpace)) { output.WriteLine(--indentation, "}}"); } }
/// <summary> /// Gets the user type generation flags. /// </summary> internal UserTypeGenerationFlags GetGenerationFlags() { UserTypeGenerationFlags generationFlags = UserTypeGenerationFlags.None; if (CompressedOutput) { generationFlags |= UserTypeGenerationFlags.CompressedOutput; DontGenerateFieldTypeInfoComment = true; MultiLineProperties = false; } if (!DontGenerateFieldTypeInfoComment) { generationFlags |= UserTypeGenerationFlags.GenerateFieldTypeInfoComment; } if (!MultiLineProperties) { generationFlags |= UserTypeGenerationFlags.SingleLineProperty; } if (UseDirectClassAccess) { generationFlags |= UserTypeGenerationFlags.UseDirectClassAccess; } if (ForceUserTypesToNewInsteadOfCasting) { generationFlags |= UserTypeGenerationFlags.ForceUserTypesToNewInsteadOfCasting; } if (CacheUserTypeFields) { generationFlags |= UserTypeGenerationFlags.CacheUserTypeFields; } if (CacheStaticUserTypeFields) { generationFlags |= UserTypeGenerationFlags.CacheStaticUserTypeFields; } if (LazyCacheUserTypeFields) { generationFlags |= UserTypeGenerationFlags.LazyCacheUserTypeFields; } if (GeneratePhysicalMappingOfUserTypes) { generationFlags |= UserTypeGenerationFlags.GeneratePhysicalMappingOfUserTypes; } if (!MultiFileExport) { generationFlags |= UserTypeGenerationFlags.SingleFileExport; } if (UseHungarianNotation) { generationFlags |= UserTypeGenerationFlags.UseHungarianNotation; } if (GenerateNamespaceAsStaticClass) { generationFlags |= UserTypeGenerationFlags.GenerateNamespaceAsStaticClass; } if (DontSaveGeneratedCodeFiles && GenerateAssemblyWithRoslyn) { generationFlags |= UserTypeGenerationFlags.DontSaveGeneratedCodeFiles; } if (InitializeSymbolCaches) { generationFlags |= UserTypeGenerationFlags.InitializeSymbolCaches; } return(generationFlags); }
/// <summary> /// Extracts all fields from the user type. /// </summary> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> protected override IEnumerable <UserTypeField> ExtractFields(UserTypeFactory factory, UserTypeGenerationFlags generationFlags) { ExportStaticFields = true; var fields = Symbol.Fields.OrderBy(s => s.Name).ToArray(); bool useThisClass = generationFlags.HasFlag(UserTypeGenerationFlags.UseClassFieldsFromDiaSymbolProvider); string previousName = ""; foreach (var field in fields) { if (string.IsNullOrEmpty(field.Type.Name)) { continue; } if (IsFieldFiltered(field) || field.Name == previousName) { continue; } if (field.Name.Contains("@")) { // Skip names contaings '@' continue; } // Skip fields that are actual values of enum values if (field.Type.Tag == Dia2Lib.SymTagEnum.SymTagEnum && field.Type.GetEnumValues().Any(t => t.Item1 == field.Name)) { continue; } var userField = ExtractField(field, factory, generationFlags, forceIsStatic: true); if (field.Type.Tag == Dia2Lib.SymTagEnum.SymTagPointerType) { // Do not use const values for pointers. // We do not allow user type implicit conversion from integers. userField.ConstantValue = string.Empty; } userField.FieldName = NormalizeSymbolNamespace(userField.FieldName); userField.PropertyName = NormalizeSymbolNamespace(userField.PropertyName); yield return(userField); previousName = field.Name; } foreach (var field in GetAutoGeneratedFields(false, useThisClass)) { yield return(field); } }
/// <summary> /// Writes the property code. /// </summary> /// <param name="output">The output.</param> /// <param name="indentation">The indentation.</param> /// <param name="options">The options.</param> /// <param name="firstField">if set to <c>true</c> [first field].</param> public override void WritePropertyCode(IndentedWriter output, int indentation, UserTypeGenerationFlags options, ref bool firstField) { // Do nothing }
/// <summary> /// Initializes a new instance of the <see cref="DotNetCodeWriter"/> class. /// </summary> /// <param name="generationFlags">The code generation options</param> /// <param name="nameLimit">Maximum number of characters that generated name can have.</param> /// <param name="fixKeywordsInUserNaming">Should we fix keywords by adding @ when fixing user naming?</param> public DotNetCodeWriter(UserTypeGenerationFlags generationFlags, int nameLimit, bool fixKeywordsInUserNaming) { GenerationFlags = generationFlags; NameLimit = nameLimit; FixKeywordsInUserNaming = fixKeywordsInUserNaming; }
/// <summary> /// Writes the code for this user type to the specified output. /// </summary> /// <param name="output">The output.</param> /// <param name="error">The error text writer.</param> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> /// <param name="indentation">The current indentation.</param> public override void WriteCode(IndentedWriter output, TextWriter error, UserTypeFactory factory, UserTypeGenerationFlags generationFlags, int indentation = 0) { // Declared In Type with namespace if (DeclaredInType != null) { foreach (string innerClass in namespaces) { output.WriteLine(indentation, "public static partial class {0}", innerClass); output.WriteLine(indentation++, @"{{"); } } else { output.WriteLine(indentation, "namespace {0}", Namespace); output.WriteLine(indentation++, @"{{"); } // Inner types foreach (var innerType in InnerTypes) { output.WriteLine(); innerType.WriteCode(output, error, factory, generationFlags, indentation); } // Declared In Type with namespace if (DeclaredInType != null) foreach (string innerClass in namespaces) output.WriteLine(--indentation, "}}"); else output.WriteLine(--indentation, "}}"); }
/// <summary> /// Extracts all fields from the user type. /// </summary> /// <param name="factory">The user type factory.</param> /// <param name="generationFlags">The user type generation flags.</param> protected override IEnumerable <UserTypeField> ExtractFields(UserTypeFactory factory, UserTypeGenerationFlags generationFlags) { ExportStaticFields = true; var fields = Symbol.Fields.OrderBy(s => s.Name).ToArray(); bool useThisClass = generationFlags.HasFlag(UserTypeGenerationFlags.UseDirectClassAccess); HashSet <string> usedNames = new HashSet <string>(); foreach (var field in fields) { if (string.IsNullOrEmpty(field.Type.Name)) { continue; } if (IsFieldFiltered(field)) { continue; } field.PropertyName = NormalizeSymbolNamespace(UserTypeField.GetPropertyName(field, this)); while (usedNames.Contains(field.PropertyName)) { field.PropertyName += "_"; } if (field.Name.Contains("@") || field.PropertyName.Length > 511) { // Skip names containing '@' continue; } // Skip fields that are actual values of enum values if (field.Type.Tag == CodeTypeTag.Enum && field.Type.EnumValues.Any(t => t.Item1 == field.Name)) { continue; } var userField = ExtractField(field, factory, generationFlags, forceIsStatic: true); if (field.Type.Tag == CodeTypeTag.Pointer) { // Do not use const values for pointers. // We do not allow user type implicit conversion from integers. userField.ConstantValue = string.Empty; } userField.FieldName = NormalizeSymbolNamespace(userField.FieldName); userField.PropertyName = NormalizeSymbolNamespace(userField.PropertyName); yield return(userField); usedNames.Add(field.PropertyName); } foreach (var field in GetAutoGeneratedFields(false, useThisClass)) { yield return(field); } }