MegaBites-AI/Windows-powershell
0372
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 