MegaBites-AI/Windows-powershell
0308
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Diagnostics;8using System.IO;9using System.Management.Automation.Internal;10using System.Management.Automation.Language;11 12namespace System.Management.Automation13{14 /// <summary>15 /// Describes how and where this command was invoked.16 /// </summary>17 [DebuggerDisplay("Command = {MyCommand}")]18 public class InvocationInfo19 {20 #region Constructors21 22 /// <summary>23 /// Constructor for InvocationInfo object when the associated command object is present.24 /// </summary>25 /// <param name="command"></param>26 internal InvocationInfo(InternalCommand command)27 : this(command.CommandInfo,28 command.InvocationExtent ?? PositionUtilities.EmptyExtent)29 {30 CommandOrigin = command.CommandOrigin;31 }32 33 /// <summary>34 /// Constructor for InvocationInfo object.35 /// </summary>36 /// <param name="commandInfo">37 /// The command information the invocation info represents.38 /// </param>39 /// <param name="scriptPosition">40 /// The position representing the invocation, or the position representing the error.41 /// </param>42 internal InvocationInfo(CommandInfo commandInfo, IScriptExtent scriptPosition)43 : this(commandInfo, scriptPosition, null)44 {45 // nothing to do here46 }47 48 /// <summary>49 /// Constructor for InvocationInfo object.50 /// </summary>51 /// <param name="commandInfo">52 /// The command information the invocation info represents.53 /// </param>54 /// <param name="scriptPosition">55 /// The position representing the invocation, or the position representing the error.56 /// </param>57 /// <param name="context">58 /// The context in which the InvocationInfo is being created.59 /// </param>60 internal InvocationInfo(CommandInfo commandInfo, IScriptExtent scriptPosition, ExecutionContext context)61 {62 MyCommand = commandInfo;63 CommandOrigin = CommandOrigin.Internal;64 _scriptPosition = scriptPosition;65 66 ExecutionContext contextToUse = null;67 if ((commandInfo != null) && (commandInfo.Context != null))68 {69 contextToUse = commandInfo.Context;70 }71 else if (context != null)72 {73 contextToUse = context;74 }75 76 // Populate the history ID of this command77 if (contextToUse != null)78 {79 Runspaces.LocalRunspace localRunspace = contextToUse.CurrentRunspace as Runspaces.LocalRunspace;80 if (localRunspace != null && localRunspace.History != null)81 {82 HistoryId = localRunspace.History.GetNextHistoryId();83 }84 }85 }86 87 /// <summary>88 /// Creates an InformationalRecord from an instance serialized as a PSObject by ToPSObjectForRemoting.89 /// </summary>90 internal InvocationInfo(PSObject psObject)91 {92 CommandOrigin = (CommandOrigin)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_CommandOrigin");93 ExpectingInput = (bool)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_ExpectingInput");94 _invocationName = (string)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_InvocationName");95 HistoryId = (long)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_HistoryId");96 PipelineLength = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_PipelineLength");97 PipelinePosition = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_PipelinePosition");98 99 string scriptName = (string)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_ScriptName");100 int scriptLineNumber = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_ScriptLineNumber");101 int offsetInLine = (int)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_OffsetInLine");102 string line = (string)SerializationUtilities.GetPropertyValue(psObject, "InvocationInfo_Line");103 var scriptPosition = new ScriptPosition(scriptName, scriptLineNumber, offsetInLine, line);104 105 ScriptPosition scriptEndPosition;106 if (!string.IsNullOrEmpty(line))107 {108 int endColumn = line.Length + 1;109 scriptEndPosition = new ScriptPosition(scriptName, scriptLineNumber, endColumn, line);110 }111 else112 {113 scriptEndPosition = scriptPosition;114 }115 116 _scriptPosition = new ScriptExtent(scriptPosition, scriptEndPosition);117 118 MyCommand = RemoteCommandInfo.FromPSObjectForRemoting(psObject);119 120 //121 // Arrays are de-serialized as ArrayList so we need to convert the deserialized122 // object into an int[] before assigning to pipelineIterationInfo.123 //124 var list = (ArrayList)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_PipelineIterationInfo");125 if (list != null)126 {127 PipelineIterationInfo = (int[])list.ToArray(typeof(int));128 }129 else130 {131 PipelineIterationInfo = Array.Empty<int>();132 }133 134 //135 // Dictionaries are de-serialized as Hashtables so we need to convert the deserialized object into a dictionary136 // before assigning to CommandLineParameters.137 //138 Hashtable hashtable = (Hashtable)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_BoundParameters");139 140 Dictionary<string, object> dictionary = new Dictionary<string, object>();141 142 if (hashtable != null)143 {144 foreach (DictionaryEntry entry in hashtable)145 {146 dictionary.Add((string)entry.Key, entry.Value);147 }148 }149 150 _boundParameters = dictionary;151 152 //153 // The unbound parameters are de-serialized as an ArrayList, which we need to convert to a List154 //155 var unboundArguments = (ArrayList)SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "InvocationInfo_UnboundArguments");156 157 _unboundArguments = new List<object>();158 159 if (unboundArguments != null)160 {161 foreach (object o in unboundArguments)162 {163 _unboundArguments.Add(o);164 }165 }166 167 object value = SerializationUtilities.GetPropertyValue(psObject, "SerializeExtent");168 bool serializeExtent = false;169 170 if (value != null)171 serializeExtent = (bool)value;172 173 if (serializeExtent)174 DisplayScriptPosition = ScriptExtent.FromPSObjectForRemoting(psObject);175 }176 177 #endregion Constructors178 179 #region Private Data180 181 private IScriptExtent _scriptPosition;182 private string _invocationName;183 private Dictionary<string, object> _boundParameters;184 private List<object> _unboundArguments;185 186 #endregion Internal or Private187 188 #region Public Members189 190 /// <summary>191 /// Provide basic information about the command.192 /// </summary>193 /// <value>may be null</value>194 public CommandInfo MyCommand { get; }195 196 /// <summary>197 /// This member provides a dictionary of the parameters that were bound for this198 /// script or command.199 /// </summary>200 public Dictionary<string, object> BoundParameters201 {202 get203 {204 return _boundParameters ??= new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);205 }206 207 internal set208 {209 _boundParameters = value;210 }211 }212 213 /// <summary>214 /// This member provides a list of the arguments that were not bound to any parameter.215 /// </summary>216 public List<object> UnboundArguments217 {218 get { return _unboundArguments ??= new List<object>(); }219 220 internal set { _unboundArguments = value; }221 }222 223 /// <summary>224 /// The line number in the executing script that contained this cmdlet.225 /// </summary>226 /// <value>The script line number or -1 if not executing in a script.</value>227 public int ScriptLineNumber228 {229 get { return ScriptPosition.StartLineNumber; }230 }231 232 /// <summary>233 /// Command's character offset in that line. If the command was234 /// executed directly through the host interfaces, this will be -1.235 /// </summary>236 /// <value>The line offset or -1 if not executed from a text line.</value>237 public int OffsetInLine238 {239 get { return ScriptPosition.StartColumnNumber; }240 }241 242 /// <summary>243 /// History ID that represents the command. If unavailable, this will be -1.244 /// </summary>245 /// <value>The history ID or -1 if not available.</value>246 public long HistoryId { get; internal set; } = -1;247 248 /// <summary>249 /// The name of the script containing the cmdlet.250 /// </summary>251 /// <value>The script name or "" if there was no script.</value>252 public string ScriptName253 {254 get { return ScriptPosition.File ?? string.Empty; }255 }256 257 /// <summary>258 /// The text of the line that contained this cmdlet invocation.259 /// </summary>260 /// <value>Line that was entered to invoke this command</value>261 public string Line262 {263 get264 {265 if (ScriptPosition.StartScriptPosition != null)266 {267 return ScriptPosition.StartScriptPosition.Line;268 }269 270 return string.Empty;271 }272 }273 274 /// <summary>275 /// The full text of the invocation statement, may span multiple lines.276 /// </summary>277 /// <value>Statement that was entered to invoke this command.</value>278 public string Statement279 {280 get281 {282 return ScriptPosition.Text;283 }284 }285 286 /// <summary>287 /// Formatted message indicating where the cmdlet appeared288 /// in the line.289 /// </summary>290 /// <value>Formatted string indicating the command's position in the line</value>291 public string PositionMessage292 {293 get { return PositionUtilities.VerboseMessage(ScriptPosition); }294 }295 296 /// <summary>297 /// This property tells you the directory from where you were being invoked.298 /// </summary>299 public string PSScriptRoot300 {301 get302 {303 if (!string.IsNullOrEmpty(ScriptPosition.File))304 {305 return Path.GetDirectoryName(ScriptPosition.File);306 }307 else308 {309 return string.Empty;310 }311 }312 }313 314 /// <summary>315 /// This property tells you the full path to the command from where you were being invoked.316 /// </summary>317 public string PSCommandPath318 {319 get { return ScriptPosition.File; }320 }321 322 /// <summary>323 /// Command name used to invoke this string - if invoked through an alias, then324 /// this would be the alias name.325 /// </summary>326 /// <value>The name string.</value>327 public string InvocationName328 {329 get { return _invocationName ?? string.Empty; }330 331 internal set { _invocationName = value; }332 }333 334 /// <summary>335 /// How many elements are in the containing pipeline.336 /// </summary>337 /// <value>number of elements in the containing pipeline</value>338 public int PipelineLength { get; internal set; }339 340 /// <summary>341 /// Which element this command was in the containing pipeline.342 /// </summary>343 /// <value>which element this command was in the containing pipeline</value>344 public int PipelinePosition { get; internal set; }345 346 /// <summary>347 /// Is true if this command is expecting input...348 /// </summary>349 public bool ExpectingInput { get; internal set; }350 351 /// <summary>352 /// This property tells you if you were being invoked inside the runspace or353 /// if it was an external request.354 /// </summary>355 public CommandOrigin CommandOrigin { get; internal set; }356 357 /// <summary>358 /// The position for the invocation or error.359 /// </summary>360 public IScriptExtent DisplayScriptPosition { get; set; }361 362 /// <summary>363 /// Create.364 /// </summary>365 /// <param name="commandInfo"></param>366 /// <param name="scriptPosition"></param>367 /// <returns></returns>368 public static InvocationInfo Create(369 CommandInfo commandInfo,370 IScriptExtent scriptPosition)371 {372 var invocationInfo = new InvocationInfo(commandInfo, scriptPosition);373 invocationInfo.DisplayScriptPosition = scriptPosition;374 375 return invocationInfo;376 }377 378 #endregion Public Members379 380 #region Internal Members381 382 /// <summary>383 /// The position for the invocation or error.384 /// </summary>385 internal IScriptExtent ScriptPosition386 {387 get388 {389 if (DisplayScriptPosition != null)390 {391 return DisplayScriptPosition;392 }393 else394 {395 return _scriptPosition;396 }397 }398 399 set400 {401 _scriptPosition = value;402 }403 }404 405 /// <summary>406 /// Returns the full text of the script for this invocation info.407 /// </summary>408 internal string GetFullScript()409 {410 return (ScriptPosition != null) && (ScriptPosition.StartScriptPosition != null) ?411 ScriptPosition.StartScriptPosition.GetFullScript() : null;412 }413 414 /// <summary>415 /// Index of the ProcessRecord iteration for each of the commands in the pipeline.416 /// </summary>417 /// <remarks>418 /// All the commands in a given pipeline share the same PipelinePositionInfo.419 /// </remarks>420 internal int[] PipelineIterationInfo { get; set; } = Array.Empty<int>();421 422 /// <summary>423 /// Adds the information about this informational record to a PSObject as note properties.424 /// The PSObject is used to serialize the record during remote operations.425 /// </summary>426 /// <remarks>427 /// InvocationInfos are usually serialized as part of another object, so we add "InvocationInfo_" to428 /// the note properties to prevent collisions with any properties set by the containing object.429 /// </remarks>430 internal void ToPSObjectForRemoting(PSObject psObject)431 {432 RemotingEncoder.AddNoteProperty<object>(psObject, "InvocationInfo_BoundParameters", () => this.BoundParameters);433 RemotingEncoder.AddNoteProperty<CommandOrigin>(psObject, "InvocationInfo_CommandOrigin", () => this.CommandOrigin);434 RemotingEncoder.AddNoteProperty<bool>(psObject, "InvocationInfo_ExpectingInput", () => this.ExpectingInput);435 RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_InvocationName", () => this.InvocationName);436 RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_Line", () => this.Line);437 RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_OffsetInLine", () => this.OffsetInLine);438 RemotingEncoder.AddNoteProperty<long>(psObject, "InvocationInfo_HistoryId", () => this.HistoryId);439 RemotingEncoder.AddNoteProperty<int[]>(psObject, "InvocationInfo_PipelineIterationInfo", () => this.PipelineIterationInfo);440 RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_PipelineLength", () => this.PipelineLength);441 RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_PipelinePosition", () => this.PipelinePosition);442 RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_PSScriptRoot", () => this.PSScriptRoot);443 RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_PSCommandPath", () => this.PSCommandPath);444 // PositionMessage is ignored when deserializing because it is synthesized from the other position related fields, but445 // it is serialized for backwards compatibility.446 RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_PositionMessage", () => this.PositionMessage);447 RemotingEncoder.AddNoteProperty<int>(psObject, "InvocationInfo_ScriptLineNumber", () => this.ScriptLineNumber);448 RemotingEncoder.AddNoteProperty<string>(psObject, "InvocationInfo_ScriptName", () => this.ScriptName);449 RemotingEncoder.AddNoteProperty<object>(psObject, "InvocationInfo_UnboundArguments", () => this.UnboundArguments);450 451 ScriptExtent extent = DisplayScriptPosition as ScriptExtent;452 if (extent != null)453 {454 extent.ToPSObjectForRemoting(psObject);455 RemotingEncoder.AddNoteProperty(psObject, "SerializeExtent", static () => true);456 }457 else458 {459 RemotingEncoder.AddNoteProperty(psObject, "SerializeExtent", static () => false);460 }461 462 RemoteCommandInfo.ToPSObjectForRemoting(this.MyCommand, psObject);463 }464 465 #endregion Internal Members466 }467 468 /// <summary>469 /// A CommandInfo that has been serialized/deserialized as part of an InvocationInfo during a remote invocation.470 /// </summary>471 public class RemoteCommandInfo : CommandInfo472 {473 /// <summary>474 /// </summary>475 private RemoteCommandInfo(string name, CommandTypes type)476 : base(name, type)477 {478 // nothing to do here479 }480 481 /// <summary>482 /// A string representing the definition of the command.483 /// </summary>484 public override string Definition { get { return _definition; } }485 486 /// <summary>487 /// Creates a RemoteCommandInfo from an instance serialized as a PSObject by ToPSObjectForRemoting.488 /// </summary>489 internal static RemoteCommandInfo FromPSObjectForRemoting(PSObject psObject)490 {491 RemoteCommandInfo commandInfo = null;492 493 object ctype = SerializationUtilities.GetPsObjectPropertyBaseObject(psObject, "CommandInfo_CommandType");494 495 if (ctype != null)496 {497 CommandTypes type = RemotingDecoder.GetPropertyValue<CommandTypes>(psObject, "CommandInfo_CommandType");498 string name = RemotingDecoder.GetPropertyValue<string>(psObject, "CommandInfo_Name");499 500 commandInfo = new RemoteCommandInfo(name, type);501 commandInfo._definition = RemotingDecoder.GetPropertyValue<string>(psObject, "CommandInfo_Definition");502 commandInfo.Visibility = RemotingDecoder.GetPropertyValue<SessionStateEntryVisibility>(psObject, "CommandInfo_Visibility");503 }504 505 return commandInfo;506 }507 508 /// <summary>509 /// Adds the information about this instance to a PSObject as note properties.510 /// The PSObject is used to serialize the CommandInfo during remote operations.511 /// </summary>512 /// <remarks>513 /// CommandInfos are usually serialized as part of InvocationInfos, so we add "CommandInfo_" to514 /// the note properties to prevent collisions with any properties set by the containing object.515 /// </remarks>516 internal static void ToPSObjectForRemoting(CommandInfo commandInfo, PSObject psObject)517 {518 if (commandInfo != null)519 {520 RemotingEncoder.AddNoteProperty<CommandTypes>(psObject, "CommandInfo_CommandType", () => commandInfo.CommandType);521 RemotingEncoder.AddNoteProperty<string>(psObject, "CommandInfo_Definition", () => commandInfo.Definition);522 RemotingEncoder.AddNoteProperty<string>(psObject, "CommandInfo_Name", () => commandInfo.Name);523 RemotingEncoder.AddNoteProperty<SessionStateEntryVisibility>(psObject, "CommandInfo_Visibility", () => commandInfo.Visibility);524 }525 }526 527 /// <summary>528 /// NYI.529 /// </summary>530 public override ReadOnlyCollection<PSTypeName> OutputType531 {532 get { return null; }533 }534 535 private string _definition;536 }537}538 