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.Management.Automation.Language;7using System.Management.Automation.Runspaces;8using System.Management.Automation.Security;9 10using Dbg = System.Management.Automation.Diagnostics;11 12namespace System.Management.Automation13{14 /// <summary>15 /// Holds the state of a Monad Shell session.16 /// </summary>17 internal sealed partial class SessionStateInternal18 {19 #region Functions20 21 /// <summary>22 /// Add an new SessionState function entry to this session state object...23 /// </summary>24 /// <param name="entry">The entry to add.</param>25 internal void AddSessionStateEntry(SessionStateFunctionEntry entry)26 {27 ScriptBlock sb = entry.ScriptBlock.Clone();28 29 FunctionInfo fn = this.SetFunction(entry.Name, sb, null, entry.Options, false, CommandOrigin.Internal, this.ExecutionContext, entry.HelpFile, true);30 fn.Visibility = entry.Visibility;31 fn.Module = entry.Module;32 fn.ScriptBlock.LanguageMode = entry.ScriptBlock.LanguageMode ?? PSLanguageMode.FullLanguage;33 }34 35 /// <summary>36 /// Gets a flattened view of the functions that are visible using37 /// the current scope as a reference and filtering the functions in38 /// the other scopes based on the scoping rules.39 /// </summary>40 /// <returns>41 /// An IDictionary representing the visible functions.42 /// </returns>43 internal IDictionary<string, FunctionInfo> GetFunctionTable()44 {45 SessionStateScopeEnumerator scopeEnumerator =46 new SessionStateScopeEnumerator(_currentScope);47 48 Dictionary<string, FunctionInfo> result =49 new Dictionary<string, FunctionInfo>(StringComparer.OrdinalIgnoreCase);50 51 foreach (SessionStateScope scope in scopeEnumerator)52 {53 foreach (FunctionInfo entry in scope.FunctionTable.Values)54 {55 if (!result.ContainsKey(entry.Name))56 {57 result.Add(entry.Name, entry);58 }59 }60 }61 62 return result;63 }64 65 /// <summary>66 /// Gets an IEnumerable for the function table for a given scope.67 /// </summary>68 /// <param name="scopeID">69 /// A scope identifier that is either one of the "special" scopes like70 /// "global", "script", "local", or "private, or a numeric ID of a relative scope71 /// to the current scope.72 /// </param>73 /// <exception cref="ArgumentException">74 /// If <paramref name="scopeID"/> is less than zero, or not75 /// a number and not "script", "global", "local", or "private"76 /// </exception>77 /// <exception cref="ArgumentOutOfRangeException">78 /// If <paramref name="scopeID"/> is less than zero or greater than the number of currently79 /// active scopes.80 /// </exception>81 internal IDictionary<string, FunctionInfo> GetFunctionTableAtScope(string scopeID)82 {83 Dictionary<string, FunctionInfo> result =84 new Dictionary<string, FunctionInfo>(StringComparer.OrdinalIgnoreCase);85 86 SessionStateScope scope = GetScopeByID(scopeID);87 88 foreach (FunctionInfo entry in scope.FunctionTable.Values)89 {90 // Make sure the function/filter isn't private or if it is that the current91 // scope is the same scope the alias was retrieved from.92 93 if ((entry.Options & ScopedItemOptions.Private) == 0 ||94 scope == _currentScope)95 {96 result.Add(entry.Name, entry);97 }98 }99 100 return result;101 }102 103 /// <summary>104 /// List of functions/filters to export from this session state object...105 /// </summary>106 internal List<FunctionInfo> ExportedFunctions { get; } = new List<FunctionInfo>();107 108 internal bool UseExportList { get; set; } = false;109 110 /// <summary>111 /// Set to true when module functions are being explicitly exported using Export-ModuleMember.112 /// </summary>113 internal bool FunctionsExported { get; set; }114 115 /// <summary>116 /// Set to true when any processed module functions are being explicitly exported using '*' wildcard.117 /// </summary>118 internal bool FunctionsExportedWithWildcard119 {120 get121 {122 return _functionsExportedWithWildcard;123 }124 125 set126 {127 Dbg.Assert((value), "This property should never be set/reset to false");128 if (value)129 {130 _functionsExportedWithWildcard = value;131 }132 }133 }134 135 private bool _functionsExportedWithWildcard;136 137 /// <summary>138 /// Set to true if module loading is performed under a manifest that explicitly exports functions (no wildcards)139 /// </summary>140 internal bool ManifestWithExplicitFunctionExport { get; set; }141 142 /// <summary>143 /// Get a functions out of session state.144 /// </summary>145 /// <param name="name">146 /// name of function to look up147 /// </param>148 /// <param name="origin">149 /// Origin of the command that called this API...150 /// </param>151 /// <returns>152 /// The value of the specified function.153 /// </returns>154 /// <exception cref="ArgumentException">155 /// If <paramref name="name"/> is null or empty.156 /// </exception>157 internal FunctionInfo GetFunction(string name, CommandOrigin origin)158 {159 if (string.IsNullOrEmpty(name))160 {161 throw PSTraceSource.NewArgumentException(nameof(name));162 }163 164 FunctionInfo result = null;165 166 FunctionLookupPath lookupPath = new FunctionLookupPath(name);167 168 FunctionScopeItemSearcher searcher =169 new FunctionScopeItemSearcher(this, lookupPath, origin);170 171 if (searcher.MoveNext())172 {173 result = ((IEnumerator<FunctionInfo>)searcher).Current;174 }175 176 return (IsFunctionVisibleInDebugger(result, origin)) ? result : null;177 }178 179 private bool IsFunctionVisibleInDebugger(FunctionInfo fnInfo, CommandOrigin origin)180 {181 // Ensure the returned function item is not exposed across language boundaries when in182 // a debugger breakpoint or nested prompt.183 // A debugger breakpoint/nested prompt has access to all current scoped functions.184 // This includes both running commands from the prompt or via a debugger Action scriptblock.185 186 // Early out.187 // Always allow built-in functions needed for command line debugging.188 if (this.ExecutionContext.LanguageMode == PSLanguageMode.FullLanguage ||189 (fnInfo == null) ||190 (fnInfo.Name.Equals("prompt", StringComparison.OrdinalIgnoreCase)) ||191 (fnInfo.Name.Equals("TabExpansion2", StringComparison.OrdinalIgnoreCase)) ||192 (fnInfo.Name.Equals("Clear-Host", StringComparison.Ordinal)))193 {194 return true;195 }196 197 // Check both InNestedPrompt and Debugger.InBreakpoint to ensure we don't miss a case.198 // Function is not visible if function and context language modes are different.199 var runspace = this.ExecutionContext.CurrentRunspace;200 if ((runspace != null) &&201 (runspace.InNestedPrompt || (runspace.Debugger?.InBreakpoint == true)) &&202 (fnInfo.DefiningLanguageMode.HasValue && (fnInfo.DefiningLanguageMode != this.ExecutionContext.LanguageMode)))203 {204 return false;205 }206 207 return true;208 }209 210 /// <summary>211 /// Get a functions out of session state.212 /// </summary>213 /// <param name="name">214 /// name of function to look up215 /// </param>216 /// <returns>217 /// The value of the specified function.218 /// </returns>219 /// <exception cref="ArgumentException">220 /// If <paramref name="name"/> is null or empty.221 /// </exception>222 internal FunctionInfo GetFunction(string name)223 {224 return GetFunction(name, CommandOrigin.Internal);225 }226 227 private static IEnumerable<string> GetFunctionAliases(IParameterMetadataProvider ipmp)228 {229 if (ipmp == null || ipmp.Body.ParamBlock == null)230 yield break;231 232 var attributes = ipmp.Body.ParamBlock.Attributes;233 foreach (var attributeAst in attributes)234 {235 var attributeType = attributeAst.TypeName.GetReflectionAttributeType();236 if (attributeType == typeof(AliasAttribute))237 {238 var cvv = new ConstantValueVisitor { AttributeArgument = true };239 for (int i = 0; i < attributeAst.PositionalArguments.Count; i++)240 {241 yield return Compiler.s_attrArgToStringConverter.Target(Compiler.s_attrArgToStringConverter,242 attributeAst.PositionalArguments[i].Accept(cvv));243 }244 }245 }246 }247 248 /// <summary>249 /// Set a function in the current scope of session state.250 /// </summary>251 /// <param name="name">252 /// The name of the function to set.253 /// </param>254 /// <param name="function">255 /// The new value of the function being set.256 /// </param>257 /// <param name="origin">258 /// Origin of the caller of this API259 /// </param>260 /// <exception cref="ArgumentException">261 /// If <paramref name="name"/> is null or empty.262 /// </exception>263 /// <exception cref="ArgumentNullException">264 /// If <paramref name="function"/> is null.265 /// </exception>266 /// <exception cref="SessionStateUnauthorizedAccessException">267 /// If the function is read-only or constant.268 /// </exception>269 internal FunctionInfo SetFunctionRaw(270 string name,271 ScriptBlock function,272 CommandOrigin origin)273 {274 if (string.IsNullOrEmpty(name))275 {276 throw PSTraceSource.NewArgumentException(nameof(name));277 }278 279 if (function == null)280 {281 throw PSTraceSource.NewArgumentNullException(nameof(function));282 }283 284 string originalName = name;285 286 FunctionLookupPath path = new FunctionLookupPath(name);287 name = path.UnqualifiedPath;288 289 if (string.IsNullOrEmpty(name))290 {291 SessionStateException exception =292 new SessionStateException(293 originalName,294 SessionStateCategory.Function,295 "ScopedFunctionMustHaveName",296 SessionStateStrings.ScopedFunctionMustHaveName,297 ErrorCategory.InvalidArgument);298 299 throw exception;300 }301 302 ScopedItemOptions options = ScopedItemOptions.None;303 if (path.IsPrivate)304 {305 options |= ScopedItemOptions.Private;306 }307 308 FunctionScopeItemSearcher searcher =309 new FunctionScopeItemSearcher(310 this,311 path,312 origin);313 314 var functionInfo = searcher.InitialScope.SetFunction(name, function, null, options, false, origin, ExecutionContext);315 316 foreach (var aliasName in GetFunctionAliases(function.Ast as IParameterMetadataProvider))317 {318 searcher.InitialScope.SetAliasValue(aliasName, name, ExecutionContext, false, origin);319 }320 321 return functionInfo;322 }323 324 /// <summary>325 /// Set a function in the current scope of session state.326 /// </summary>327 /// <param name="name">328 /// The name of the function to set.329 /// </param>330 /// <param name="function">331 /// The new value of the function being set.332 /// </param>333 /// <param name="originalFunction">334 /// The original function (if any) from which the ScriptBlock is derived.335 /// </param>336 /// <param name="options">337 /// The options to set on the function.338 /// </param>339 /// <param name="force">340 /// If true, the function will be set even if its ReadOnly.341 /// </param>342 /// <param name="origin">343 /// Origin of the caller of this API344 /// </param>345 /// <exception cref="ArgumentException">346 /// If <paramref name="name"/> is null or empty.347 /// </exception>348 /// <exception cref="ArgumentNullException">349 /// If <paramref name="function"/> is null.350 /// </exception>351 /// <exception cref="SessionStateUnauthorizedAccessException">352 /// If the function is read-only or constant.353 /// </exception>354 internal FunctionInfo SetFunction(355 string name,356 ScriptBlock function,357 FunctionInfo originalFunction,358 ScopedItemOptions options,359 bool force,360 CommandOrigin origin)361 {362 return SetFunction(name, function, originalFunction, options, force, origin, ExecutionContext, null);363 }364 365 /// <summary>366 /// Set a function in the current scope of session state.367 /// </summary>368 /// <param name="name">369 /// The name of the function to set.370 /// </param>371 /// <param name="function">372 /// The new value of the function being set.373 /// </param>374 /// <param name="originalFunction">375 /// The original function (if any) from which the ScriptBlock is derived.376 /// </param>377 /// <param name="options">378 /// The options to set on the function.379 /// </param>380 /// <param name="force">381 /// If true, the function will be set even if its ReadOnly.382 /// </param>383 /// <param name="origin">384 /// Origin of the caller of this API385 /// </param>386 /// <param name="helpFile">387 /// The name of the help file associated with the function.388 /// </param>389 /// <exception cref="ArgumentException">390 /// If <paramref name="name"/> is null or empty.391 /// </exception>392 /// <exception cref="ArgumentNullException">393 /// If <paramref name="function"/> is null.394 /// </exception>395 /// <exception cref="SessionStateUnauthorizedAccessException">396 /// If the function is read-only or constant.397 /// </exception>398 internal FunctionInfo SetFunction(399 string name,400 ScriptBlock function,401 FunctionInfo originalFunction,402 ScopedItemOptions options,403 bool force,404 CommandOrigin origin,405 string helpFile)406 {407 return SetFunction(name, function, originalFunction, options, force, origin, ExecutionContext, helpFile, false);408 }409 410 /// <summary>411 /// Set a function in the current scope of session state.412 /// </summary>413 /// <param name="name">414 /// The name of the function to set.415 /// </param>416 /// <param name="function">417 /// The new value of the function being set.418 /// </param>419 /// <param name="originalFunction">420 /// The original function (if any) from which the ScriptBlock is derived.421 /// </param>422 /// <param name="options">423 /// The options to set on the function.424 /// </param>425 /// <param name="force">426 /// If true, the function will be set even if its ReadOnly.427 /// </param>428 /// <param name="origin">429 /// Origin of the caller of this API430 /// </param>431 /// <param name="context">432 /// The execution context for the function.433 /// </param>434 /// <param name="helpFile">435 /// The name of the help file associated with the function.436 /// </param>437 /// <exception cref="ArgumentException">438 /// If <paramref name="name"/> is null or empty.439 /// </exception>440 /// <exception cref="ArgumentNullException">441 /// If <paramref name="function"/> is null.442 /// </exception>443 /// <exception cref="SessionStateUnauthorizedAccessException">444 /// If the function is read-only or constant.445 /// </exception>446 internal FunctionInfo SetFunction(447 string name,448 ScriptBlock function,449 FunctionInfo originalFunction,450 ScopedItemOptions options,451 bool force,452 CommandOrigin origin,453 ExecutionContext context,454 string helpFile)455 {456 return SetFunction(name, function, originalFunction, options, force, origin, context, helpFile, false);457 }458 459 /// <summary>460 /// Set a function in the current scope of session state.461 /// </summary>462 /// <param name="name">463 /// The name of the function to set.464 /// </param>465 /// <param name="function">466 /// The new value of the function being set.467 /// </param>468 /// <param name="originalFunction">469 /// The original function (if any) from which the ScriptBlock is derived.470 /// </param>471 /// <param name="options">472 /// The options to set on the function.473 /// </param>474 /// <param name="force">475 /// If true, the function will be set even if its ReadOnly.476 /// </param>477 /// <param name="origin">478 /// Origin of the caller of this API479 /// </param>480 /// <param name="context">481 /// The execution context for the function.482 /// </param>483 /// <param name="helpFile">484 /// The name of the help file associated with the function.485 /// </param>486 /// <param name="isPreValidated">487 /// Set to true if it is a regular function (meaning, we do not need to check if the script contains JobDefinition Attribute and then process it)488 /// </param>489 /// <exception cref="ArgumentException">490 /// If <paramref name="name"/> is null or empty.491 /// </exception>492 /// <exception cref="ArgumentNullException">493 /// If <paramref name="function"/> is null.494 /// </exception>495 /// <exception cref="SessionStateUnauthorizedAccessException">496 /// If the function is read-only or constant.497 /// </exception>498 internal FunctionInfo SetFunction(499 string name,500 ScriptBlock function,501 FunctionInfo originalFunction,502 ScopedItemOptions options,503 bool force,504 CommandOrigin origin,505 ExecutionContext context,506 string helpFile,507 bool isPreValidated)508 {509 if (string.IsNullOrEmpty(name))510 {511 throw PSTraceSource.NewArgumentException(nameof(name));512 }513 514 if (function == null)515 {516 throw PSTraceSource.NewArgumentNullException(nameof(function));517 }518 519 string originalName = name;520 521 FunctionLookupPath path = new FunctionLookupPath(name);522 name = path.UnqualifiedPath;523 524 if (string.IsNullOrEmpty(name))525 {526 SessionStateException exception =527 new SessionStateException(528 originalName,529 SessionStateCategory.Function,530 "ScopedFunctionMustHaveName",531 SessionStateStrings.ScopedFunctionMustHaveName,532 ErrorCategory.InvalidArgument);533 534 throw exception;535 }536 537 if (path.IsPrivate)538 {539 options |= ScopedItemOptions.Private;540 }541 542 FunctionScopeItemSearcher searcher =543 new FunctionScopeItemSearcher(544 this,545 path,546 origin);547 548 return searcher.InitialScope.SetFunction(name, function, originalFunction, options, force, origin, context, helpFile);549 }550 551 /// <summary>552 /// Set a function in the current scope of session state.553 /// </summary>554 /// <param name="name">555 /// The name of the function to set.556 /// </param>557 /// <param name="function">558 /// The new value of the function being set.559 /// </param>560 /// <param name="originalFunction">561 /// The original function (if any) from which the ScriptBlock is derived.562 /// </param>563 /// <param name="force">564 /// If true, the function will be set even if its ReadOnly.565 /// </param>566 /// <param name="origin">567 /// The origin of the caller568 /// </param>569 /// <exception cref="ArgumentException">570 /// If <paramref name="name"/> is null or empty.571 /// or572 /// If <paramref name="function"/> is not a <see cref="FilterInfo">FilterInfo</see>573 /// or <see cref="FunctionInfo">FunctionInfo</see>574 /// </exception>575 /// <exception cref="ArgumentNullException">576 /// If <paramref name="function"/> is null.577 /// </exception>578 /// <exception cref="SessionStateUnauthorizedAccessException">579 /// If the function is read-only or constant.580 /// </exception>581 internal FunctionInfo SetFunction(582 string name,583 ScriptBlock function,584 FunctionInfo originalFunction,585 bool force,586 CommandOrigin origin)587 {588 if (string.IsNullOrEmpty(name))589 {590 throw PSTraceSource.NewArgumentException(nameof(name));591 }592 593 if (function == null)594 {595 throw PSTraceSource.NewArgumentNullException(nameof(function));596 }597 598 string originalName = name;599 600 FunctionLookupPath path = new FunctionLookupPath(name);601 name = path.UnqualifiedPath;602 603 if (string.IsNullOrEmpty(name))604 {605 SessionStateException exception =606 new SessionStateException(607 originalName,608 SessionStateCategory.Function,609 "ScopedFunctionMustHaveName",610 SessionStateStrings.ScopedFunctionMustHaveName,611 ErrorCategory.InvalidArgument);612 613 throw exception;614 }615 616 ScopedItemOptions options = ScopedItemOptions.None;617 if (path.IsPrivate)618 {619 options |= ScopedItemOptions.Private;620 }621 622 FunctionScopeItemSearcher searcher =623 new FunctionScopeItemSearcher(624 this,625 path,626 origin);627 628 FunctionInfo result = null;629 630 SessionStateScope scope = searcher.InitialScope;631 632 if (searcher.MoveNext())633 {634 scope = searcher.CurrentLookupScope;635 name = searcher.Name;636 637 if (path.IsPrivate)638 {639 // Need to add the Private flag640 FunctionInfo existingFunction = scope.GetFunction(name);641 options |= existingFunction.Options;642 result = scope.SetFunction(name, function, originalFunction, options, force, origin, ExecutionContext);643 }644 else645 {646 result = scope.SetFunction(name, function, force, origin, ExecutionContext);647 }648 }649 else650 {651 if (path.IsPrivate)652 {653 result = scope.SetFunction(name, function, originalFunction, options, force, origin, ExecutionContext);654 }655 else656 {657 result = scope.SetFunction(name, function, force, origin, ExecutionContext);658 }659 }660 661 return result;662 }663 664 /// <summary>665 /// Set a function in the current scope of session state.666 ///667 /// BUGBUG: this overload is preserved because a lot of tests use reflection to668 /// call it. The tests should be fixed and this API eventually removed.669 /// </summary>670 /// <param name="name">671 /// The name of the function to set.672 /// </param>673 /// <param name="function">674 /// The new value of the function being set.675 /// </param>676 /// <param name="force">677 /// If true, the function will be set even if its ReadOnly.678 /// </param>679 /// <exception cref="ArgumentException">680 /// If <paramref name="name"/> is null or empty.681 /// or682 /// If <paramref name="function"/> is not a <see cref="FilterInfo">FilterInfo</see>683 /// or <see cref="FunctionInfo">FunctionInfo</see>684 /// </exception>685 /// <exception cref="ArgumentNullException">686 /// If <paramref name="function"/> is null.687 /// </exception>688 /// <exception cref="SessionStateUnauthorizedAccessException">689 /// If the function is read-only or constant.690 /// </exception>691 internal FunctionInfo SetFunction(string name, ScriptBlock function, bool force)692 {693 return SetFunction(name, function, null, force, CommandOrigin.Internal);694 }695 696 /// <summary>697 /// Removes a function from the function table.698 /// </summary>699 /// <param name="name">700 /// The name of the function to remove.701 /// </param>702 /// <param name="origin">703 /// THe origin of the caller of this API704 /// </param>705 /// <param name="force">706 /// If true, the function is removed even if it is ReadOnly.707 /// </param>708 /// <exception cref="ArgumentException">709 /// If <paramref name="name"/> is null or empty.710 /// </exception>711 /// <exception cref="SessionStateUnauthorizedAccessException">712 /// If the function is constant.713 /// </exception>714 internal void RemoveFunction(string name, bool force, CommandOrigin origin)715 {716 if (string.IsNullOrEmpty(name))717 {718 throw PSTraceSource.NewArgumentException(nameof(name));719 }720 721 // Use the scope enumerator to find an existing function722 723 SessionStateScope scope = _currentScope;724 725 FunctionLookupPath path = new FunctionLookupPath(name);726 727 FunctionScopeItemSearcher searcher =728 new FunctionScopeItemSearcher(729 this,730 path,731 origin);732 733 if (searcher.MoveNext())734 {735 scope = searcher.CurrentLookupScope;736 }737 738 scope.RemoveFunction(name, force);739 }740 741 /// <summary>742 /// Removes a function from the function table.743 /// </summary>744 /// <param name="name">745 /// The name of the function to remove.746 /// </param>747 /// <param name="force">748 /// If true, the function is removed even if it is ReadOnly.749 /// </param>750 /// <exception cref="ArgumentException">751 /// If <paramref name="name"/> is null or empty.752 /// </exception>753 /// <exception cref="SessionStateUnauthorizedAccessException">754 /// If the function is constant.755 /// </exception>756 internal void RemoveFunction(string name, bool force)757 {758 RemoveFunction(name, force, CommandOrigin.Internal);759 }760 761 /// <summary>762 /// Removes a function from the function table763 /// if the function was imported from the given module.764 ///765 /// BUGBUG: This is only used by the implicit remoting functions...766 /// </summary>767 /// <param name="name">768 /// The name of the function to remove.769 /// </param>770 /// <param name="module">771 /// Module the function might be imported from.772 /// </param>773 /// <exception cref="SessionStateUnauthorizedAccessException">774 /// If the function is constant.775 /// </exception>776 internal void RemoveFunction(string name, PSModuleInfo module)777 {778 Dbg.Assert(module != null, "Caller should verify that module parameter is not null");779 780 FunctionInfo func = GetFunction(name) as FunctionInfo;781 if (func != null && func.ScriptBlock != null782 && func.ScriptBlock.File != null783 && func.ScriptBlock.File.Equals(module.Path, StringComparison.OrdinalIgnoreCase))784 {785 RemoveFunction(name, true);786 }787 }788 789 #endregion Functions790 }791}792 