"Fossies" - the Fresh Open Source Software Archive

Member "PowerShell-7.2.6/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs" (11 Aug 2022, 53388 Bytes) of package /linux/misc/PowerShell-7.2.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C# source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "AddType.cs" see the Fossies "Dox" file reference documentation.

    1 // Copyright (c) Microsoft Corporation.
    2 // Licensed under the MIT License.
    3 
    4 using System;
    5 using System.Collections.Concurrent;
    6 using System.Collections.Generic;
    7 using System.Collections.Immutable;
    8 using System.Collections.ObjectModel;
    9 using System.Globalization;
   10 using System.IO;
   11 using System.Linq;
   12 using System.Management.Automation;
   13 using System.Management.Automation.Internal;
   14 using System.Management.Automation.Security;
   15 using System.Reflection;
   16 using System.Runtime.Loader;
   17 using System.Security;
   18 using System.Text;
   19 
   20 using Microsoft.CodeAnalysis;
   21 using Microsoft.CodeAnalysis.CSharp;
   22 using Microsoft.CodeAnalysis.Emit;
   23 using Microsoft.CodeAnalysis.Text;
   24 using PathType = System.IO.Path;
   25 
   26 namespace Microsoft.PowerShell.Commands
   27 {
   28     /// <summary>
   29     /// Languages supported for code generation.
   30     /// </summary>
   31     public enum Language
   32     {
   33         /// <summary>
   34         /// The C# programming language.
   35         /// </summary>
   36         CSharp
   37     }
   38 
   39     /// <summary>
   40     /// Types supported for the OutputAssembly parameter.
   41     /// </summary>
   42     public enum OutputAssemblyType
   43     {
   44         /// <summary>
   45         /// A Dynamically linked library (DLL).
   46         /// </summary>
   47         Library,
   48 
   49         /// <summary>
   50         /// An executable application that targets the console subsystem.
   51         /// </summary>
   52         ConsoleApplication,
   53 
   54         /// <summary>
   55         /// An executable application that targets the graphical subsystem.
   56         /// </summary>
   57         WindowsApplication
   58     }
   59 
   60     /// <summary>
   61     /// Adds a new type to the Application Domain.
   62     /// This version is based on CodeAnalysis (Roslyn).
   63     /// </summary>
   64     [Cmdlet(VerbsCommon.Add, "Type", DefaultParameterSetName = FromSourceParameterSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096601")]
   65     [OutputType(typeof(Type))]
   66     public sealed class AddTypeCommand : PSCmdlet
   67     {
   68         #region Parameters
   69 
   70         /// <summary>
   71         /// The source code of this generated type.
   72         /// </summary>
   73         [Parameter(Mandatory = true, Position = 0, ParameterSetName = FromSourceParameterSetName)]
   74         [ValidateTrustedData]
   75         public string TypeDefinition
   76         {
   77             get
   78             {
   79                 return _sourceCode;
   80             }
   81 
   82             set
   83             {
   84                 _sourceCode = value;
   85             }
   86         }
   87 
   88         /// <summary>
   89         /// The name of the type (class) used for auto-generated types.
   90         /// </summary>
   91         [Parameter(Mandatory = true, Position = 0, ParameterSetName = FromMemberParameterSetName)]
   92         [ValidateTrustedData]
   93         public string Name { get; set; }
   94 
   95         /// <summary>
   96         /// The source code of this generated method / member.
   97         /// </summary>
   98         [Parameter(Mandatory = true, Position = 1, ParameterSetName = FromMemberParameterSetName)]
   99         public string[] MemberDefinition
  100         {
  101             get
  102             {
  103                 return new string[] { _sourceCode };
  104             }
  105 
  106             set
  107             {
  108                 _sourceCode = string.Empty;
  109 
  110                 if (value != null)
  111                 {
  112                     _sourceCode = string.Join("\n", value);
  113                 }
  114             }
  115         }
  116 
  117         private string _sourceCode;
  118 
  119         /// <summary>
  120         /// The namespace used for the auto-generated type.
  121         /// </summary>
  122         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  123         [AllowNull]
  124         [Alias("NS")]
  125         public string Namespace { get; set; } = "Microsoft.PowerShell.Commands.AddType.AutoGeneratedTypes";
  126 
  127         /// <summary>
  128         /// Any using statements required by the auto-generated type.
  129         /// </summary>
  130         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  131         [ValidateNotNull()]
  132         [Alias("Using")]
  133         public string[] UsingNamespace { get; set; } = Array.Empty<string>();
  134 
  135         /// <summary>
  136         /// The path to the source code or DLL to load.
  137         /// </summary>
  138         [Parameter(Mandatory = true, Position = 0, ParameterSetName = FromPathParameterSetName)]
  139         [ValidateTrustedData]
  140         public string[] Path
  141         {
  142             get
  143             {
  144                 return _paths;
  145             }
  146 
  147             set
  148             {
  149                 if (value == null)
  150                 {
  151                     _paths = null;
  152                     return;
  153                 }
  154 
  155                 string[] pathValue = value;
  156 
  157                 List<string> resolvedPaths = new();
  158 
  159                 // Verify that the paths are resolved and valid
  160                 foreach (string path in pathValue)
  161                 {
  162                     // Try to resolve the path
  163                     Collection<string> newPaths = SessionState.Path.GetResolvedProviderPathFromPSPath(path, out ProviderInfo _);
  164 
  165                     // If it didn't resolve, add the original back
  166                     // for a better error message.
  167                     if (newPaths.Count == 0)
  168                     {
  169                         resolvedPaths.Add(path);
  170                     }
  171                     else
  172                     {
  173                         resolvedPaths.AddRange(newPaths);
  174                     }
  175                 }
  176 
  177                 ProcessPaths(resolvedPaths);
  178             }
  179         }
  180 
  181         /// <summary>
  182         /// The literal path to the source code or DLL to load.
  183         /// </summary>
  184         [Parameter(Mandatory = true, ParameterSetName = FromLiteralPathParameterSetName)]
  185         [Alias("PSPath", "LP")]
  186         [ValidateTrustedData]
  187         public string[] LiteralPath
  188         {
  189             get
  190             {
  191                 return _paths;
  192             }
  193 
  194             set
  195             {
  196                 if (value == null)
  197                 {
  198                     _paths = null;
  199                     return;
  200                 }
  201 
  202                 List<string> resolvedPaths = new();
  203                 foreach (string path in value)
  204                 {
  205                     string literalPath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(path);
  206                     resolvedPaths.Add(literalPath);
  207                 }
  208 
  209                 ProcessPaths(resolvedPaths);
  210             }
  211         }
  212 
  213         private void ProcessPaths(List<string> resolvedPaths)
  214         {
  215             // Validate file extensions.
  216             // Make sure we don't mix source files from different languages (if we support any other languages in future).
  217             string activeExtension = null;
  218             foreach (string path in resolvedPaths)
  219             {
  220                 string currentExtension = PathType.GetExtension(path).ToUpperInvariant();
  221 
  222                 switch (currentExtension)
  223                 {
  224                     case ".CS":
  225                         Language = Language.CSharp;
  226                         break;
  227 
  228                     case ".DLL":
  229                         _loadAssembly = true;
  230                         break;
  231 
  232                     // Throw an error if it is an unrecognized extension
  233                     default:
  234                         ErrorRecord errorRecord = new(
  235                             new Exception(
  236                                 StringUtil.Format(AddTypeStrings.FileExtensionNotSupported, currentExtension)),
  237                             "EXTENSION_NOT_SUPPORTED",
  238                             ErrorCategory.InvalidArgument,
  239                             currentExtension);
  240 
  241                         ThrowTerminatingError(errorRecord);
  242                         break;
  243                 }
  244 
  245                 if (activeExtension == null)
  246                 {
  247                     activeExtension = currentExtension;
  248                 }
  249                 else if (!string.Equals(activeExtension, currentExtension, StringComparison.OrdinalIgnoreCase))
  250                 {
  251                     // All files must have the same extension otherwise throw.
  252                     ErrorRecord errorRecord = new(
  253                         new Exception(
  254                             StringUtil.Format(AddTypeStrings.MultipleExtensionsNotSupported)),
  255                         "MULTIPLE_EXTENSION_NOT_SUPPORTED",
  256                         ErrorCategory.InvalidArgument,
  257                         currentExtension);
  258 
  259                     ThrowTerminatingError(errorRecord);
  260                 }
  261             }
  262 
  263             _paths = resolvedPaths.ToArray();
  264         }
  265 
  266         private string[] _paths;
  267 
  268         /// <summary>
  269         /// The name of the assembly to load.
  270         /// </summary>
  271         [Parameter(Mandatory = true, ParameterSetName = FromAssemblyNameParameterSetName)]
  272         [Alias("AN")]
  273         [ValidateTrustedData]
  274         public string[] AssemblyName { get; set; }
  275 
  276         private bool _loadAssembly = false;
  277 
  278         /// <summary>
  279         /// The language used to compile the source code.
  280         /// Default is C#.
  281         /// </summary>
  282         [Parameter(ParameterSetName = FromSourceParameterSetName)]
  283         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  284         public Language Language { get; set; } = Language.CSharp;
  285 
  286         /// <summary>
  287         /// Any reference DLLs to use in the compilation.
  288         /// </summary>
  289         [Parameter(ParameterSetName = FromSourceParameterSetName)]
  290         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  291         [Parameter(ParameterSetName = FromPathParameterSetName)]
  292         [Parameter(ParameterSetName = FromLiteralPathParameterSetName)]
  293         [Alias("RA")]
  294         public string[] ReferencedAssemblies
  295         {
  296             get
  297             {
  298                 return _referencedAssemblies;
  299             }
  300 
  301             set
  302             {
  303                 if (value != null) { _referencedAssemblies = value; }
  304             }
  305         }
  306 
  307         private string[] _referencedAssemblies = Array.Empty<string>();
  308 
  309         /// <summary>
  310         /// The path to the output assembly.
  311         /// </summary>
  312         [Parameter(ParameterSetName = FromSourceParameterSetName)]
  313         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  314         [Parameter(ParameterSetName = FromPathParameterSetName)]
  315         [Parameter(ParameterSetName = FromLiteralPathParameterSetName)]
  316         [Alias("OA")]
  317         public string OutputAssembly
  318         {
  319             get
  320             {
  321                 return _outputAssembly;
  322             }
  323 
  324             set
  325             {
  326                 _outputAssembly = value;
  327 
  328                 if (_outputAssembly != null)
  329                 {
  330                     _outputAssembly = _outputAssembly.Trim();
  331 
  332                     // Try to resolve the path
  333                     ProviderInfo provider = null;
  334                     Collection<string> newPaths = new();
  335 
  336                     try
  337                     {
  338                         newPaths = SessionState.Path.GetResolvedProviderPathFromPSPath(_outputAssembly, out provider);
  339                     }
  340                     // Ignore the ItemNotFound -- we handle it.
  341                     catch (ItemNotFoundException) { }
  342 
  343                     ErrorRecord errorRecord = new(
  344                         new Exception(
  345                             StringUtil.Format(AddTypeStrings.OutputAssemblyDidNotResolve, _outputAssembly)),
  346                         "INVALID_OUTPUT_ASSEMBLY",
  347                         ErrorCategory.InvalidArgument,
  348                         _outputAssembly);
  349 
  350                     // If it resolved to a non-standard provider,
  351                     // generate an error.
  352                     if (!string.Equals("FileSystem", provider.Name, StringComparison.OrdinalIgnoreCase))
  353                     {
  354                         ThrowTerminatingError(errorRecord);
  355                         return;
  356                     }
  357 
  358                     // If it resolved to more than one path,
  359                     // generate an error.
  360                     if (newPaths.Count > 1)
  361                     {
  362                         ThrowTerminatingError(errorRecord);
  363                         return;
  364                     }
  365                     // It didn't resolve to any files. They may
  366                     // want to create the file.
  367                     else if (newPaths.Count == 0)
  368                     {
  369                         // We can't create one with wildcard characters
  370                         if (WildcardPattern.ContainsWildcardCharacters(_outputAssembly))
  371                         {
  372                             ThrowTerminatingError(errorRecord);
  373                         }
  374                         // Create the file
  375                         else
  376                         {
  377                             _outputAssembly = SessionState.Path.GetUnresolvedProviderPathFromPSPath(_outputAssembly);
  378                         }
  379                     }
  380                     // It resolved to a single file
  381                     else
  382                     {
  383                         _outputAssembly = newPaths[0];
  384                     }
  385                 }
  386             }
  387         }
  388 
  389         private string _outputAssembly = null;
  390 
  391         /// <summary>
  392         /// The output type of the assembly.
  393         /// </summary>
  394         [Parameter(ParameterSetName = FromSourceParameterSetName)]
  395         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  396         [Parameter(ParameterSetName = FromPathParameterSetName)]
  397         [Parameter(ParameterSetName = FromLiteralPathParameterSetName)]
  398         [Alias("OT")]
  399         public OutputAssemblyType OutputType { get; set; } = OutputAssemblyType.Library;
  400 
  401         /// <summary>
  402         /// Flag to pass the resulting types along.
  403         /// </summary>
  404         [Parameter()]
  405         public SwitchParameter PassThru { get; set; }
  406 
  407         /// <summary>
  408         /// Flag to ignore warnings during compilation.
  409         /// </summary>
  410         [Parameter(ParameterSetName = FromSourceParameterSetName)]
  411         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  412         [Parameter(ParameterSetName = FromPathParameterSetName)]
  413         [Parameter(ParameterSetName = FromLiteralPathParameterSetName)]
  414         public SwitchParameter IgnoreWarnings { get; set; }
  415 
  416         /// <summary>
  417         /// Roslyn command line parameters.
  418         /// https://github.com/dotnet/roslyn/blob/master/docs/compilers/CSharp/CommandLine.md
  419         ///
  420         /// Parser options:
  421         ///     langversion:string - language version from:
  422         ///                                 [enum]::GetNames([Microsoft.CodeAnalysis.CSharp.LanguageVersion])
  423         ///     define:symbol list - preprocessor symbols:
  424         ///                                 /define:UNIX,DEBUG      - CSharp
  425         ///
  426         /// Compilation options:
  427         ///     optimize{+|-}            - optimization level
  428         ///     parallel{+|-}            - concurrent build
  429         ///     warnaserror{+|-}         - report warnings to errors
  430         ///     warnaserror{+|-}:strings - report specific warnings to errors
  431         ///     warn:number              - warning level (0-4) for CSharp
  432         ///     nowarn                   - disable all warnings
  433         ///     nowarn:strings           - disable a list of individual warnings
  434         ///     usings:strings           - ';'-delimited usings for CSharp
  435         ///
  436         /// Emit options:
  437         ///     platform:string          - limit which platforms this code can run on; must be x86, x64, Itanium, arm, AnyCPU32BitPreferred or anycpu (default)
  438         ///     delaysign{+|-}           - delay-sign the assembly using only the public portion of the strong name key
  439         ///     keyfile:file             - specifies a strong name key file
  440         ///     keycontainer:string      - specifies a strong name key container
  441         ///     highentropyva{+|-}       - enable high-entropy ASLR.
  442         /// </summary>
  443         [Parameter(ParameterSetName = FromSourceParameterSetName)]
  444         [Parameter(ParameterSetName = FromMemberParameterSetName)]
  445         [Parameter(ParameterSetName = FromPathParameterSetName)]
  446         [Parameter(ParameterSetName = FromLiteralPathParameterSetName)]
  447         [ValidateNotNullOrEmpty]
  448         public string[] CompilerOptions { get; set; }
  449 
  450         #endregion Parameters
  451 
  452         #region GererateSource
  453 
  454         private string GenerateTypeSource(string typeNamespace, string typeName, string sourceCodeText, Language language)
  455         {
  456             string usingSource = string.Format(
  457                     CultureInfo.InvariantCulture,
  458                     GetUsingTemplate(language), GetUsingSet(language));
  459 
  460             string typeSource = string.Format(
  461                     CultureInfo.InvariantCulture,
  462                     GetMethodTemplate(language), typeName, sourceCodeText);
  463 
  464             if (!string.IsNullOrEmpty(typeNamespace))
  465             {
  466                 return usingSource + string.Format(
  467                     CultureInfo.InvariantCulture,
  468                     GetNamespaceTemplate(language), typeNamespace, typeSource);
  469             }
  470             else
  471             {
  472                 return usingSource + typeSource;
  473             }
  474         }
  475 
  476         // Get the -FromMember template for a given language
  477         private static string GetMethodTemplate(Language language)
  478         {
  479             switch (language)
  480             {
  481                 case Language.CSharp:
  482                     return
  483                         "    public class {0}\n" +
  484                         "    {{\n" +
  485                         "    {1}\n" +
  486                         "    }}\n";
  487             }
  488 
  489             throw PSTraceSource.NewNotSupportedException();
  490         }
  491 
  492         // Get the -FromMember namespace template for a given language
  493         private static string GetNamespaceTemplate(Language language)
  494         {
  495             switch (language)
  496             {
  497                 case Language.CSharp:
  498                     return
  499                         "namespace {0}\n" +
  500                         "{{\n" +
  501                         "{1}\n" +
  502                         "}}\n";
  503             }
  504 
  505             throw PSTraceSource.NewNotSupportedException();
  506         }
  507 
  508         // Get the -FromMember namespace template for a given language
  509         private static string GetUsingTemplate(Language language)
  510         {
  511             switch (language)
  512             {
  513                 case Language.CSharp:
  514                     return
  515                         "using System;\n" +
  516                         "using System.Runtime.InteropServices;\n" +
  517                         "{0}" +
  518                         "\n";
  519             }
  520 
  521             throw PSTraceSource.NewNotSupportedException();
  522         }
  523 
  524         // Generate the code for the using statements
  525         private string GetUsingSet(Language language)
  526         {
  527             StringBuilder usingNamespaceSet = new();
  528 
  529             switch (language)
  530             {
  531                 case Language.CSharp:
  532                     foreach (string namespaceValue in UsingNamespace)
  533                     {
  534                         usingNamespaceSet.Append("using " + namespaceValue + ";\n");
  535                     }
  536 
  537                     break;
  538 
  539                 default:
  540                     throw PSTraceSource.NewNotSupportedException();
  541             }
  542 
  543             return usingNamespaceSet.ToString();
  544         }
  545 
  546         #endregion GererateSource
  547 
  548         /// <summary>
  549         /// Prevent code compilation in ConstrainedLanguage mode.
  550         /// </summary>
  551         protected override void BeginProcessing()
  552         {
  553             // Prevent code compilation in ConstrainedLanguage mode, or NoLanguage mode under system lock down.
  554             if (SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage ||
  555                 (SessionState.LanguageMode == PSLanguageMode.NoLanguage && 
  556                  SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce))
  557             {
  558                 ThrowTerminatingError(
  559                     new ErrorRecord(
  560                         new PSNotSupportedException(AddTypeStrings.CannotDefineNewType),
  561                         nameof(AddTypeStrings.CannotDefineNewType),
  562                         ErrorCategory.PermissionDenied,
  563                         targetObject: null));
  564             }
  565 
  566             // 'ConsoleApplication' and 'WindowsApplication' types are currently not working in .NET Core
  567             if (OutputType != OutputAssemblyType.Library)
  568             {
  569                 ThrowTerminatingError(
  570                     new ErrorRecord(
  571                         new PSNotSupportedException(AddTypeStrings.AssemblyTypeNotSupported),
  572                         nameof(AddTypeStrings.AssemblyTypeNotSupported),
  573                         ErrorCategory.NotImplemented,
  574                         targetObject: OutputType));
  575             }
  576         }
  577 
  578         /// <summary>
  579         /// Generate and load the type(s).
  580         /// </summary>
  581         protected override void EndProcessing()
  582         {
  583             // Generate an error if they've specified an output
  584             // assembly type without an output assembly
  585             if (string.IsNullOrEmpty(_outputAssembly) && this.MyInvocation.BoundParameters.ContainsKey(nameof(OutputType)))
  586             {
  587                 ErrorRecord errorRecord = new(
  588                     new Exception(
  589                         string.Format(
  590                             CultureInfo.CurrentCulture,
  591                             AddTypeStrings.OutputTypeRequiresOutputAssembly)),
  592                     "OUTPUTTYPE_REQUIRES_ASSEMBLY",
  593                     ErrorCategory.InvalidArgument,
  594                     OutputType);
  595 
  596                 ThrowTerminatingError(errorRecord);
  597                 return;
  598             }
  599 
  600             if (_loadAssembly)
  601             {
  602                 // File extension is ".DLL" (ParameterSetName = FromPathParameterSetName or FromLiteralPathParameterSetName).
  603                 LoadAssemblies(_paths);
  604             }
  605             else if (ParameterSetName == FromAssemblyNameParameterSetName)
  606             {
  607                 LoadAssemblies(AssemblyName);
  608             }
  609             else
  610             {
  611                 // Process a source code from files or strings.
  612                 SourceCodeProcessing();
  613             }
  614         }
  615 
  616         #region LoadAssembly
  617 
  618         // We now ship .NET Core's reference assemblies with PowerShell, so that Add-Type can work
  619         // in a predictable way and won't be broken when we move to newer version of .NET Core.
  620         // The reference assemblies are located at '$PSHOME\ref' for pwsh.
  621         //
  622         // For applications that host PowerShell, the 'ref' folder will be deployed to the 'publish'
  623         // folder, not where 'System.Management.Automation.dll' is located. So here we should use
  624         // the entry assembly's location to construct the path to the 'ref' folder.
  625         // For pwsh, the entry assembly is 'pwsh.dll', so the entry assembly's location is still
  626         // $PSHOME.
  627         // However, 'Assembly.GetEntryAssembly()' returns null when the managed code is called from
  628         // unmanaged code (PowerShell WSMan remoting scenario), so in that case, we continue to use
  629         // the location of 'System.Management.Automation.dll'.
  630         private static readonly string s_netcoreAppRefFolder = PathType.Combine(
  631             PathType.GetDirectoryName(
  632                 (Assembly.GetEntryAssembly() ?? typeof(PSObject).Assembly).Location),
  633             "ref");
  634 
  635         // Path to the folder where .NET Core runtime assemblies are located.
  636         private static readonly string s_frameworkFolder = PathType.GetDirectoryName(typeof(object).Assembly.Location);
  637 
  638         // These assemblies are always automatically added to ReferencedAssemblies.
  639         private static readonly Lazy<PortableExecutableReference[]> s_autoReferencedAssemblies = new(InitAutoIncludedRefAssemblies);
  640 
  641         // A HashSet of assembly names to be ignored if they are specified in '-ReferencedAssemblies'
  642         private static readonly Lazy<HashSet<string>> s_refAssemblyNamesToIgnore = new(InitRefAssemblyNamesToIgnore);
  643 
  644         // These assemblies are used, when ReferencedAssemblies parameter is not specified.
  645         private static readonly Lazy<IEnumerable<PortableExecutableReference>> s_defaultAssemblies = new(InitDefaultRefAssemblies);
  646 
  647         private bool InMemory { get { return string.IsNullOrEmpty(_outputAssembly); } }
  648 
  649         // These dictionaries prevent reloading already loaded and unchanged code.
  650         // We don't worry about unbounded growing of the cache because in .Net Core 2.0 we can not unload assemblies.
  651         // TODO: review if we will be able to unload assemblies after migrating to .Net Core 2.1.
  652         private static readonly HashSet<string> s_sourceTypesCache = new();
  653         private static readonly Dictionary<int, Assembly> s_sourceAssemblyCache = new();
  654 
  655         private static readonly string s_defaultSdkDirectory = Utils.DefaultPowerShellAppBase;
  656 
  657         private const ReportDiagnostic defaultDiagnosticOption = ReportDiagnostic.Error;
  658 
  659         private static readonly string[] s_writeInformationTags = new string[] { "PSHOST" };
  660         private int _syntaxTreesHash;
  661 
  662         private const string FromMemberParameterSetName = "FromMember";
  663         private const string FromSourceParameterSetName = "FromSource";
  664         private const string FromPathParameterSetName = "FromPath";
  665         private const string FromLiteralPathParameterSetName = "FromLiteralPath";
  666         private const string FromAssemblyNameParameterSetName = "FromAssemblyName";
  667 
  668         private void LoadAssemblies(IEnumerable<string> assemblies)
  669         {
  670             foreach (string assemblyName in assemblies)
  671             {
  672                 // CoreCLR doesn't allow re-load TPA assemblies with different API (i.e. we load them by name and now want to load by path).
  673                 // LoadAssemblyHelper helps us avoid re-loading them, if they already loaded.
  674                 Assembly assembly = LoadAssemblyHelper(assemblyName) ?? Assembly.LoadFrom(ResolveAssemblyName(assemblyName, false));
  675 
  676                 if (PassThru)
  677                 {
  678                     WriteTypes(assembly);
  679                 }
  680             }
  681         }
  682 
  683         /// <summary>
  684         /// Initialize the list of reference assemblies that will be used when '-ReferencedAssemblies' is not specified.
  685         /// </summary>
  686         private static IEnumerable<PortableExecutableReference> InitDefaultRefAssemblies()
  687         {
  688             // Define number of reference assemblies distributed with PowerShell.
  689             const int maxPowershellRefAssemblies = 160;
  690 
  691             const int capacity = maxPowershellRefAssemblies + 1;
  692             var defaultRefAssemblies = new List<PortableExecutableReference>(capacity);
  693 
  694             foreach (string file in Directory.EnumerateFiles(s_netcoreAppRefFolder, "*.dll", SearchOption.TopDirectoryOnly))
  695             {
  696                 defaultRefAssemblies.Add(MetadataReference.CreateFromFile(file));
  697             }
  698 
  699             // Add System.Management.Automation.dll
  700             defaultRefAssemblies.Add(MetadataReference.CreateFromFile(typeof(PSObject).Assembly.Location));
  701 
  702             // We want to avoid reallocating the internal array, so we assert if the list capacity has increased.
  703             Diagnostics.Assert(
  704                 defaultRefAssemblies.Capacity <= capacity,
  705                 $"defaultRefAssemblies was resized because of insufficient initial capacity! A capacity of {defaultRefAssemblies.Count} is required.");
  706 
  707             return defaultRefAssemblies;
  708         }
  709 
  710         /// <summary>
  711         /// Initialize the set of assembly names that should be ignored when they are specified in '-ReferencedAssemblies'.
  712         ///   - System.Private.CoreLib.ni.dll - the runtime dll that contains most core/primitive types
  713         ///   - System.Private.Uri.dll - the runtime dll that contains 'System.Uri' and related types
  714         /// Referencing these runtime dlls may cause ambiguous type identity or other issues.
  715         ///   - System.Runtime.dll - the corresponding reference dll will be automatically included
  716         ///   - System.Runtime.InteropServices.dll - the corresponding reference dll will be automatically included.
  717         /// </summary>
  718         private static HashSet<string> InitRefAssemblyNamesToIgnore()
  719         {
  720             return new HashSet<string>(StringComparer.OrdinalIgnoreCase) {
  721                 PathType.GetFileName(typeof(object).Assembly.Location),
  722                 PathType.GetFileName(typeof(Uri).Assembly.Location),
  723                 PathType.GetFileName(GetReferenceAssemblyPathBasedOnType(typeof(object))),
  724                 PathType.GetFileName(GetReferenceAssemblyPathBasedOnType(typeof(SecureString)))
  725             };
  726         }
  727 
  728         /// <summary>
  729         /// Initialize the list of reference assemblies that will be automatically added when '-ReferencedAssemblies' is specified.
  730         /// </summary>
  731         private static PortableExecutableReference[] InitAutoIncludedRefAssemblies()
  732         {
  733             return new PortableExecutableReference[] {
  734                 MetadataReference.CreateFromFile(GetReferenceAssemblyPathBasedOnType(typeof(object))),
  735                 MetadataReference.CreateFromFile(GetReferenceAssemblyPathBasedOnType(typeof(SecureString)))
  736             };
  737         }
  738 
  739         /// <summary>
  740         /// Get the path of reference assembly where the type is declared.
  741         /// </summary>
  742         private static string GetReferenceAssemblyPathBasedOnType(Type type)
  743         {
  744             string refAsmFileName = PathType.GetFileName(ClrFacade.GetAssemblies(type.FullName).First().Location);
  745             return PathType.Combine(s_netcoreAppRefFolder, refAsmFileName);
  746         }
  747 
  748         private string ResolveAssemblyName(string assembly, bool isForReferenceAssembly)
  749         {
  750             ErrorRecord errorRecord;
  751 
  752             // if it's a path, resolve it
  753             if (assembly.Contains(PathType.DirectorySeparatorChar) || assembly.Contains(PathType.AltDirectorySeparatorChar))
  754             {
  755                 if (PathType.IsPathRooted(assembly))
  756                 {
  757                     return assembly;
  758                 }
  759                 else
  760                 {
  761                     var paths = SessionState.Path.GetResolvedPSPathFromPSPath(assembly);
  762                     if (paths.Count > 0)
  763                     {
  764                         return paths[0].Path;
  765                     }
  766                     else
  767                     {
  768                         errorRecord = new ErrorRecord(
  769                             new Exception(
  770                                 string.Format(ParserStrings.ErrorLoadingAssembly, assembly)),
  771                             "ErrorLoadingAssembly",
  772                             ErrorCategory.InvalidOperation,
  773                             assembly);
  774 
  775                         ThrowTerminatingError(errorRecord);
  776                         return null;
  777                     }
  778                 }
  779             }
  780 
  781             string refAssemblyDll = assembly;
  782             if (!assembly.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
  783             {
  784                 // It could be a short assembly name or a full assembly name, but we
  785                 // always want the short name to find the corresponding assembly file.
  786                 var assemblyName = new AssemblyName(assembly);
  787                 refAssemblyDll = assemblyName.Name + ".dll";
  788             }
  789 
  790             // We look up in reference/framework only when it's for resolving reference assemblies.
  791             // In case of 'Add-Type -AssemblyName' scenario, we don't attempt to resolve against framework assemblies because
  792             //   1. Explicitly loading a framework assembly usually is not necessary in PowerShell 6+.
  793             //   2. A user should use assembly name instead of path if they want to explicitly load a framework assembly.
  794             if (isForReferenceAssembly)
  795             {
  796                 // If it's for resolving a reference assembly, then we look in NetCoreApp ref assemblies first
  797                 string netcoreAppRefPath = PathType.Combine(s_netcoreAppRefFolder, refAssemblyDll);
  798                 if (File.Exists(netcoreAppRefPath))
  799                 {
  800                     return netcoreAppRefPath;
  801                 }
  802 
  803                 // Look up the assembly in the framework folder. This may happen when assembly is not part of
  804                 // NetCoreApp, but comes from an additional package, such as 'Json.Net'.
  805                 string frameworkPossiblePath = PathType.Combine(s_frameworkFolder, refAssemblyDll);
  806                 if (File.Exists(frameworkPossiblePath))
  807                 {
  808                     return frameworkPossiblePath;
  809                 }
  810 
  811                 // The assembly name may point to a third-party assembly that is already loaded at run time.
  812                 if (!assembly.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
  813                 {
  814                     Assembly result = LoadAssemblyHelper(assembly);
  815                     if (result != null)
  816                     {
  817                         return result.Location;
  818                     }
  819                 }
  820             }
  821 
  822             // Look up the assembly in the current folder
  823             var resolvedPaths = SessionState.Path.GetResolvedPSPathFromPSPath(refAssemblyDll);
  824 
  825             if (resolvedPaths.Count > 0)
  826             {
  827                 string currentFolderPath = resolvedPaths[0].Path;
  828                 if (File.Exists(currentFolderPath))
  829                 {
  830                     return currentFolderPath;
  831                 }
  832             }
  833 
  834             errorRecord = new ErrorRecord(
  835                 new Exception(
  836                     string.Format(ParserStrings.ErrorLoadingAssembly, assembly)),
  837                 "ErrorLoadingAssembly",
  838                 ErrorCategory.InvalidOperation,
  839                 assembly);
  840 
  841             ThrowTerminatingError(errorRecord);
  842             return null;
  843         }
  844 
  845         // LoadWithPartialName is deprecated, so we have to write the closest approximation possible.
  846         // However, this does give us a massive usability improvement, as users can just say
  847         // Add-Type -AssemblyName Forms (instead of System.Windows.Forms)
  848         // This is just long, not unmaintainable.
  849         private static Assembly LoadAssemblyHelper(string assemblyName)
  850         {
  851             Assembly loadedAssembly = null;
  852 
  853             // First try by strong name
  854             try
  855             {
  856                 loadedAssembly = Assembly.Load(new AssemblyName(assemblyName));
  857             }
  858             // Generates a FileNotFoundException if you can't load the strong type.
  859             // So we'll try from the short name.
  860             catch (System.IO.FileNotFoundException) { }
  861             // File load exception can happen, when we trying to load from the incorrect assembly name
  862             // or file corrupted.
  863             catch (System.IO.FileLoadException) { }
  864 
  865             return loadedAssembly;
  866         }
  867 
  868         private IEnumerable<PortableExecutableReference> GetPortableExecutableReferences()
  869         {
  870             if (ReferencedAssemblies.Length > 0)
  871             {
  872                 var tempReferences = new List<PortableExecutableReference>(s_autoReferencedAssemblies.Value);
  873                 foreach (string assembly in ReferencedAssemblies)
  874                 {
  875                     if (string.IsNullOrWhiteSpace(assembly)) { continue; }
  876 
  877                     string resolvedAssemblyPath = ResolveAssemblyName(assembly, true);
  878 
  879                     // Ignore some specified reference assemblies
  880                     string fileName = PathType.GetFileName(resolvedAssemblyPath);
  881                     if (s_refAssemblyNamesToIgnore.Value.Contains(fileName))
  882                     {
  883                         WriteVerbose(StringUtil.Format(AddTypeStrings.ReferenceAssemblyIgnored, resolvedAssemblyPath));
  884                         continue;
  885                     }
  886 
  887                     tempReferences.Add(MetadataReference.CreateFromFile(resolvedAssemblyPath));
  888                 }
  889 
  890                 return tempReferences;
  891             }
  892             else
  893             {
  894                 return s_defaultAssemblies.Value;
  895             }
  896         }
  897 
  898         private void WriteTypes(Assembly assembly)
  899         {
  900             WriteObject(assembly.GetTypes(), true);
  901         }
  902 
  903         #endregion LoadAssembly
  904 
  905         #region SourceCodeProcessing
  906 
  907         private static OutputKind OutputAssemblyTypeToOutputKind(OutputAssemblyType outputType)
  908         {
  909             switch (outputType)
  910             {
  911                 case OutputAssemblyType.Library:
  912                     return OutputKind.DynamicallyLinkedLibrary;
  913 
  914                 default:
  915                     throw PSTraceSource.NewNotSupportedException();
  916             }
  917         }
  918 
  919         private CommandLineArguments ParseCompilerOption(IEnumerable<string> args)
  920         {
  921             string sdkDirectory = s_defaultSdkDirectory;
  922             string baseDirectory = this.SessionState.Path.CurrentLocation.Path;
  923 
  924             switch (Language)
  925             {
  926                 case Language.CSharp:
  927                     return CSharpCommandLineParser.Default.Parse(args, baseDirectory, sdkDirectory);
  928 
  929                 default:
  930                     throw PSTraceSource.NewNotSupportedException();
  931             }
  932         }
  933 
  934         private SyntaxTree ParseSourceText(SourceText sourceText, ParseOptions parseOptions, string path = "")
  935         {
  936             switch (Language)
  937             {
  938                 case Language.CSharp:
  939                     return CSharpSyntaxTree.ParseText(sourceText, (CSharpParseOptions)parseOptions, path);
  940 
  941                 default:
  942                     throw PSTraceSource.NewNotSupportedException();
  943             }
  944         }
  945 
  946         private CompilationOptions GetDefaultCompilationOptions()
  947         {
  948             switch (Language)
  949             {
  950                 case Language.CSharp:
  951                     return new CSharpCompilationOptions(OutputAssemblyTypeToOutputKind(OutputType));
  952 
  953                 default:
  954                     throw PSTraceSource.NewNotSupportedException();
  955             }
  956         }
  957 
  958         private bool isSourceCodeUpdated(List<SyntaxTree> syntaxTrees, out Assembly assembly)
  959         {
  960             Diagnostics.Assert(syntaxTrees.Count != 0, "syntaxTrees should contains a source code.");
  961 
  962             _syntaxTreesHash = SyntaxTreeArrayGetHashCode(syntaxTrees);
  963 
  964             if (s_sourceAssemblyCache.TryGetValue(_syntaxTreesHash, out Assembly hashedAssembly))
  965             {
  966                 assembly = hashedAssembly;
  967                 return false;
  968             }
  969             else
  970             {
  971                 assembly = null;
  972                 return true;
  973             }
  974         }
  975 
  976         private void SourceCodeProcessing()
  977         {
  978             ParseOptions parseOptions = null;
  979             CompilationOptions compilationOptions = null;
  980             EmitOptions emitOptions = null;
  981 
  982             if (CompilerOptions != null)
  983             {
  984                 var arguments = ParseCompilerOption(CompilerOptions);
  985 
  986                 HandleCompilerErrors(arguments.Errors);
  987 
  988                 parseOptions = arguments.ParseOptions;
  989                 compilationOptions = arguments.CompilationOptions.WithOutputKind(OutputAssemblyTypeToOutputKind(OutputType));
  990                 emitOptions = arguments.EmitOptions;
  991             }
  992             else
  993             {
  994                 parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest);
  995                 compilationOptions = GetDefaultCompilationOptions();
  996             }
  997 
  998             if (!IgnoreWarnings.IsPresent)
  999             {
 1000                 compilationOptions = compilationOptions.WithGeneralDiagnosticOption(defaultDiagnosticOption);
 1001             }
 1002 
 1003             SourceText sourceText;
 1004             List<SyntaxTree> syntaxTrees = new();
 1005 
 1006             switch (ParameterSetName)
 1007             {
 1008                 case FromPathParameterSetName:
 1009                 case FromLiteralPathParameterSetName:
 1010                     foreach (string filePath in _paths)
 1011                     {
 1012                         using (var sourceFile = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
 1013                         {
 1014                             sourceText = SourceText.From(sourceFile);
 1015                             syntaxTrees.Add(ParseSourceText(sourceText, parseOptions, path: filePath));
 1016                         }
 1017                     }
 1018 
 1019                     break;
 1020                 case FromMemberParameterSetName:
 1021                     _sourceCode = GenerateTypeSource(Namespace, Name, _sourceCode, Language);
 1022 
 1023                     sourceText = SourceText.From(_sourceCode);
 1024                     syntaxTrees.Add(ParseSourceText(sourceText, parseOptions));
 1025                     break;
 1026                 case FromSourceParameterSetName:
 1027                     sourceText = SourceText.From(_sourceCode);
 1028                     syntaxTrees.Add(ParseSourceText(sourceText, parseOptions));
 1029                     break;
 1030                 default:
 1031                     Diagnostics.Assert(false, "Invalid parameter set: {0}", this.ParameterSetName);
 1032                     break;
 1033             }
 1034 
 1035             if (!string.IsNullOrEmpty(_outputAssembly) && !PassThru.IsPresent)
 1036             {
 1037                 CompileToAssembly(syntaxTrees, compilationOptions, emitOptions);
 1038             }
 1039             else
 1040             {
 1041                 // if the source code was already compiled and loaded and not changed
 1042                 // we get the assembly from the cache.
 1043                 if (isSourceCodeUpdated(syntaxTrees, out Assembly assembly))
 1044                 {
 1045                     CompileToAssembly(syntaxTrees, compilationOptions, emitOptions);
 1046                 }
 1047                 else
 1048                 {
 1049                     WriteVerbose(AddTypeStrings.AlreadyCompiledandLoaded);
 1050 
 1051                     if (PassThru)
 1052                     {
 1053                         WriteTypes(assembly);
 1054                     }
 1055                 }
 1056             }
 1057         }
 1058 
 1059         private void CompileToAssembly(List<SyntaxTree> syntaxTrees, CompilationOptions compilationOptions, EmitOptions emitOptions)
 1060         {
 1061             IEnumerable<PortableExecutableReference> references = GetPortableExecutableReferences();
 1062             Compilation compilation = null;
 1063 
 1064             switch (Language)
 1065             {
 1066                 case Language.CSharp:
 1067                     compilation = CSharpCompilation.Create(
 1068                         PathType.GetRandomFileName(),
 1069                         syntaxTrees: syntaxTrees,
 1070                         references: references,
 1071                         options: (CSharpCompilationOptions)compilationOptions);
 1072                     break;
 1073 
 1074                 default:
 1075                     throw PSTraceSource.NewNotSupportedException();
 1076             }
 1077 
 1078             DoEmitAndLoadAssembly(compilation, emitOptions);
 1079         }
 1080 
 1081         private void CheckDuplicateTypes(Compilation compilation, out ConcurrentBag<string> newTypes)
 1082         {
 1083             AllNamedTypeSymbolsVisitor visitor = new();
 1084             visitor.Visit(compilation.Assembly.GlobalNamespace);
 1085 
 1086             foreach (var symbolName in visitor.DuplicateSymbols)
 1087             {
 1088                 ErrorRecord errorRecord = new(
 1089                     new Exception(
 1090                         string.Format(AddTypeStrings.TypeAlreadyExists, symbolName)),
 1091                     "TYPE_ALREADY_EXISTS",
 1092                     ErrorCategory.InvalidOperation,
 1093                     symbolName);
 1094                 WriteError(errorRecord);
 1095             }
 1096 
 1097             if (!visitor.DuplicateSymbols.IsEmpty)
 1098             {
 1099                 ErrorRecord errorRecord = new(
 1100                     new InvalidOperationException(AddTypeStrings.CompilerErrors),
 1101                     "COMPILER_ERRORS",
 1102                     ErrorCategory.InvalidData,
 1103                     null);
 1104                 ThrowTerminatingError(errorRecord);
 1105             }
 1106 
 1107             newTypes = visitor.UniqueSymbols;
 1108 
 1109             return;
 1110         }
 1111 
 1112         // Visit symbols in all namespaces and collect duplicates.
 1113         private sealed class AllNamedTypeSymbolsVisitor : SymbolVisitor
 1114         {
 1115             public readonly ConcurrentBag<string> DuplicateSymbols = new();
 1116             public readonly ConcurrentBag<string> UniqueSymbols = new();
 1117 
 1118             public override void VisitNamespace(INamespaceSymbol symbol)
 1119             {
 1120                 // Main cycle.
 1121                 // For large files we could use symbol.GetMembers().AsParallel().ForAll(s => s.Accept(this));
 1122                 foreach (var member in symbol.GetMembers())
 1123                 {
 1124                     member.Accept(this);
 1125                 }
 1126             }
 1127 
 1128             public override void VisitNamedType(INamedTypeSymbol symbol)
 1129             {
 1130                 // It is namespace-fully-qualified name
 1131                 var symbolFullName = symbol.ToString();
 1132 
 1133                 if (s_sourceTypesCache.TryGetValue(symbolFullName, out _))
 1134                 {
 1135                     DuplicateSymbols.Add(symbolFullName);
 1136                 }
 1137                 else
 1138                 {
 1139                     UniqueSymbols.Add(symbolFullName);
 1140                 }
 1141             }
 1142         }
 1143 
 1144         private static void CacheNewTypes(ConcurrentBag<string> newTypes)
 1145         {
 1146             foreach (var typeName in newTypes)
 1147             {
 1148                 s_sourceTypesCache.Add(typeName);
 1149             }
 1150         }
 1151 
 1152         private void CacheAssembly(Assembly assembly)
 1153         {
 1154             s_sourceAssemblyCache.Add(_syntaxTreesHash, assembly);
 1155         }
 1156 
 1157         private void DoEmitAndLoadAssembly(Compilation compilation, EmitOptions emitOptions)
 1158         {
 1159             EmitResult emitResult;
 1160 
 1161             CheckDuplicateTypes(compilation, out ConcurrentBag<string> newTypes);
 1162 
 1163             if (InMemory)
 1164             {
 1165                 using (var ms = new MemoryStream())
 1166                 {
 1167                     emitResult = compilation.Emit(peStream: ms, options: emitOptions);
 1168 
 1169                     HandleCompilerErrors(emitResult.Diagnostics);
 1170 
 1171                     if (emitResult.Success)
 1172                     {
 1173                         // TODO:  We could use Assembly.LoadFromStream() in future.
 1174                         // See https://github.com/dotnet/corefx/issues/26994
 1175                         ms.Seek(0, SeekOrigin.Begin);
 1176                         Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms);
 1177 
 1178                         CacheNewTypes(newTypes);
 1179                         CacheAssembly(assembly);
 1180 
 1181                         if (PassThru)
 1182                         {
 1183                             WriteTypes(assembly);
 1184                         }
 1185                     }
 1186                 }
 1187             }
 1188             else
 1189             {
 1190                 using (var fs = new FileStream(_outputAssembly, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None))
 1191                 {
 1192                     emitResult = compilation.Emit(peStream: fs, options: emitOptions);
 1193                 }
 1194 
 1195                 HandleCompilerErrors(emitResult.Diagnostics);
 1196 
 1197                 if (emitResult.Success && PassThru)
 1198                 {
 1199                     Assembly assembly = Assembly.LoadFrom(_outputAssembly);
 1200 
 1201                     CacheNewTypes(newTypes);
 1202                     CacheAssembly(assembly);
 1203 
 1204                     WriteTypes(assembly);
 1205                 }
 1206             }
 1207         }
 1208 
 1209         private void HandleCompilerErrors(ImmutableArray<Diagnostic> compilerDiagnostics)
 1210         {
 1211             if (compilerDiagnostics.Length > 0)
 1212             {
 1213                 bool IsError = false;
 1214 
 1215                 foreach (var diagnisticRecord in compilerDiagnostics)
 1216                 {
 1217                     // We shouldn't specify input and output files in CompilerOptions parameter
 1218                     // so suppress errors from Roslyn default command line parser:
 1219                     //      CS1562: Outputs without source must have the /out option specified
 1220                     //      CS2008: No inputs specified
 1221                     //      BC2008: No inputs specified
 1222                     //
 1223                     // On emit phase some warnings (like CS8019/BS50001) don't suppressed
 1224                     // and present in diagnostic report with DefaultSeverity equal to Hidden
 1225                     // so we skip them explicitly here too.
 1226                     if (diagnisticRecord.IsSuppressed || diagnisticRecord.DefaultSeverity == DiagnosticSeverity.Hidden ||
 1227                         string.Equals(diagnisticRecord.Id, "CS2008", StringComparison.InvariantCulture) ||
 1228                         string.Equals(diagnisticRecord.Id, "CS1562", StringComparison.InvariantCulture) ||
 1229                         string.Equals(diagnisticRecord.Id, "BC2008", StringComparison.InvariantCulture))
 1230                     {
 1231                         continue;
 1232                     }
 1233 
 1234                     if (!IsError)
 1235                     {
 1236                         IsError = diagnisticRecord.Severity == DiagnosticSeverity.Error ||
 1237                                  (diagnisticRecord.IsWarningAsError && diagnisticRecord.Severity == DiagnosticSeverity.Warning);
 1238                     }
 1239 
 1240                     string errorText = BuildErrorMessage(diagnisticRecord);
 1241 
 1242                     if (diagnisticRecord.Severity == DiagnosticSeverity.Warning)
 1243                     {
 1244                         WriteWarning(errorText);
 1245                     }
 1246                     else if (diagnisticRecord.Severity == DiagnosticSeverity.Info)
 1247                     {
 1248                         WriteInformation(errorText, s_writeInformationTags);
 1249                     }
 1250                     else
 1251                     {
 1252                         ErrorRecord errorRecord = new(
 1253                             new Exception(errorText),
 1254                             "SOURCE_CODE_ERROR",
 1255                             ErrorCategory.InvalidData,
 1256                             diagnisticRecord);
 1257 
 1258                         WriteError(errorRecord);
 1259                     }
 1260                 }
 1261 
 1262                 if (IsError)
 1263                 {
 1264                     ErrorRecord errorRecord = new(
 1265                         new InvalidOperationException(AddTypeStrings.CompilerErrors),
 1266                         "COMPILER_ERRORS",
 1267                         ErrorCategory.InvalidData,
 1268                         null);
 1269                     ThrowTerminatingError(errorRecord);
 1270                 }
 1271             }
 1272         }
 1273 
 1274         private static string BuildErrorMessage(Diagnostic diagnisticRecord)
 1275         {
 1276             var location = diagnisticRecord.Location;
 1277 
 1278             if (location.SourceTree == null)
 1279             {
 1280                 // For some error types (linker?) we don't have related source code.
 1281                 return diagnisticRecord.ToString();
 1282             }
 1283             else
 1284             {
 1285                 var text = location.SourceTree.GetText();
 1286                 var textLines = text.Lines;
 1287 
 1288                 var lineSpan = location.GetLineSpan(); // FileLinePositionSpan type.
 1289                 var errorLineNumber = lineSpan.StartLinePosition.Line;
 1290 
 1291                 // This is typical Roslyn diagnostic message which contains
 1292                 // a message number, a source context and an error position.
 1293                 var diagnisticMessage = diagnisticRecord.ToString();
 1294                 var errorLineString = textLines[errorLineNumber].ToString();
 1295                 var errorPosition = lineSpan.StartLinePosition.Character;
 1296 
 1297                 StringBuilder sb = new(diagnisticMessage.Length + errorLineString.Length * 2 + 4);
 1298 
 1299                 sb.AppendLine(diagnisticMessage);
 1300                 sb.AppendLine(errorLineString);
 1301 
 1302                 for (var i = 0; i < errorLineString.Length; i++)
 1303                 {
 1304                     if (!char.IsWhiteSpace(errorLineString[i]))
 1305                     {
 1306                         // We copy white chars from the source string.
 1307                         sb.Append(errorLineString, 0, i);
 1308                         // then pad up to the error position.
 1309                         sb.Append(' ', Math.Max(0, errorPosition - i));
 1310                         // then put "^" into the error position.
 1311                         sb.AppendLine("^");
 1312                         break;
 1313                     }
 1314                 }
 1315 
 1316                 return sb.ToString();
 1317             }
 1318         }
 1319 
 1320         private static int SyntaxTreeArrayGetHashCode(IEnumerable<SyntaxTree> sts)
 1321         {
 1322             // We use our extension method EnumerableExtensions.SequenceGetHashCode<T>().
 1323             List<int> stHashes = new();
 1324             foreach (var st in sts)
 1325             {
 1326                 stHashes.Add(SyntaxTreeGetHashCode(st));
 1327             }
 1328 
 1329             return stHashes.SequenceGetHashCode<int>();
 1330         }
 1331 
 1332         private static int SyntaxTreeGetHashCode(SyntaxTree st)
 1333         {
 1334             int hash;
 1335 
 1336             if (string.IsNullOrEmpty(st.FilePath))
 1337             {
 1338                 // If the file name does not exist, the source text is set by the user using parameters.
 1339                 // In this case, we assume that the source text is of a small size and we can re-allocate by ToString().
 1340                 hash = st.ToString().GetHashCode();
 1341             }
 1342             else
 1343             {
 1344                 // If the file was modified, the write time stamp was also modified
 1345                 // so we do not need to calculate the entire file hash.
 1346                 var updateTime = File.GetLastWriteTimeUtc(st.FilePath);
 1347                 hash = Utils.CombineHashCodes(st.FilePath.GetHashCode(), updateTime.GetHashCode());
 1348             }
 1349 
 1350             return hash;
 1351         }
 1352 
 1353         #endregion SourceCodeProcessing
 1354     }
 1355 }