Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
CommandInfo.cs985 linesDownload Raw Back to engine
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Diagnostics;7using System.Diagnostics.CodeAnalysis;8using System.Linq;9using System.Management.Automation.Language;10using System.Management.Automation.Runspaces;11using System.Reflection;12using System.Runtime.ExceptionServices;13using System.Text;14 15using Microsoft.PowerShell.Commands;16 17namespace System.Management.Automation18{19    /// <summary>20    /// Defines the types of commands that PowerShell can execute.21    /// </summary>22    [Flags]23    public enum CommandTypes24    {25        /// <summary>26        /// Aliases create a name that refers to other command types.27        /// Aliases are only persisted within the execution of a single engine.28        /// </summary>29        Alias = 0x0001,30 31        /// <summary>32        /// Script functions that are defined by a script block.33        /// Functions are only persisted within the execution of a single engine.34        /// </summary>35        Function = 0x0002,36 37        /// <summary>38        /// Script filters that are defined by a script block.39        /// Filters are only persisted within the execution of a single engine.40        /// </summary>41        Filter = 0x0004,42 43        /// <summary>44        /// A cmdlet.45        /// </summary>46        Cmdlet = 0x0008,47 48        /// <summary>49        /// An PowerShell script (*.ps1 file)50        /// </summary>51        ExternalScript = 0x0010,52 53        /// <summary>54        /// Any existing application (can be console or GUI).55        /// An application can have any extension that can be executed either directly through CreateProcess56        /// or indirectly through ShellExecute.57        /// </summary>58        Application = 0x0020,59 60        /// <summary>61        /// A script that is built into the runspace configuration.62        /// </summary>63        Script = 0x0040,64 65        /// <summary>66        /// A Configuration.67        /// </summary>68        Configuration = 0x0100,69 70        /// <summary>71        /// All possible command types.72        /// NOTE: a CommandInfo instance will never specify All as its CommandType73        /// but All can be used when filtering the CommandTypes.74        /// </summary>75        All = Alias | Function | Filter | Cmdlet | Script | ExternalScript | Application | Configuration,76    }77 78    /// <summary>79    /// The base class for the information about commands. Contains the basic information about80    /// the command, like name and type.81    /// </summary>82    public abstract class CommandInfo : IHasSessionStateEntryVisibility83    {84        #region ctor85 86        /// <summary>87        /// Creates an instance of the CommandInfo class with the specified name and type.88        /// </summary>89        /// <param name="name">90        /// The name of the command.91        /// </param>92        /// <param name="type">93        /// The type of the command.94        /// </param>95        /// <exception cref="ArgumentNullException">96        /// If <paramref name="name"/> is null.97        /// </exception>98        internal CommandInfo(string name, CommandTypes type)99        {100            // The name can be empty for functions and filters but it101            // can't be null102 103            ArgumentNullException.ThrowIfNull(name);104 105            Name = name;106            CommandType = type;107        }108 109        /// <summary>110        /// Creates an instance of the CommandInfo class with the specified name and type.111        /// </summary>112        /// <param name="name">113        /// The name of the command.114        /// </param>115        /// <param name="type">116        /// The type of the command.117        /// </param>118        /// <param name="context">119        /// The execution context for the command.120        /// </param>121        /// <exception cref="ArgumentNullException">122        /// If <paramref name="name"/> is null.123        /// </exception>124        internal CommandInfo(string name, CommandTypes type, ExecutionContext context)125            : this(name, type)126        {127            this.Context = context;128        }129 130        /// <summary>131        /// This is a copy constructor, used primarily for get-command.132        /// </summary>133        internal CommandInfo(CommandInfo other)134        {135            // Computed fields not copied:136            // this._externalCommandMetadata = other._externalCommandMetadata;137            // this._moduleName = other._moduleName;138            // this.parameterSets = other.parameterSets;139            this.Module = other.Module;140            _visibility = other._visibility;141            Arguments = other.Arguments;142            this.Context = other.Context;143            Name = other.Name;144            CommandType = other.CommandType;145            CopiedCommand = other;146            this.DefiningLanguageMode = other.DefiningLanguageMode;147        }148 149        /// <summary>150        /// This is a copy constructor, used primarily for get-command.151        /// </summary>152        internal CommandInfo(string name, CommandInfo other)153            : this(other)154        {155            Name = name;156        }157 158        #endregion ctor159 160        /// <summary>161        /// Gets the name of the command.162        /// </summary>163        public string Name { get; private set; } = string.Empty;164 165        // Name166 167        /// <summary>168        /// Gets the type of the command.169        /// </summary>170        public CommandTypes CommandType { get; private set; } = CommandTypes.Application;171 172        // CommandType173 174        /// <summary>175        /// Gets the source of the command (shown by default in Get-Command)176        /// </summary>177        public virtual string Source { get { return this.ModuleName; } }178 179        /// <summary>180        /// Gets the source version (shown by default in Get-Command)181        /// </summary>182        public virtual Version Version183        {184            get185            {186                if (_version == null)187                {188                    if (Module != null)189                    {190                        if (Module.Version.Equals(new Version(0, 0)))191                        {192                            if (Module.Path.EndsWith(StringLiterals.PowerShellDataFileExtension, StringComparison.OrdinalIgnoreCase))193                            {194                                // Manifest module (.psd1)195                                Module.SetVersion(ModuleIntrinsics.GetManifestModuleVersion(Module.Path));196                            }197                            else if (Module.Path.EndsWith(StringLiterals.PowerShellILAssemblyExtension, StringComparison.OrdinalIgnoreCase) ||198                                     Module.Path.EndsWith(StringLiterals.PowerShellILExecutableExtension, StringComparison.OrdinalIgnoreCase))199                            {200                                // Binary module (.dll or .exe)201                                Module.SetVersion(AssemblyName.GetAssemblyName(Module.Path).Version);202                            }203                        }204 205                        _version = Module.Version;206                    }207                }208 209                return _version;210            }211        }212 213        private Version _version;214 215        /// <summary>216        /// The execution context this command will run in.217        /// </summary>218        internal ExecutionContext Context219        {220            get221            {222                return _context;223            }224 225            set226            {227                _context = value;228                if ((value != null) && !this.DefiningLanguageMode.HasValue)229                {230                    this.DefiningLanguageMode = value.LanguageMode;231                }232            }233        }234 235        private ExecutionContext _context;236 237        /// <summary>238        /// The language mode that was in effect when this alias was defined.239        /// </summary>240        internal PSLanguageMode? DefiningLanguageMode { get; set; }241 242        internal virtual HelpCategory HelpCategory243        {244            get { return HelpCategory.None; }245        }246 247        internal CommandInfo CopiedCommand { get; set; }248 249        /// <summary>250        /// Internal interface to change the type of a CommandInfo object.251        /// </summary>252        /// <param name="newType"></param>253        internal void SetCommandType(CommandTypes newType)254        {255            CommandType = newType;256        }257 258        /// <summary>259        /// A string representing the definition of the command.260        /// </summary>261        /// <remarks>262        /// This is overridden by derived classes to return specific263        /// information for the command type.264        /// </remarks>265        public abstract string Definition { get; }266 267        /// <summary>268        /// This is required for renaming aliases, functions, and filters.269        /// </summary>270        /// <param name="newName">271        /// The new name for the command.272        /// </param>273        /// <exception cref="ArgumentException">274        /// If <paramref name="newName"/> is null or empty.275        /// </exception>276        internal void Rename(string newName)277        {278            ArgumentException.ThrowIfNullOrEmpty(newName);279 280            Name = newName;281        }282 283        /// <summary>284        /// For diagnostic purposes.285        /// </summary>286        /// <returns></returns>287        public override string ToString()288        {289            return ModuleCmdletBase.AddPrefixToCommandName(Name, Prefix);290        }291 292        /// <summary>293        /// Indicates if the command is to be allowed to be executed by a request294        /// external to the runspace.295        /// </summary>296        public virtual SessionStateEntryVisibility Visibility297        {298            get299            {300                return CopiedCommand == null ? _visibility : CopiedCommand.Visibility;301            }302 303            set304            {305                if (CopiedCommand == null)306                {307                    _visibility = value;308                }309                else310                {311                    CopiedCommand.Visibility = value;312                }313 314                if (value == SessionStateEntryVisibility.Private && Module != null)315                {316                    Module.ModuleHasPrivateMembers = true;317                }318            }319        }320 321        private SessionStateEntryVisibility _visibility = SessionStateEntryVisibility.Public;322 323        /// <summary>324        /// Return a CommandMetadata instance that is never exposed publicly.325        /// </summary>326        internal virtual CommandMetadata CommandMetadata327        {328            get329            {330                throw new InvalidOperationException();331            }332        }333 334        /// <summary>335        /// Returns the syntax of a command.336        /// </summary>337        internal virtual string Syntax338        {339            get { return Definition; }340        }341 342        /// <summary>343        /// The module name of this command. It will be empty for commands344        /// not imported from either a module or snapin.345        /// </summary>346        public string ModuleName347        {348            get349            {350                string moduleName = null;351 352                if (Module != null && !string.IsNullOrEmpty(Module.Name))353                {354                    moduleName = Module.Name;355                }356                else357                {358                    CmdletInfo cmdlet = this as CmdletInfo;359                    if (cmdlet != null && cmdlet.PSSnapIn != null)360                    {361                        moduleName = cmdlet.PSSnapInName;362                    }363                }364 365                if (moduleName == null)366                    return string.Empty;367 368                return moduleName;369            }370        }371 372        /// <summary>373        /// The module that defines this cmdlet. This will be null for commands374        /// that are not defined in the context of a module.375        /// </summary>376        public PSModuleInfo Module { get; internal set; }377 378        /// <summary>379        /// The remoting capabilities of this cmdlet, when exposed in a context380        /// with ambient remoting.381        /// </summary>382        public RemotingCapability RemotingCapability383        {384            get385            {386                try387                {388                    return ExternalCommandMetadata.RemotingCapability;389                }390                catch (PSNotSupportedException)391                {392                    // Thrown on an alias that hasn't been resolved yet (i.e.: in a module that393                    // hasn't been loaded.) Assume the default.394                    return RemotingCapability.PowerShell;395                }396            }397        }398 399        /// <summary>400        /// True if the command has dynamic parameters, false otherwise.401        /// </summary>402        internal virtual bool ImplementsDynamicParameters403        {404            get { return false; }405        }406 407        /// <summary>408        /// Constructs the MergedCommandParameterMetadata, using any arguments that409        /// may have been specified so that dynamic parameters can be determined, if any.410        /// </summary>411        /// <returns></returns>412        private MergedCommandParameterMetadata GetMergedCommandParameterMetadataSafely()413        {414            if (_context == null)415                return null;416 417            MergedCommandParameterMetadata result;418            if (_context != LocalPipeline.GetExecutionContextFromTLS())419            {420                // In the normal case, _context is from the thread we're on, and we won't get here.421                // But, if it's not, we can't safely get the parameter metadata without running on422                // on the correct thread, because that thread may be busy doing something else.423                // One of the things we do here is change the current scope in execution context,424                // that can mess up the runspace our CommandInfo object came from.425 426                var runspace = (RunspaceBase)_context.CurrentRunspace;427                if (runspace.CanRunActionInCurrentPipeline())428                {429                    GetMergedCommandParameterMetadata(out result);430                }431                else432                {433                    _context.Events.SubscribeEvent(434                            source: null,435                            eventName: PSEngineEvent.GetCommandInfoParameterMetadata,436                            sourceIdentifier: PSEngineEvent.GetCommandInfoParameterMetadata,437                            data: null,438                            handlerDelegate: new PSEventReceivedEventHandler(OnGetMergedCommandParameterMetadataSafelyEventHandler),439                            supportEvent: true,440                            forwardEvent: false,441                            shouldQueueAndProcessInExecutionThread: true,442                            maxTriggerCount: 1);443 444                    var eventArgs = new GetMergedCommandParameterMetadataSafelyEventArgs();445 446                    _context.Events.GenerateEvent(447                        sourceIdentifier: PSEngineEvent.GetCommandInfoParameterMetadata,448                        sender: null,449                        args: new[] { eventArgs },450                        extraData: null,451                        processInCurrentThread: true,452                        waitForCompletionInCurrentThread: true);453 454                    // An exception happened on a different thread, rethrow it here on the correct thread.455                    eventArgs.Exception?.Throw();456 457                    return eventArgs.Result;458                }459            }460 461            GetMergedCommandParameterMetadata(out result);462            return result;463        }464 465        private sealed class GetMergedCommandParameterMetadataSafelyEventArgs : EventArgs466        {467            public MergedCommandParameterMetadata Result;468            public ExceptionDispatchInfo Exception;469        }470 471        private void OnGetMergedCommandParameterMetadataSafelyEventHandler(object sender, PSEventArgs args)472        {473            var eventArgs = args.SourceEventArgs as GetMergedCommandParameterMetadataSafelyEventArgs;474            if (eventArgs != null)475            {476                try477                {478                    // Save the result in our event args as the return value.479                    GetMergedCommandParameterMetadata(out eventArgs.Result);480                }481                catch (Exception e)482                {483                    // Save the exception so we can throw it on the correct thread.484                    eventArgs.Exception = ExceptionDispatchInfo.Capture(e);485                }486            }487        }488 489        private void GetMergedCommandParameterMetadata(out MergedCommandParameterMetadata result)490        {491            // MSFT:652277 - When invoking cmdlets or advanced functions, MyInvocation.MyCommand.Parameters do not contain the dynamic parameters492            // When trying to get parameter metadata for a CommandInfo that has dynamic parameters, a new CommandProcessor will be493            // created out of this CommandInfo and the parameter binding algorithm will be invoked. However, when this happens via494            // 'MyInvocation.MyCommand.Parameter', it's actually retrieving the parameter metadata of the same cmdlet that is currently495            // running. In this case, information about the specified parameters are not kept around in 'MyInvocation.MyCommand', so496            // going through the binding algorithm again won't give us the metadata about the dynamic parameters that should have been497            // discovered already.498            // The fix is to check if the CommandInfo is actually representing the currently running cmdlet. If so, the retrieval of parameter499            // metadata actually stems from the running of the same cmdlet. In this case, we can just use the current CommandProcessor to500            // retrieve all bindable parameters, which should include the dynamic parameters that have been discovered already.501            CommandProcessor processor;502            if (Context.CurrentCommandProcessor != null && Context.CurrentCommandProcessor.CommandInfo == this)503            {504                // Accessing the parameters within the invocation of the same cmdlet/advanced function.505                processor = (CommandProcessor)Context.CurrentCommandProcessor;506            }507            else508            {509                IScriptCommandInfo scriptCommand = this as IScriptCommandInfo;510                processor = scriptCommand != null511                    ? new CommandProcessor(scriptCommand, _context, useLocalScope: true, fromScriptFile: false,512                        sessionState: scriptCommand.ScriptBlock.SessionStateInternal ?? Context.EngineSessionState)513                    : new CommandProcessor((CmdletInfo)this, _context);514 515                ParameterBinderController.AddArgumentsToCommandProcessor(processor, Arguments);516                CommandProcessorBase oldCurrentCommandProcessor = Context.CurrentCommandProcessor;517                try518                {519                    Context.CurrentCommandProcessor = processor;520 521                    processor.SetCurrentScopeToExecutionScope();522                    processor.CmdletParameterBinderController.BindCommandLineParametersNoValidation(processor.arguments);523                }524                catch (ParameterBindingException)525                {526                    // Ignore the binding exception if no argument is specified527                    if (processor.arguments.Count > 0)528                    {529                        throw;530                    }531                }532                finally533                {534                    Context.CurrentCommandProcessor = oldCurrentCommandProcessor;535                    processor.RestorePreviousScope();536                }537            }538 539            result = processor.CmdletParameterBinderController.BindableParameters;540        }541 542        /// <summary>543        /// Return the parameters for this command.544        /// </summary>545        public virtual Dictionary<string, ParameterMetadata> Parameters546        {547            get548            {549                Dictionary<string, ParameterMetadata> result = new Dictionary<string, ParameterMetadata>(StringComparer.OrdinalIgnoreCase);550 551                if (ImplementsDynamicParameters && Context != null)552                {553                    MergedCommandParameterMetadata merged = GetMergedCommandParameterMetadataSafely();554 555                    foreach (KeyValuePair<string, MergedCompiledCommandParameter> pair in merged.BindableParameters)556                    {557                        result.Add(pair.Key, new ParameterMetadata(pair.Value.Parameter));558                    }559 560                    // Don't cache this data...561                    return result;562                }563 564                return ExternalCommandMetadata.Parameters;565            }566        }567 568        internal CommandMetadata ExternalCommandMetadata569        {570            get { return _externalCommandMetadata ??= new CommandMetadata(this, true); }571 572            set { _externalCommandMetadata = value; }573        }574 575        private CommandMetadata _externalCommandMetadata;576 577        /// <summary>578        /// Resolves a full, shortened, or aliased parameter name to the actual579        /// cmdlet parameter name, using PowerShell's standard parameter resolution580        /// algorithm.581        /// </summary>582        /// <param name="name">The name of the parameter to resolve.</param>583        /// <returns>The parameter that matches this name.</returns>584        public ParameterMetadata ResolveParameter(string name)585        {586            MergedCommandParameterMetadata merged = GetMergedCommandParameterMetadataSafely();587            MergedCompiledCommandParameter result = merged.GetMatchingParameter(name, true, true, null);588            return this.Parameters[result.Parameter.Name];589        }590 591        /// <summary>592        /// Gets the information about the parameters and parameter sets for593        /// this command.594        /// </summary>595        public ReadOnlyCollection<CommandParameterSetInfo> ParameterSets596        {597            get598            {599                if (_parameterSets == null)600                {601                    Collection<CommandParameterSetInfo> parameterSetInfo =602                        GenerateCommandParameterSetInfo();603 604                    _parameterSets = new ReadOnlyCollection<CommandParameterSetInfo>(parameterSetInfo);605                }606 607                return _parameterSets;608            }609        }610 611        internal ReadOnlyCollection<CommandParameterSetInfo> _parameterSets;612 613        /// <summary>614        /// A possibly incomplete or even incorrect list of types the command could return.615        /// </summary>616        [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]617        public abstract ReadOnlyCollection<PSTypeName> OutputType { get; }618 619        /// <summary>620        /// Specifies whether this command was imported from a module or not.621        /// This is used in Get-Command to figure out which of the commands in module session state were imported.622        /// </summary>623        internal bool IsImported { get; set; } = false;624 625        /// <summary>626        /// The prefix that was used when importing this command.627        /// </summary>628        internal string Prefix { get; set; } = string.Empty;629 630        /// <summary>631        /// Create a copy of commandInfo for GetCommandCommand so that we can generate parameter632        /// sets based on an argument list (so we can get the dynamic parameters.)633        /// </summary>634        internal virtual CommandInfo CreateGetCommandCopy(object[] argumentList)635        {636            throw new InvalidOperationException();637        }638 639        /// <summary>640        /// Generates the parameter and parameter set info from the cmdlet metadata.641        /// </summary>642        /// <returns>643        /// A collection of CommandParameterSetInfo representing the cmdlet metadata.644        /// </returns>645        /// <exception cref="ArgumentException">646        /// The type name is invalid or the length of the type name647        /// exceeds 1024 characters.648        /// </exception>649        /// <exception cref="System.Security.SecurityException">650        /// The caller does not have the required permission to load the assembly651        /// or create the type.652        /// </exception>653        /// <exception cref="ParsingMetadataException">654        /// If more than int.MaxValue parameter-sets are defined for the command.655        /// </exception>656        /// <exception cref="MetadataException">657        /// If a parameter defines the same parameter-set name multiple times.658        /// If the attributes could not be read from a property or field.659        /// </exception>660        internal Collection<CommandParameterSetInfo> GenerateCommandParameterSetInfo()661        {662            Collection<CommandParameterSetInfo> result;663 664            if (IsGetCommandCopy && ImplementsDynamicParameters)665            {666                result = GetParameterMetadata(CommandMetadata, GetMergedCommandParameterMetadataSafely());667            }668            else669            {670                result = GetCacheableMetadata(CommandMetadata);671            }672 673            return result;674        }675 676        /// <summary>677        /// Gets or sets whether this CmdletInfo instance is a copy used for get-command.678        /// If true, and the cmdlet supports dynamic parameters, it means that the dynamic679        /// parameter metadata will be merged into the parameter set information.680        /// </summary>681        internal bool IsGetCommandCopy { get; set; }682 683        /// <summary>684        /// Gets or sets the command line arguments/parameters that were specified685        /// which will allow for the dynamic parameters to be retrieved and their686        /// metadata merged into the parameter set information.687        /// </summary>688        internal object[] Arguments { get; set; }689 690        internal static Collection<CommandParameterSetInfo> GetCacheableMetadata(CommandMetadata metadata)691        {692            return GetParameterMetadata(metadata, metadata.StaticCommandParameterMetadata);693        }694 695        internal static Collection<CommandParameterSetInfo> GetParameterMetadata(CommandMetadata metadata, MergedCommandParameterMetadata parameterMetadata)696        {697            Collection<CommandParameterSetInfo> result = new Collection<CommandParameterSetInfo>();698 699            if (parameterMetadata != null)700            {701                if (parameterMetadata.ParameterSetCount == 0)702                {703                    const string parameterSetName = ParameterAttribute.AllParameterSets;704 705                    result.Add(706                        new CommandParameterSetInfo(707                            parameterSetName,708                            false,709                            uint.MaxValue,710                            parameterMetadata));711                }712                else713                {714                    int parameterSetCount = parameterMetadata.ParameterSetCount;715                    for (int index = 0; index < parameterSetCount; ++index)716                    {717                        uint currentFlagPosition = (uint)0x1 << index;718 719                        // Get the parameter set name720                        string parameterSetName = parameterMetadata.GetParameterSetName(currentFlagPosition);721 722                        // Is the parameter set the default?723                        bool isDefaultParameterSet = (currentFlagPosition & metadata.DefaultParameterSetFlag) != 0;724 725                        result.Add(726                            new CommandParameterSetInfo(727                                parameterSetName,728                                isDefaultParameterSet,729                                currentFlagPosition,730                                parameterMetadata));731                    }732                }733            }734 735            return result;736        }737    }738 739    /// <summary>740    /// Represents <see cref="System.Type"/>, but can be used where a real type741    /// might not be available, in which case the name of the type can be used.742    /// </summary>743    public class PSTypeName744    {745        /// <summary>746        /// This constructor is used when the type exists and is currently loaded.747        /// </summary>748        /// <param name="type">The type.</param>749        public PSTypeName(Type type)750        {751            _type = type;752            if (_type != null)753            {754                Name = _type.FullName;755            }756        }757 758        /// <summary>759        /// This constructor is used when the type may not exist, or is not loaded.760        /// </summary>761        /// <param name="name">The name of the type.</param>762        public PSTypeName(string name)763        {764            Name = name;765            _type = null;766        }767 768        /// <summary>769        /// This constructor is used when the creating a PSObject with a custom typename.770        /// </summary>771        /// <param name="name">The name of the type.</param>772        /// <param name="type">The real type.</param>773        public PSTypeName(string name, Type type)774        {775            Name = name;776            _type = type;777        }778 779        /// <summary>780        /// This constructor is used when the type is defined in PowerShell.781        /// </summary>782        /// <param name="typeDefinitionAst">The type definition from the ast.</param>783        public PSTypeName(TypeDefinitionAst typeDefinitionAst)784        {785            if (typeDefinitionAst == null)786            {787                throw PSTraceSource.NewArgumentNullException(nameof(typeDefinitionAst));788            }789 790            TypeDefinitionAst = typeDefinitionAst;791            Name = typeDefinitionAst.Name;792        }793 794        /// <summary>795        /// This constructor creates a type from a ITypeName.796        /// </summary>797        public PSTypeName(ITypeName typeName)798        {799            if (typeName == null)800            {801                throw PSTraceSource.NewArgumentNullException(nameof(typeName));802            }803 804            _type = typeName.GetReflectionType();805            if (_type != null)806            {807                Name = _type.FullName;808            }809            else810            {811                var t = typeName as TypeName;812                if (t != null && t._typeDefinitionAst != null)813                {814                    TypeDefinitionAst = t._typeDefinitionAst;815                    Name = TypeDefinitionAst.Name;816                }817                else818                {819                    _type = null;820                    Name = typeName.FullName;821                }822            }823        }824 825        /// <summary>826        /// Return the name of the type.827        /// </summary>828        public string Name { get; }829 830        /// <summary>831        /// Return the type with metadata, or null if the type is not loaded.832        /// </summary>833        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]834        public Type Type835        {836            get837            {838                if (!_typeWasCalculated)839                {840                    if (_type == null)841                    {842                        if (TypeDefinitionAst != null)843                        {844                            _type = TypeDefinitionAst.Type;845                        }846                        else847                        {848                            TypeResolver.TryResolveType(Name, out _type);849                        }850                    }851 852                    if (_type == null)853                    {854                        // We ignore the exception.855                        if (Name != null &&856                            Name.StartsWith('[') &&857                            Name.EndsWith(']'))858                        {859                            string tmp = Name.Substring(1, Name.Length - 2);860                            TypeResolver.TryResolveType(tmp, out _type);861                        }862                    }863 864                    _typeWasCalculated = true;865                }866 867                return _type;868            }869        }870 871        private Type _type;872 873        /// <summary>874        /// When a type is defined by PowerShell, the ast for that type.875        /// </summary>876        public TypeDefinitionAst TypeDefinitionAst { get; }877 878        private bool _typeWasCalculated;879 880        /// <summary>881        /// Returns a String that represents the current PSTypeName.882        /// </summary>883        /// <returns>String that represents the current PSTypeName.</returns>884        public override string ToString()885        {886            return Name ?? string.Empty;887        }888    }889 890    [DebuggerDisplay("{PSTypeName} {Name}")]891    internal readonly struct PSMemberNameAndType892    {893        public readonly string Name;894 895        public readonly PSTypeName PSTypeName;896 897        public readonly object Value;898 899        public PSMemberNameAndType(string name, PSTypeName typeName, object value = null)900        {901            Name = name;902            PSTypeName = typeName;903            Value = value;904        }905    }906 907    /// <summary>908    /// Represents dynamic types such as <see cref="System.Management.Automation.PSObject"/>,909    /// but can be used where a real type might not be available, in which case the name of the type can be used.910    /// The type encodes the members of dynamic objects in the type name.911    /// </summary>912    internal sealed class PSSyntheticTypeName : PSTypeName913    {914        internal static PSSyntheticTypeName Create(string typename, IList<PSMemberNameAndType> membersTypes) => Create(new PSTypeName(typename), membersTypes);915 916        internal static PSSyntheticTypeName Create(Type type, IList<PSMemberNameAndType> membersTypes) => Create(new PSTypeName(type), membersTypes);917 918        internal static PSSyntheticTypeName Create(PSTypeName typename, IList<PSMemberNameAndType> membersTypes)919        {920            var typeName = GetMemberTypeProjection(typename.Name, membersTypes);921            var members = new List<PSMemberNameAndType>();922            members.AddRange(membersTypes);923            members.Sort(static (c1, c2) => string.Compare(c1.Name, c2.Name, StringComparison.OrdinalIgnoreCase));924            return new PSSyntheticTypeName(typeName, typename.Type, members);925        }926 927        private PSSyntheticTypeName(string typeName, Type type, IList<PSMemberNameAndType> membersTypes)928        : base(typeName, type)929        {930            Members = membersTypes;931            if (type != typeof(PSObject))932            {933                return;934            }935 936            for (int i = 0; i < Members.Count; i++)937            {938                var psMemberNameAndType = Members[i];939                if (IsPSTypeName(psMemberNameAndType))940                {941                    Members.RemoveAt(i);942                    break;943                }944            }945        }946 947        private static bool IsPSTypeName(in PSMemberNameAndType member) => member.Name.Equals(nameof(PSTypeName), StringComparison.OrdinalIgnoreCase);948 949        private static string GetMemberTypeProjection(string typename, IList<PSMemberNameAndType> members)950        {951            if (typename == typeof(PSObject).FullName)952            {953                foreach (var mem in members)954                {955                    if (IsPSTypeName(mem))956                    {957                        typename = mem.Value.ToString();958                    }959                }960            }961 962            var builder = new StringBuilder(typename, members.Count * 7);963            builder.Append('#');964            foreach (var m in members.OrderBy(static m => m.Name))965            {966                if (!IsPSTypeName(m))967                {968                    builder.Append(m.Name).Append(':');969                }970            }971 972            builder.Length--;973            return builder.ToString();974        }975 976        public IList<PSMemberNameAndType> Members { get; }977    }978 979#nullable enable980    internal interface IScriptCommandInfo981    {982        ScriptBlock ScriptBlock { get; }983    }984}985