/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/System.Management.Automation/engine/InvocationInfo.cs
537 строк
21 KB
Mathias R. Jessen
Add Statement property to `$MyInvocation` (#19027)
07 мар 2023, 02:01
Не верифицирован
07 мар 2023, 02:01
1925cf6
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Management.Automation.Internal; using System.Management.Automation.Language; namespace System.Management.Automation { /// <summary> /// Describes how and where this command was invoked. /// </summary> [DebuggerDisplay("Command = {MyCommand}")] public class InvocationInfo { #region Constructors /// <summary> /// Constructor for InvocationInfo object when the associated command object is present. /// </summary> /// <param name="command"></param> internal InvocationInfo(InternalCommand command) : this(command.CommandInfo, command.InvocationExtent ?? PositionUtilities.EmptyExtent) { CommandOrigin = command.CommandOrigin; } /// <summary> /// Constructor for InvocationInfo object. /// </summary> /// <param name="commandInfo"> /// The command information the invocation info represents. /// </param> /// <param name="scriptPosition"> /// The position representing the invocation, or the position representing the error. /// </param> internal InvocationInfo(CommandInfo commandInfo, IScriptExtent scriptPosition) : this(commandInfo, scriptPosition, null) { // nothing to do here } /// <summary> /// Constructor for InvocationInfo object. /// </summary> /// <param name="commandInfo"> /// The command information the invocation info represents. /// </param> /// <param name="scriptPosition"> /// The position representing the invocation, or the position representing the error. /// </param> /// <param name="context"> /// The context in which the InvocationInfo is being created. /// </param> internal InvocationInfo(CommandInfo commandInfo, IScriptExtent scriptPosition, ExecutionContext context) { MyCommand = commandInfo; CommandOrigin = CommandOrigin.Internal; _scriptPosition = scriptPosition; ExecutionContext contextToUse = null; if ((commandInfo != null) && (commandInfo.Context != null)) { contextToUse = commandInfo.Context; } else if (context != null) { contextToUse = context; } // Populate the history ID of this command if (contextToUse != null) { Runspaces.LocalRunspace localRunspace = contextToUse.CurrentRunspace as Runspaces.LocalRunspace; if (localRunspace != null && localRunspace.History != null) { HistoryId = localRunspace.History.GetNextHistoryId(); } } } /// <summary> /// Creates an InformationalRecord from an instance serialized as a PSObject by ToPSObjectForRemoting. /// </summary> internal InvocationInfo(PSObject psObject) { CommandOrigin = (CommandOrigin)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_CommandOrigin"); ExpectingInput = (bool)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_ExpectingInput"); _invocationName = (string)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_InvocationName"); HistoryId = (long)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_HistoryId"); PipelineLength = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_PipelineLength"); PipelinePosition = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_PipelinePosition"); string scriptName = (string)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_ScriptName"); int scriptLineNumber = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_ScriptLineNumber"); int offsetInLine = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_OffsetInLine"); string line = (string)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_Line"); var scriptPosition = new ScriptPosition(scriptName, scriptLineNumber, offsetInLine, line); ScriptPosition scriptEndPosition; if (!string.IsNullOrEmpty(line)) { int endColumn = line.Length + 1; scriptEndPosition = new ScriptPosition(scriptName, scriptLineNumber, endColumn, line); } else { scriptEndPosition = scriptPosition; } _scriptPosition = new ScriptExtent(scriptPosition, scriptEndPosition); MyCommand = RemoteCommandInfo.FromPSObjectForRemoting(psObject); // // Arrays are de-serialized as ArrayList so we need to convert the deserialized // object into an int[] before assigning to pipelineIterationInfo. // var list = (ArrayList)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_PipelineIterationInfo"); if (list != null) { PipelineIterationInfo = (int[])list.ToArray(typeof(int)); } else { PipelineIterationInfo = Array.Empty<int>(); } // // Dictionaries are de-serialized as Hashtables so we need to convert the deserialized object into a dictionary // before assigning to CommandLineParameters. // Hashtable hashtable = (Hashtable)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_BoundParameters"); Dictionary<string, object> dictionary = new Dictionary<string, object>(); if (hashtable != null) { foreach (DictionaryEntry entry in hashtable) { dictionary.Add((string)entry.Key, entry.Value); } } _boundParameters = dictionary; // // The unbound parameters are de-serialized as an ArrayList, which we need to convert to a List // var unboundArguments = (ArrayList)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_UnboundArguments"); _unboundArguments = new List<object>(); if (unboundArguments != null) { foreach (object o in unboundArguments) { _unboundArguments.Add(o); } } object value = SerializationUtilities.GetPropertyValue(psObject, "SerializeExtent"); bool serializeExtent = false; if (value != null) serializeExtent = (bool)value; if (serializeExtent) DisplayScriptPosition = ScriptExtent.FromPSObjectForRemoting(psObject); } #endregion Constructors #region Private Data private IScriptExtent _scriptPosition; private string _invocationName; private Dictionary<string, object> _boundParameters; private List<object> _unboundArguments; #endregion Internal or Private #region Public Members /// <summary> /// Provide basic information about the command. /// </summary> /// <value>may be null</value> public CommandInfo MyCommand { get; } /// <summary> /// This member provides a dictionary of the parameters that were bound for this /// script or command. /// </summary> public Dictionary<string, object> BoundParameters { get { return _boundParameters ??= new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); } internal set { _boundParameters = value; } } /// <summary> /// This member provides a list of the arguments that were not bound to any parameter. /// </summary> public List<object> UnboundArguments { get { return _unboundArguments ??= new List<object>(); } internal set { _unboundArguments = value; } } /// <summary> /// The line number in the executing script that contained this cmdlet. /// </summary> /// <value>The script line number or -1 if not executing in a script.</value> public int ScriptLineNumber { get { return ScriptPosition.StartLineNumber; } } /// <summary> /// Command's character offset in that line. If the command was /// executed directly through the host interfaces, this will be -1. /// </summary> /// <value>The line offset or -1 if not executed from a text line.</value> public int OffsetInLine { get { return ScriptPosition.StartColumnNumber; } } /// <summary> /// History ID that represents the command. If unavailable, this will be -1. /// </summary> /// <value>The history ID or -1 if not available.</value> public long HistoryId { get; internal set; } = -1; /// <summary> /// The name of the script containing the cmdlet. /// </summary> /// <value>The script name or "" if there was no script.</value> public string ScriptName { get { return ScriptPosition.File ?? string.Empty; } } /// <summary> /// The text of the line that contained this cmdlet invocation. /// </summary> /// <value>Line that was entered to invoke this command</value> public string Line { get { if (ScriptPosition.StartScriptPosition != null) { return ScriptPosition.StartScriptPosition.Line; } return string.Empty; } } /// <summary> /// The full text of the invocation statement, may span multiple lines. /// </summary> /// <value>Statement that was entered to invoke this command.</value> public string Statement { get { return ScriptPosition.Text; } } /// <summary> /// Formatted message indicating where the cmdlet appeared /// in the line. /// </summary> /// <value>Formatted string indicating the command's position in the line</value> public string PositionMessage { get { return PositionUtilities.VerboseMessage(ScriptPosition); } } /// <summary> /// This property tells you the directory from where you were being invoked. /// </summary> public string PSScriptRoot { get { if (!string.IsNullOrEmpty(ScriptPosition.File)) { return Path.GetDirectoryName(ScriptPosition.File); } else { return string.Empty; } } } /// <summary> /// This property tells you the full path to the command from where you were being invoked. /// </summary> public string PSCommandPath { get { return ScriptPosition.File; } } /// <summary> /// Command name used to invoke this string - if invoked through an alias, then /// this would be the alias name. /// </summary> /// <value>The name string.</value> public string InvocationName { get { return _invocationName ?? string.Empty; } internal set { _invocationName = value; } } /// <summary> /// How many elements are in the containing pipeline. /// </summary> /// <value>number of elements in the containing pipeline</value> public int PipelineLength { get; internal set; } /// <summary> /// Which element this command was in the containing pipeline. /// </summary> /// <value>which element this command was in the containing pipeline</value> public int PipelinePosition { get; internal set; } /// <summary> /// Is true if this command is expecting input... /// </summary> public bool ExpectingInput { get; internal set; } /// <summary> /// This property tells you if you were being invoked inside the runspace or /// if it was an external request. /// </summary> public CommandOrigin CommandOrigin { get; internal set; } /// <summary> /// The position for the invocation or error. /// </summary> public IScriptExtent DisplayScriptPosition { get; set; } /// <summary> /// Create. /// </summary> /// <param name="commandInfo"></param> /// <param name="scriptPosition"></param> /// <returns></returns> public static InvocationInfo Create( CommandInfo commandInfo, IScriptExtent scriptPosition) { var invocationInfo = new InvocationInfo(commandInfo, scriptPosition); invocationInfo.DisplayScriptPosition = scriptPosition; return invocationInfo; } #endregion Public Members #region Internal Members /// <summary> /// The position for the invocation or error. /// </summary> internal IScriptExtent ScriptPosition { get { if (DisplayScriptPosition != null) { return DisplayScriptPosition; } else { return _scriptPosition; } } set { _scriptPosition = value; } } /// <summary> /// Returns the full text of the script for this invocation info. /// </summary> internal string GetFullScript() { return (ScriptPosition != null) && (ScriptPosition.StartScriptPosition != null) ? ScriptPosition.StartScriptPosition.GetFullScript() : null; } /// <summary> /// Index of the ProcessRecord iteration for each of the commands in the pipeline. /// </summary> /// <remarks> /// All the commands in a given pipeline share the same PipelinePositionInfo. /// </remarks> internal int[] PipelineIterationInfo { get; set; } = Array.Empty<int>(); /// <summary> /// Adds the information about this informational record to a PSObject as note properties. /// The PSObject is used to serialize the record during remote operations. /// </summary> /// <remarks> /// InvocationInfos are usually serialized as part of another object, so we add "InvocationInfo_" to /// the note properties to prevent collisions with any properties set by the containing object. /// </remarks> internal void ToPSObjectForRemoting(PSObject psObject) { RemotingEncoder.AddNoteProperty<object>(psObject, "InvocationInfo_BoundParameters", () => this.BoundParameters); RemotingEncoder.AddNoteProperty<CommandOrigin>(psObject, "InvocationInfo_CommandOrigin", () => this.CommandOrigin); RemotingEncoder.AddNoteProperty<bool>(psObject, "InvocationInfo_ExpectingInput", () => this.ExpectingInput); RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_InvocationName", () => this.InvocationName); RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_Line", () => this.Line); RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_OffsetInLine", () => this.OffsetInLine); RemotingEncoder.AddNoteProperty<long>(psObject, "InvocationInfo_HistoryId", () => this.HistoryId); RemotingEncoder.AddNoteProperty<int[]>(psObject, "InvocationInfo_PipelineIterationInfo", () => this.PipelineIterationInfo); RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_PipelineLength", () => this.PipelineLength); RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_PipelinePosition", () => this.PipelinePosition); RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_PSScriptRoot", () => this.PSScriptRoot); RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_PSCommandPath", () => this.PSCommandPath); // PositionMessage is ignored when deserializing because it is synthesized from the other position related fields, but // it is serialized for backwards compatibility. RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_PositionMessage", () => this.PositionMessage); RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_ScriptLineNumber", () => this.ScriptLineNumber); RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_ScriptName", () => this.ScriptName); RemotingEncoder.AddNoteProperty<object>(psObject, "InvocationInfo_UnboundArguments", () => this.UnboundArguments); ScriptExtent extent = DisplayScriptPosition as ScriptExtent; if (extent != null) { extent.ToPSObjectForRemoting(psObject); RemotingEncoder.AddNoteProperty(psObject, "SerializeExtent", static () => true); } else { RemotingEncoder.AddNoteProperty(psObject, "SerializeExtent", static () => false); } RemoteCommandInfo.ToPSObjectForRemoting(this.MyCommand, psObject); } #endregion Internal Members } /// <summary> /// A CommandInfo that has been serialized/deserialized as part of an InvocationInfo during a remote invocation. /// </summary> public class RemoteCommandInfo : CommandInfo { /// <summary> /// </summary> private RemoteCommandInfo(string name, CommandTypes type) : base(name, type) { // nothing to do here } /// <summary> /// A string representing the definition of the command. /// </summary> public override string Definition { get { return _definition; } } /// <summary> /// Creates a RemoteCommandInfo from an instance serialized as a PSObject by ToPSObjectForRemoting. /// </summary> internal static RemoteCommandInfo FromPSObjectForRemoting(PSObject psObject) { RemoteCommandInfo commandInfo = null; object ctype = SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "CommandInfo_CommandType"); if (ctype != null) { CommandTypes type = RemotingDecoder.GetPropertyValue<CommandTypes>(psObject, "CommandInfo_CommandType"); string name = RemotingDecoder.GetPropertyValue<string>(psObject, "CommandInfo_Name"); commandInfo = new RemoteCommandInfo(name, type); commandInfo._definition = RemotingDecoder.GetPropertyValue<string>(psObject, "CommandInfo_Definition"); commandInfo.Visibility = RemotingDecoder.GetPropertyValue<SessionStateEntryVisibility>(psObject, "CommandInfo_Visibility"); } return commandInfo; } /// <summary> /// Adds the information about this instance to a PSObject as note properties. /// The PSObject is used to serialize the CommandInfo during remote operations. /// </summary> /// <remarks> /// CommandInfos are usually serialized as part of InvocationInfos, so we add "CommandInfo_" to /// the note properties to prevent collisions with any properties set by the containing object. /// </remarks> internal static void ToPSObjectForRemoting(CommandInfo commandInfo, PSObject psObject) { if (commandInfo != null) { RemotingEncoder.AddNoteProperty<CommandTypes>(psObject, "CommandInfo_CommandType", () => commandInfo.CommandType); RemotingEncoder.AddNoteProperty<string>(psObject, "CommandInfo_Definition", () => commandInfo.Definition); RemotingEncoder.AddNoteProperty<string>(psObject, "CommandInfo_Name", () => commandInfo.Name); RemotingEncoder.AddNoteProperty<SessionStateEntryVisibility>(psObject, "CommandInfo_Visibility", () => commandInfo.Visibility); } } /// <summary> /// NYI. /// </summary> public override ReadOnlyCollection<PSTypeName> OutputType { get { return null; } } private string _definition; } }