2018-02-13 09:23:53 -08:00
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
2016-03-30 14:20:52 -07:00
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Linq ;
using System.Management.Automation.Internal ;
2018-11-13 16:16:29 -08:00
using System.Management.Automation.Runspaces ;
2016-03-30 14:20:52 -07:00
namespace System.Management.Automation
{
/// <summary>
/// A SessionStateScope defines the scope of visibility for a set
/// of virtual drives and their data.
/// </summary>
internal sealed class SessionStateScope
{
#region constructor
/// <summary>
/// Constructor for a session state scope.
/// </summary>
/// <param name="parentScope">
/// The parent of this scope. It can be null for the global scope.
/// </param>
internal SessionStateScope ( SessionStateScope parentScope )
{
ScopeOrigin = CommandOrigin . Internal ;
Parent = parentScope ;
if ( parentScope != null )
{
// Now copy the script: scope stack from the parent
2016-07-29 13:02:49 -07:00
_scriptScope = parentScope . ScriptScope ;
2016-03-30 14:20:52 -07:00
}
else
{
2016-07-29 13:02:49 -07:00
_scriptScope = this ;
2016-03-30 14:20:52 -07:00
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
#endregion constructor
#region Internal properties
/// <summary>
/// Gets the parent scope of this scope. May be null
/// for the global scope.
/// </summary>
internal SessionStateScope Parent { get ; set ; }
/// <summary>
/// Defines the origin of the command that resulted in this scope
/// being created.
/// </summary>
internal CommandOrigin ScopeOrigin { get ; set ; }
/// <summary>
/// The script scope for this scope. It may reference itself but may not
/// be a null reference.
/// </summary>
/// <exception cref="ArgumentNullException">
/// If <paramref name="value"/> is null when setting the property.
/// </exception>
internal SessionStateScope ScriptScope
{
get { return _scriptScope ; }
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
set
{
Diagnostics . Assert ( value != null , "Caller to verify scope is not null" );
_scriptScope = value ;
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
private SessionStateScope _scriptScope ;
/// <summary>
/// The version of strict mode for the interpreter.
/// </summary>
/// <value>Which version of strict mode is active for this scope and it's children.</value>
internal Version StrictModeVersion { get ; set ; }
/// <summary>
/// Some local variables are stored in this tuple (for non-global scope, any variable assigned to,
/// or parameters, or some predefined locals.)
/// </summary>
internal MutableTuple LocalsTuple { get ; set ; }
/// <summary>
/// When dotting a script, no new scope is created. Automatic variables must go somewhere, so rather than store
/// them in the scope they are dotted into, we just store them in a tuple like any other local variable so we
/// can skip saving and restoring them as the scopes change, instead it's a simple push/pop of this stack.
2017-01-16 13:31:14 -08:00
///
2016-03-30 14:20:52 -07:00
/// This works because in a dotted script block, the only locals in the tuple are the automatic variables, all
/// other variables use the variable apis to find the variable and get/set it.
/// </summary>
internal Stack < MutableTuple > DottedScopes { get { return _dottedScopes ; } }
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
private readonly Stack < MutableTuple > _dottedScopes = new Stack < MutableTuple >();
#region Drives
/// <summary>
/// Adds a new drive to the scope's drive collection.
/// </summary>
/// <param name="newDrive">
/// The new drive to be added.
/// </param>
/// <remarks>
/// This method assumes the drive has already been verified and
/// the provider has already been notified.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// If <paramref name="newDrive" /> is null.
/// </exception>
/// <exception cref="SessionStateException">
/// If a drive of the same name already exists in this scope.
/// </exception>
internal void NewDrive ( PSDriveInfo newDrive )
{
if ( newDrive == null )
{
throw PSTraceSource . NewArgumentNullException ( "newDrive" );
}
// Ensure that multiple threads do not try to modify the
// drive data at the same time.
var driveInfos = GetDrives ();
if ( driveInfos . ContainsKey ( newDrive . Name ))
{
SessionStateException e =
new SessionStateException (
newDrive . Name ,
SessionStateCategory . Drive ,
"DriveAlreadyExists" ,
SessionStateStrings . DriveAlreadyExists ,
ErrorCategory . ResourceExists );
throw e ;
}
if (! newDrive . IsAutoMounted )
{
driveInfos . Add ( newDrive . Name , newDrive );
}
else
{
var automountedDrives = GetAutomountedDrives ();
if (! automountedDrives . ContainsKey ( newDrive . Name ))
{
automountedDrives . Add ( newDrive . Name , newDrive );
}
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes the specified drive from this scope.
/// </summary>
/// <param name="drive">
/// The drive to be removed.
/// </param>
/// <remarks>
/// This method assumes that the drive has already been validated for removal
/// by the provider.
/// </remarks>
/// <exception cref="ArgumentNullException">
/// If <paramref name="drive" /> is null.
/// </exception>
internal void RemoveDrive ( PSDriveInfo drive )
{
if ( drive == null )
{
throw PSTraceSource . NewArgumentNullException ( "drive" );
}
if ( _drives == null )
return ;
var driveInfos = GetDrives ();
if (! driveInfos . Remove ( drive . Name ))
{
// Check to see if it is in the automounted drive collection.
var automountedDrives = GetAutomountedDrives ();
PSDriveInfo automountedDrive ;
if ( automountedDrives . TryGetValue ( drive . Name , out automountedDrive ))
{
automountedDrive . IsAutoMountedManuallyRemoved = true ;
// Remove ths persisted from the list of automounted drives.
if ( drive . IsNetworkDrive )
{
automountedDrives . Remove ( drive . Name );
}
}
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes all the drives from the scope.
/// </summary>
internal void RemoveAllDrives ()
{
GetDrives (). Clear ();
GetAutomountedDrives (). Clear ();
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Retrieves the drive of the specified name.
/// </summary>
/// <param name="name">
/// The name of the drive to retrieve.
/// </param>
/// <returns>
/// An instance of a PSDriveInfo object with the specified name if one
/// exists in this scope or null if one does not exist.
/// </returns>
/// <exception cref="ArgumentNullException">
/// If <paramref name="name" /> is null.
/// </exception>
internal PSDriveInfo GetDrive ( string name )
{
if ( name == null )
{
throw PSTraceSource . NewArgumentNullException ( "name" );
}
PSDriveInfo result = null ;
var driveInfos = GetDrives ();
if (! driveInfos . TryGetValue ( name , out result ))
{
// The caller needs to determine what to do with
// manually removed drives.
GetAutomountedDrives (). TryGetValue ( name , out result );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets an IEnumerable for the drives in this scope.
/// </summary>
internal IEnumerable < PSDriveInfo > Drives
{
get
{
Collection < PSDriveInfo > result = new Collection < PSDriveInfo >();
foreach ( PSDriveInfo drive in GetDrives (). Values )
{
result . Add ( drive );
}
// Now add automounted drives that have not been manually
// removed by the user.
foreach ( PSDriveInfo drive in GetAutomountedDrives (). Values )
{
if (! drive . IsAutoMountedManuallyRemoved )
{
result . Add ( drive );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return result ;
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
#endregion Drives
#region Variables
/// <summary>
/// Gets an IDictionary for the variables in this scope.
/// </summary>
internal IDictionary < string , PSVariable > Variables { get { return GetPrivateVariables (); } }
/// <summary>
/// Gets the specified variable from the variable table.
/// </summary>
/// <param name="name">
/// The name of the variable to retrieve.
/// </param>
/// <param name="origin">
/// The origin of the command trying to retrieve this variable...
/// </param>
/// <returns>
/// The PSVariable representing the variable specified.
/// </returns>
internal PSVariable GetVariable ( string name , CommandOrigin origin )
{
PSVariable result ;
TryGetVariable ( name , origin , false , out result );
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets the specified variable from the variable table.
/// </summary>
/// <param name="name">
/// The name of the variable to retrieve.
/// </param>
/// <returns>
/// The PSVariable representing the variable specified.
/// </returns>
internal PSVariable GetVariable ( string name )
{
return GetVariable ( name , ScopeOrigin );
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Looks up a variable, returns true and the variable if found and is visible, throws if the found variable is not visible,
/// and returns false if there is no variable with the given name in the current scope.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="origin">The command origin (where the scope was created), used to decide if the variable is visible.</param>
2018-12-28 16:39:06 +05:00
/// <param name="fromNewOrSet">true if looking up the variable as part of a new or set variable operation.</param>
/// <param name="variable">The variable, if one is found in scope.</param>
2016-03-30 14:20:52 -07:00
/// <exception cref="SessionStateException">Thrown if the variable is not visible based on CommandOrigin.</exception>
/// <returns>True if there is a variable in scope, false otherwise.</returns>
internal bool TryGetVariable ( string name , CommandOrigin origin , bool fromNewOrSet , out PSVariable variable )
{
Diagnostics . Assert ( name != null , "The caller should verify the name" );
if ( TryGetLocalVariableFromTuple ( name , fromNewOrSet , out variable ))
{
SessionState . ThrowIfNotVisible ( origin , variable );
return true ;
}
if ( GetPrivateVariables (). TryGetValue ( name , out variable ))
{
SessionState . ThrowIfNotVisible ( origin , variable );
return true ;
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
return false ;
}
/// <summary>
/// </summary>
/// <param name="variable"></param>
/// <returns></returns>
internal object GetAutomaticVariableValue ( AutomaticVariable variable )
{
int index = ( int ) variable ;
foreach ( var dottedScope in _dottedScopes )
{
if ( dottedScope . IsValueSet ( index ))
{
return dottedScope . GetValue ( index );
}
}
// LocalsTuple should not be null, but the test infrastructure creates scopes
// and doesn't set LocalsTuple
if ( LocalsTuple != null && LocalsTuple . IsValueSet ( index ))
{
return LocalsTuple . GetValue ( index );
}
return AutomationNull . Value ;
}
/// <summary>
/// Sets a variable to the given value.
/// </summary>
/// <param name="name">
/// The name of the variable to set.
/// </param>
/// <param name="value">
/// The value for the variable
/// </param>
/// <param name="asValue">
/// If true, sets the variable value to newValue. If false, newValue must
/// be a PSVariable object and the item will be set rather than the value.
/// </param>
/// <param name="force">
/// If true, the variable will be set even if it is readonly.
/// </param>
/// <param name="sessionState">
/// Which SessionState this variable belongs to.
/// </param>
/// <param name="origin">
/// The origin of the caller
/// </param>
/// <param name="fastPath">
/// If true and the variable is being set in the global scope,
/// then all of the normal variable lookup stuff is bypassed and
/// the variable is added directly to the dictionary.
/// </param>
/// <returns>
/// The PSVariable representing the variable that was set.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the variable is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal PSVariable SetVariable ( string name , object value , bool asValue , bool force , SessionStateInternal sessionState , CommandOrigin origin = CommandOrigin . Internal , bool fastPath = false )
{
Diagnostics . Assert ( name != null , "The caller should verify the name" );
PSVariable variable ;
PSVariable variableToSet = value as PSVariable ;
// Set the variable directly in the table, bypassing all of the checks. This
// can only be used for global scope otherwise the slow path is used.
2016-07-29 13:02:49 -07:00
if ( fastPath )
2016-03-30 14:20:52 -07:00
{
if ( Parent != null )
{
throw new NotImplementedException ( "fastPath" );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
variable = new PSVariable ( name , variableToSet . Value , variableToSet . Options , variableToSet . Attributes ) { Description = variableToSet . Description };
GetPrivateVariables ()[ name ] = variable ;
return variable ;
}
bool varExists = TryGetVariable ( name , origin , true , out variable );
// Initialize the private variable dictionary if it's not yet
if ( _variables == null ) { GetPrivateVariables (); }
if (! asValue && variableToSet != null )
{
if ( varExists )
{
// First check the variable to ensure that it
// is not constant or readonly
if ( variable == null || variable . IsConstant || (! force && variable . IsReadOnly ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Variable ,
"VariableNotWritable" ,
SessionStateStrings . VariableNotWritable );
throw e ;
}
if ( variable is LocalVariable
&& ( variableToSet . Attributes . Any () || variableToSet . Options != variable . Options ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Variable ,
"VariableNotWritableRare" ,
SessionStateStrings . VariableNotWritableRare );
throw e ;
}
if ( variable . IsReadOnly && force )
{
_variables . Remove ( name );
varExists = false ;
variable = new PSVariable ( name , variableToSet . Value , variableToSet . Options , variableToSet . Attributes ) { Description = variableToSet . Description };
}
else
{
// Since the variable already exists, copy
// the value, options, description, and attributes
// to it.
variable . Attributes . Clear ();
variable . Value = variableToSet . Value ;
variable . Options = variableToSet . Options ;
variable . Description = variableToSet . Description ;
foreach ( Attribute attr in variableToSet . Attributes )
{
variable . Attributes . Add ( attr );
}
}
}
else
{
// Since the variable doesn't exist, use the new Variable
// object
variable = variableToSet ;
}
}
else if ( variable != null )
{
variable . Value = value ;
}
else
{
variable = ( LocalsTuple != null ? LocalsTuple . TrySetVariable ( name , value ) : null ) ?? new PSVariable ( name , value );
}
if ( ExecutionContext . HasEverUsedConstrainedLanguage )
{
2018-11-13 16:16:29 -08:00
CheckVariableChangeInConstrainedLanguage ( variable );
2016-03-30 14:20:52 -07:00
}
_variables [ name ] = variable ;
variable . SessionState = sessionState ;
return variable ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
2016-09-01 17:21:54 -07:00
/// <summary>
/// Sets a variable to scope without any checks.
/// This is intended to be used only for global scope.
/// </summary>
2018-12-28 16:39:06 +05:00
/// <param name="variableToSet">PSVariable to set.</param>
/// <param name="sessionState">SessionState for variable.</param>
2016-09-01 17:21:54 -07:00
/// <returns></returns>
internal void SetVariableForce ( PSVariable variableToSet , SessionStateInternal sessionState )
{
if ( Parent != null )
{
throw new NotImplementedException ( "SetVariableForce" );
}
variableToSet . SessionState = sessionState ;
GetPrivateVariables ()[ variableToSet . Name ] = variableToSet ;
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets a variable to the given value.
/// </summary>
/// <param name="newVariable">
/// The new variable to create.
/// </param>
/// <param name="force">
/// If true, the variable will be set even if it is readonly.
/// </param>
/// <param name="sessionState">
/// Which SessionState this variable belongs to.
/// </param>
/// <returns>
/// The PSVariable representing the variable that was set.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the variable is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal PSVariable NewVariable ( PSVariable newVariable , bool force , SessionStateInternal sessionState )
{
PSVariable variable ;
bool varExists = TryGetVariable ( newVariable . Name , ScopeOrigin , true , out variable );
if ( varExists )
{
// First check the variable to ensure that it
// is not constant or readonly
if ( variable == null || variable . IsConstant || (! force && variable . IsReadOnly ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
newVariable . Name ,
SessionStateCategory . Variable ,
"VariableNotWritable" ,
SessionStateStrings . VariableNotWritable );
throw e ;
}
if ( variable is LocalVariable )
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
newVariable . Name ,
SessionStateCategory . Variable ,
"VariableNotWritableRare" ,
SessionStateStrings . VariableNotWritableRare );
2016-07-29 13:02:49 -07:00
throw e ;
2016-03-30 14:20:52 -07:00
}
// If the new and old variable are the same then don't bother
// doing the assignment and marking as "removed".
// This can happen when a module variable is imported twice.
if (! ReferenceEquals ( newVariable , variable ))
{
// Mark the old variable as removed...
variable . WasRemoved = true ;
variable = newVariable ;
}
}
else
{
// Since the variable doesn't exist, use the new Variable
// object
variable = newVariable ;
}
if ( ExecutionContext . HasEverUsedConstrainedLanguage )
{
2018-11-13 16:16:29 -08:00
CheckVariableChangeInConstrainedLanguage ( variable );
2016-03-30 14:20:52 -07:00
}
_variables [ variable . Name ] = variable ;
variable . SessionState = sessionState ;
return variable ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes a variable from the variable table.
/// </summary>
/// <param name="name">
/// The name of the variable to remove.
/// </param>
/// <param name="force">
/// If true, the variable will be removed even if its ReadOnly.
/// </param>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// if the variable is constant.
/// </exception>
internal void RemoveVariable ( string name , bool force )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
PSVariable variable = GetVariable ( name );
if ( variable . IsConstant || ( variable . IsReadOnly && ! force ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Variable ,
"VariableNotRemovable" ,
SessionStateStrings . VariableNotRemovable );
throw e ;
}
if ( variable is LocalVariable )
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Variable ,
"VariableNotRemovableRare" ,
SessionStateStrings . VariableNotRemovableRare );
throw e ;
}
_variables . Remove ( name );
// Finally mark the variable itself has having been removed so
// anyone holding a reference to it can be aware of this.
variable . WasRemoved = true ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
internal bool TrySetLocalParameterValue ( string name , object value )
{
foreach ( var dottedScope in _dottedScopes )
{
if ( dottedScope . TrySetParameter ( name , value ))
{
return true ;
}
}
return LocalsTuple != null && LocalsTuple . TrySetParameter ( name , value );
}
/// <summary>
/// For most scopes (global scope being the notable exception), most variables are known ahead of
/// time and stored in a tuple. The names of those variables are stored separately, this method
/// determines if variable name is active in this scope, and if so, returns a wrapper around the
/// tuple to access the property in the tuple for the given variable.
/// </summary>
internal bool TryGetLocalVariableFromTuple ( string name , bool fromNewOrSet , out PSVariable result )
{
foreach ( var dottedScope in _dottedScopes )
{
if ( dottedScope . TryGetLocalVariable ( name , fromNewOrSet , out result ))
{
return true ;
}
}
result = null ;
return LocalsTuple != null && LocalsTuple . TryGetLocalVariable ( name , fromNewOrSet , out result );
}
#endregion variables
#region Aliases
/// <summary>
/// Gets an IEnumerable for the aliases in this scope.
/// </summary>
internal IEnumerable < AliasInfo > AliasTable
{
get
{
return GetAliases (). Values ;
}
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets the specified alias from the alias table.
/// </summary>
/// <param name="name">
/// The name of the alias to retrieve.
/// </param>
/// <returns>
/// The string representing the value of the alias specified.
/// </returns>
internal AliasInfo GetAlias ( string name )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
AliasInfo result ;
GetAliases (). TryGetValue ( name , out result );
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets an alias to the given value.
/// </summary>
/// <param name="name">
/// The name of the alias to set.
/// </param>
/// <param name="value">
/// The value for the alias
/// </param>
/// <param name="context">
/// The execution context for this engine instance.
/// </param>
/// <param name="force">
/// If true, the value will be set even if the alias is ReadOnly.
/// </param>
/// <param name="origin">
/// Origin of the caller of this API
/// </param>
/// <returns>
/// The string representing the value that was set.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// if the alias is read-only or constant.
/// </exception>
internal AliasInfo SetAliasValue ( string name , string value , ExecutionContext context , bool force , CommandOrigin origin )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
var aliasInfos = GetAliases ();
AliasInfo aliasInfo ;
if (! aliasInfos . TryGetValue ( name , out aliasInfo ))
{
aliasInfos [ name ] = new AliasInfo ( name , value , context );
}
else
{
// Make sure the alias isn't constant or readonly
if (( aliasInfo . Options & ScopedItemOptions . Constant ) != 0 ||
(! force && ( aliasInfo . Options & ScopedItemOptions . ReadOnly ) != 0 ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Alias ,
"AliasNotWritable" ,
SessionStateStrings . AliasNotWritable );
throw e ;
}
SessionState . ThrowIfNotVisible ( origin , aliasInfo );
RemoveAliasFromCache ( aliasInfo . Name , aliasInfo . Definition );
if ( force )
{
aliasInfos . Remove ( name );
aliasInfo = new AliasInfo ( name , value , context );
aliasInfos [ name ] = aliasInfo ;
}
else
{
aliasInfo . SetDefinition ( value , false );
}
}
AddAliasToCache ( name , value );
return aliasInfos [ name ];
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets an alias to the given value.
/// </summary>
/// <param name="name">
/// The name of the alias to set.
/// </param>
/// <param name="value">
/// The value for the alias
/// </param>
/// <param name="context">
/// The execution context for this engine instance.
/// </param>
/// <param name="options">
/// The options to set on the alias.
/// </param>
/// <param name="force">
/// If true, the value will be set even if the alias is ReadOnly.
/// </param>
/// <param name="origin">
/// Origin of the caller of this API
/// </param>
/// <returns>
/// The string representing the value that was set.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the alias is read-only or constant.
/// </exception>
internal AliasInfo SetAliasValue (
string name ,
string value ,
ScopedItemOptions options ,
ExecutionContext context ,
bool force ,
CommandOrigin origin )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
var aliasInfos = GetAliases ();
AliasInfo aliasInfo ;
AliasInfo result ;
if (! aliasInfos . TryGetValue ( name , out aliasInfo ))
{
result = new AliasInfo ( name , value , context , options );
aliasInfos [ name ] = result ;
}
else
{
// Make sure the alias isn't constant or readonly
if (( aliasInfo . Options & ScopedItemOptions . Constant ) != 0 ||
(! force && ( aliasInfo . Options & ScopedItemOptions . ReadOnly ) != 0 ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Alias ,
"AliasNotWritable" ,
SessionStateStrings . AliasNotWritable );
throw e ;
}
// Ensure we are not trying to set the alias to constant as this can only be
// done at creation time.
if (( options & ScopedItemOptions . Constant ) != 0 )
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Alias ,
"AliasCannotBeMadeConstant" ,
SessionStateStrings . AliasCannotBeMadeConstant );
throw e ;
}
if (( options & ScopedItemOptions . AllScope ) == 0 &&
( aliasInfo . Options & ScopedItemOptions . AllScope ) != 0 )
{
2017-01-16 13:31:14 -08:00
// user is trying to remove the AllScope option from the alias.
2016-03-30 14:20:52 -07:00
// Do not allow this (as per spec).
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Alias ,
"AliasAllScopeOptionCannotBeRemoved" ,
SessionStateStrings . AliasAllScopeOptionCannotBeRemoved );
throw e ;
}
SessionState . ThrowIfNotVisible ( origin , aliasInfo );
RemoveAliasFromCache ( aliasInfo . Name , aliasInfo . Definition );
if ( force )
{
aliasInfos . Remove ( name );
result = new AliasInfo ( name , value , context , options );
aliasInfos [ name ] = result ;
}
else
{
result = aliasInfo ;
aliasInfo . Options = options ;
aliasInfo . SetDefinition ( value , false );
}
}
AddAliasToCache ( name , value );
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets an alias to the given value.
/// </summary>
/// <param name="aliasToSet">
/// The information about the alias to be set
/// </param>
/// <param name="force">
/// If true, the alias will be set even if there is an existing ReadOnly
/// alias.
/// </param>
/// <param name="origin">
/// Specifies the command origin of the calling command.
/// </param>
/// <returns>
/// The string representing the value that was set.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the alias is read-only or constant.
/// </exception>
internal AliasInfo SetAliasItem ( AliasInfo aliasToSet , bool force , CommandOrigin origin = CommandOrigin . Internal )
{
Diagnostics . Assert (
aliasToSet != null ,
"The caller should verify the aliasToSet" );
var aliasInfos = GetAliases ();
AliasInfo aliasInfo ;
2016-09-26 15:16:35 -07:00
if ( aliasInfos . TryGetValue ( aliasToSet . Name , out aliasInfo ))
2016-03-30 14:20:52 -07:00
{
// An existing alias cannot be set if it is ReadOnly or Constant unless
// force is specified, in which case an existing ReadOnly alias can
// be set.
SessionState . ThrowIfNotVisible ( origin , aliasInfo );
if (( aliasInfo . Options & ScopedItemOptions . Constant ) != 0 ||
(( aliasInfo . Options & ScopedItemOptions . ReadOnly ) != 0 && ! force ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
aliasToSet . Name ,
SessionStateCategory . Alias ,
"AliasNotWritable" ,
SessionStateStrings . AliasNotWritable );
throw e ;
}
if (( aliasToSet . Options & ScopedItemOptions . AllScope ) == 0 &&
( aliasInfo . Options & ScopedItemOptions . AllScope ) != 0 )
{
2017-01-16 13:31:14 -08:00
// user is trying to remove the AllScope option from the alias.
2016-03-30 14:20:52 -07:00
// Do not allow this (as per spec).
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
aliasToSet . Name ,
SessionStateCategory . Alias ,
"AliasAllScopeOptionCannotBeRemoved" ,
SessionStateStrings . AliasAllScopeOptionCannotBeRemoved );
throw e ;
}
RemoveAliasFromCache ( aliasInfo . Name , aliasInfo . Definition );
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
aliasInfos [ aliasToSet . Name ] = aliasToSet ;
AddAliasToCache ( aliasToSet . Name , aliasToSet . Definition );
return aliasToSet ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes a alias from the alias table.
/// </summary>
/// <param name="name">
/// The name of the alias to remove.
/// </param>
/// <param name="force">
/// If true, the alias will be removed even if it is ReadOnly.
/// </param>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the alias is constant.
/// </exception>
internal void RemoveAlias ( string name , bool force )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
// Make sure the alias isn't constant or readonly
var aliasInfos = GetAliases ();
AliasInfo aliasInfo ;
if ( aliasInfos . TryGetValue ( name , out aliasInfo ))
{
if (( aliasInfo . Options & ScopedItemOptions . Constant ) != 0 ||
(! force && ( aliasInfo . Options & ScopedItemOptions . ReadOnly ) != 0 ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Alias ,
"AliasNotRemovable" ,
SessionStateStrings . AliasNotRemovable );
throw e ;
}
RemoveAliasFromCache ( aliasInfo . Name , aliasInfo . Definition );
}
aliasInfos . Remove ( name );
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
#endregion aliases
#region Functions
/// <summary>
/// Gets an IEnumerable for the functions in this scope.
/// </summary>
internal Dictionary < string , FunctionInfo > FunctionTable
{
get
{
return GetFunctions ();
2018-12-08 18:53:30 +02:00
}
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets the specified function from the function table.
/// </summary>
/// <param name="name">
/// The name of the function to retrieve.
/// </param>
/// <returns>
/// A FunctionInfo that is either a FilterInfo or FunctionInfo representing the
/// function or filter.
/// </returns>
internal FunctionInfo GetFunction ( string name )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
FunctionInfo result ;
GetFunctions (). TryGetValue ( name , out result );
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets an function to the given function declaration.
/// </summary>
/// <param name="name">
/// The name of the function to set.
/// </param>
/// <param name="function">
/// The script block that represents the code for the function.
/// </param>
/// <param name="force">
/// If true, the function will be set even if its ReadOnly.
/// </param>
/// <param name="origin">
/// The origin of the caller of this API
/// </param>
/// <param name="context">
/// The execution context for the function/filter.
/// </param>
/// <returns>
/// A FunctionInfo that is either a FilterInfo or FunctionInfo representing the
/// function or filter.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the function is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal FunctionInfo SetFunction (
string name ,
ScriptBlock function ,
bool force ,
CommandOrigin origin ,
ExecutionContext context )
{
return SetFunction ( name , function , null , ScopedItemOptions . Unspecified , force , origin , context );
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets an function to the given function declaration.
/// </summary>
/// <param name="name">
/// The name of the function to set.
/// </param>
/// <param name="function">
/// The script block that represents the code for the function.
/// </param>
/// <param name="originalFunction">
/// The original function (if any) from which the scriptblock was derived.
/// </param>
/// <param name="force">
/// If true, the function will be set even if its ReadOnly.
/// </param>
/// <param name="origin">
/// The origin of the caller of this API
/// </param>
/// <param name="context">
/// The execution context for the function/filter.
/// </param>
/// <returns>
/// A FunctionInfo that is either a FilterInfo or FunctionInfo representing the
/// function or filter.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the function is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal FunctionInfo SetFunction (
string name ,
ScriptBlock function ,
FunctionInfo originalFunction ,
bool force ,
CommandOrigin origin ,
ExecutionContext context )
{
return SetFunction ( name , function , originalFunction , ScopedItemOptions . Unspecified , force , origin , context );
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Sets an function to the given function declaration.
/// </summary>
/// <param name="name">
/// The name of the function to set.
/// </param>
/// <param name="function">
/// The script block that the function should represent.
/// </param>
/// <param name="originalFunction">
/// The original function (if any) from which the scriptblock was derived.
/// </param>
/// <param name="options">
/// The options that should be applied to the function.
/// </param>
/// <param name="force">
/// If true, the function will be set even if its ReadOnly.
/// </param>
/// <param name="origin">
/// The origin of the caller of this API
/// </param>
/// <param name="context">
/// The execution context for the function/filter.
/// </param>
/// <returns>
/// A FunctionInfo that is either a FilterInfo or FunctionInfo representing the
/// function or filter.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the function is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal FunctionInfo SetFunction (
string name ,
ScriptBlock function ,
FunctionInfo originalFunction ,
ScopedItemOptions options ,
bool force ,
CommandOrigin origin ,
ExecutionContext context )
{
return SetFunction ( name , function , originalFunction , options , force , origin , context , null );
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
internal FunctionInfo SetFunction (
string name ,
ScriptBlock function ,
FunctionInfo originalFunction ,
ScopedItemOptions options ,
bool force ,
CommandOrigin origin ,
ExecutionContext context ,
string helpFile )
{
return SetFunction ( name , function , originalFunction , options , force , origin , context , helpFile , CreateFunction );
}
/// <summary>
/// Sets an function to the given function declaration.
/// </summary>
/// <param name="name">
/// The name of the function to set.
/// </param>
/// <param name="function">
/// The script block that the function should represent.
/// </param>
/// <param name="originalFunction">
/// The original function (if any) from which the scriptblock was derived.
/// </param>
/// <param name="options">
/// The options that should be applied to the function.
/// </param>
/// <param name="force">
/// If true, the function will be set even if its ReadOnly.
/// </param>
/// <param name="origin">
/// The origin of the caller of this API
/// </param>
/// <param name="context">
/// The execution context for the function/filter.
/// </param>
/// <param name="helpFile">
/// The name of the help file associated with the function.
/// </param>
/// <param name="functionFactory">
/// Function to create the FunctionInfo.
/// </param>
/// <returns>
/// A FunctionInfo that is either a FilterInfo or FunctionInfo representing the
/// function or filter.
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the function is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal FunctionInfo SetFunction (
string name ,
ScriptBlock function ,
FunctionInfo originalFunction ,
ScopedItemOptions options ,
bool force ,
CommandOrigin origin ,
ExecutionContext context ,
string helpFile ,
Func < string , ScriptBlock , FunctionInfo , ScopedItemOptions , ExecutionContext , string , FunctionInfo > functionFactory )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
var functionInfos = GetFunctions ();
FunctionInfo existingValue ;
FunctionInfo result ;
if (! functionInfos . TryGetValue ( name , out existingValue ))
{
result = functionFactory ( name , function , originalFunction , options , context , helpFile );
functionInfos [ name ] = result ;
if ( IsFunctionOptionSet ( result , ScopedItemOptions . AllScope ))
{
GetAllScopeFunctions ()[ name ] = result ;
}
}
else
{
// Make sure the function isn't constant or readonly
SessionState . ThrowIfNotVisible ( origin , existingValue );
if ( IsFunctionOptionSet ( existingValue , ScopedItemOptions . Constant ) ||
(! force && IsFunctionOptionSet ( existingValue , ScopedItemOptions . ReadOnly )))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Function ,
"FunctionNotWritable" ,
SessionStateStrings . FunctionNotWritable );
throw e ;
}
// Ensure we are not trying to set the function to constant as this can only be
// done at creation time.
if (( options & ScopedItemOptions . Constant ) != 0 )
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Function ,
"FunctionCannotBeMadeConstant" ,
SessionStateStrings . FunctionCannotBeMadeConstant );
throw e ;
}
// Ensure we are not trying to remove the AllScope option
if (( options & ScopedItemOptions . AllScope ) == 0 &&
IsFunctionOptionSet ( existingValue , ScopedItemOptions . AllScope ))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Function ,
"FunctionAllScopeOptionCannotBeRemoved" ,
SessionStateStrings . FunctionAllScopeOptionCannotBeRemoved );
throw e ;
}
FunctionInfo existingFunction = existingValue ;
FunctionInfo newValue = null ;
// If the function type changes (i.e.: function to workflow or back)
// then we need to blast what was there
newValue = functionFactory ( name , function , originalFunction , options , context , helpFile );
bool changesFunctionType = existingFunction . GetType () != newValue . GetType ();
// Since the options are set after the script block, we have to
// forcefully apply the script block if the options will be
// set to not being ReadOnly
if ( changesFunctionType ||
(( existingFunction . Options & ScopedItemOptions . ReadOnly ) != 0 && force ))
{
result = newValue ;
functionInfos [ name ] = newValue ;
}
else
{
bool applyForce = force || ( options & ScopedItemOptions . ReadOnly ) == 0 ;
2016-07-29 13:02:49 -07:00
2016-03-30 14:20:52 -07:00
existingFunction . Update ( newValue , applyForce , options , helpFile );
result = existingFunction ;
}
}
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes a function from the function table.
/// </summary>
/// <param name="name">
/// The name of the function to remove.
/// </param>
/// <param name="force">
/// If true, the function is removed even if it is ReadOnly.
/// </param>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the function is constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal void RemoveFunction ( string name , bool force )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
var functionInfos = GetFunctions ();
FunctionInfo function ;
if ( functionInfos . TryGetValue ( name , out function ))
{
if ( IsFunctionOptionSet ( function , ScopedItemOptions . Constant ) ||
(! force && IsFunctionOptionSet ( function , ScopedItemOptions . ReadOnly )))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException (
name ,
SessionStateCategory . Function ,
"FunctionNotRemovable" ,
SessionStateStrings . FunctionNotRemovable );
throw e ;
}
if ( IsFunctionOptionSet ( function , ScopedItemOptions . AllScope ))
{
GetAllScopeFunctions (). Remove ( name );
}
}
2018-12-23 22:20:06 -08:00
2016-03-30 14:20:52 -07:00
functionInfos . Remove ( name );
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
#endregion functions
#region Cmdlets
/// <summary>
/// Gets an IEnumerable for the cmdlets in this scope.
/// </summary>
internal Dictionary < string , List < CmdletInfo >> CmdletTable
{
get
{
return _cmdlets ;
2018-12-08 18:53:30 +02:00
}
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Gets the specified cmdlet from the cmdlet table.
/// </summary>
/// <param name="name">
/// The name of the cmdlet to retrieve.
/// </param>
/// <returns>
/// A CmdletInfo representing this cmdlet
/// </returns>
internal CmdletInfo GetCmdlet ( string name )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
CmdletInfo result = null ;
List < CmdletInfo > cmdlets ;
if ( _cmdlets . TryGetValue ( name , out cmdlets ))
{
if ( cmdlets != null && cmdlets . Count > 0 )
{
result = cmdlets [ 0 ];
}
}
return result ;
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Adds a cmdlet to the cmdlet cache.
/// </summary>
/// <param name="name">
/// The name of the cmdlet to add.
/// </param>
/// <param name="cmdlet">
/// The cmdlet that should be added.
/// </param>
/// <param name="origin">
/// The origin of the caller of this API
/// </param>
/// <param name="context">
/// The execution context for the cmdlet.
/// </param>
/// <returns>
/// A CmdletInfo representing the cmdlet
/// </returns>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the cmdlet is read-only or constant.
2017-01-16 13:31:14 -08:00
/// </exception>
///
2016-03-30 14:20:52 -07:00
internal CmdletInfo AddCmdletToCache (
string name ,
CmdletInfo cmdlet ,
CommandOrigin origin ,
ExecutionContext context )
{
bool throwNotSupported = false ;
try
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
List < CmdletInfo > cmdlets ;
if (! _cmdlets . TryGetValue ( name , out cmdlets ))
{
cmdlets = new List < CmdletInfo >();
cmdlets . Add ( cmdlet );
_cmdlets . Add ( name , cmdlets );
if (( cmdlet . Options & ScopedItemOptions . AllScope ) != 0 )
{
_allScopeCmdlets [ name ]. Insert ( 0 , cmdlet );
}
}
else
{
if (! string . IsNullOrEmpty ( cmdlet . ModuleName ))
{
// Need to be sure that the existing cmdlet doesn't have the same snapin name
foreach ( CmdletInfo cmdletInfo in cmdlets )
{
if ( string . Equals ( cmdlet . FullName , cmdletInfo . FullName ,
StringComparison . OrdinalIgnoreCase ))
{
if ( cmdlet . ImplementingType == cmdletInfo . ImplementingType )
{
// It is already added in the cache. Do not add it again
return null ;
}
// Otherwise it's an error...
throwNotSupported = true ;
break ;
}
}
}
else
{
// If there's no module name, then see if there is a cmdlet that matches the type
foreach ( CmdletInfo cmdletInfo in cmdlets )
{
if ( cmdlet . ImplementingType == cmdletInfo . ImplementingType )
{
// It's already in the cache so don't need to add it again...
return null ;
}
// Otherwise it's an error...
throwNotSupported = true ;
break ;
}
}
// Insert the cmdlet if a duplicate doesn't already exist
if (! throwNotSupported )
{
cmdlets . Insert ( 0 , cmdlet );
}
}
}
catch ( ArgumentException )
{
throwNotSupported = true ;
}
if ( throwNotSupported )
{
PSNotSupportedException notSupported =
PSTraceSource . NewNotSupportedException (
DiscoveryExceptions . DuplicateCmdletName ,
cmdlet . Name );
throw notSupported ;
}
return _cmdlets [ name ][ 0 ];
2018-12-08 18:53:30 +02:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes a cmdlet from the cmdlet table.
/// </summary>
/// <param name="name">
/// The name of the cmdlet to remove.
/// </param>
/// <param name="index">
/// The index at which to remove the cmdlet
/// If index is -1, remove all cmdlets with that name
/// </param>
/// <param name="force">
/// If true, the cmdlet is removed even if it is ReadOnly.
/// </param>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the cmdlet is constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal void RemoveCmdlet ( string name , int index , bool force )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
List < CmdletInfo > cmdlets ;
if ( _cmdlets . TryGetValue ( name , out cmdlets ))
{
CmdletInfo tempCmdlet = cmdlets [ index ];
if (( tempCmdlet . Options & ScopedItemOptions . AllScope ) != 0 )
{
_allScopeCmdlets [ name ]. RemoveAt ( index );
}
cmdlets . RemoveAt ( index );
// Remove the entry is the list is now empty
if ( cmdlets . Count == 0 )
{
//Remove the key
_cmdlets . Remove ( name );
return ;
}
}
2018-12-20 22:54:10 -08:00
}
2016-03-30 14:20:52 -07:00
/// <summary>
/// Removes a cmdlet entry from the cmdlet table.
/// </summary>
/// <param name="name">
/// The key for the cmdlet entry to remove.
/// </param>
/// <param name="force">
/// If true, the cmdlet entry is removed even if it is ReadOnly.
/// </param>
/// <exception cref="SessionStateUnauthorizedAccessException">
/// If the cmdlet is constant.
2017-01-16 13:31:14 -08:00
/// </exception>
2016-03-30 14:20:52 -07:00
internal void RemoveCmdletEntry ( string name , bool force )
{
Diagnostics . Assert (
name != null ,
"The caller should verify the name" );
_cmdlets . Remove ( name );
2018-12-20 22:54:10 -08:00
}
2016-03-30 14:20:52 -07:00
#endregion Cmdlets
#region Types
private Language . TypeResolutionState _typeResolutionState ;
internal Language . TypeResolutionState TypeResolutionState
{
get
{
2016-08-28 05:44:55 +00:00
// this is kind of our own lazy initialization logic here.
2016-03-30 14:20:52 -07:00
if ( _typeResolutionState == null )
{
if ( this . Parent != null )
{
_typeResolutionState = this . Parent . TypeResolutionState ;
}
else
{
_typeResolutionState = Language . TypeResolutionState . UsingSystem ;
}
}
return _typeResolutionState ;
}
set { _typeResolutionState = value ; }
}
internal IDictionary < string , Type > TypeTable { get ; private set ; }
internal void AddType ( string name , Type type )
{
if ( TypeTable == null )
{
TypeTable = new Dictionary < string , Type >( StringComparer . OrdinalIgnoreCase );
}
TypeTable [ name ] = type ;
}
internal Type LookupType ( string name )
{
if ( TypeTable == null ) return null ;
Type result ;
TypeTable . TryGetValue ( name , out result );
return result ;
}
#endregion Types
#endregion Internal methods
#region Private members
private static bool IsFunctionOptionSet ( FunctionInfo function , ScopedItemOptions options )
{
return ( function . Options & options ) != 0 ;
}
2016-07-29 13:02:49 -07:00
private static FunctionInfo CreateFunction ( string name , ScriptBlock function , FunctionInfo originalFunction ,
2016-03-30 14:20:52 -07:00
ScopedItemOptions options , ExecutionContext context , string helpFile )
{
FunctionInfo newValue = null ;
if ( options == ScopedItemOptions . Unspecified )
{
options = ScopedItemOptions . None ;
}
// First use the copy constructors
if ( originalFunction is FilterInfo )
{
newValue = new FilterInfo ( name , ( FilterInfo ) originalFunction );
}
else if ( originalFunction is ConfigurationInfo )
{
newValue = new ConfigurationInfo ( name , ( ConfigurationInfo ) originalFunction );
}
else if ( originalFunction != null )
{
newValue = new FunctionInfo ( name , originalFunction );
}
// Then use the creation constructors - workflows don't get here because the workflow info
// is created during compilation.
else if ( function . IsFilter ) { newValue = new FilterInfo ( name , function , options , context , helpFile ); }
else if ( function . IsConfiguration )
{
newValue = new ConfigurationInfo ( name , function , options , context , helpFile , function . IsMetaConfiguration ());
}
else newValue = new FunctionInfo ( name , function , options , context , helpFile );
return newValue ;
}
/// <summary>
/// Contains the virtual drives for this scope.
/// </summary>
// Initializing all of the session state items every time we create a new scope causes a measurable
// performance degradation, so we use lazy initialization for all of them.
private Dictionary < string , PSDriveInfo > GetDrives ()
{
2016-08-02 22:25:13 -07:00
return _drives ?? ( _drives = new Dictionary < string , PSDriveInfo >( StringComparer . OrdinalIgnoreCase ));
2016-03-30 14:20:52 -07:00
}
2018-12-22 01:26:06 -08:00
private Dictionary < string , PSDriveInfo > _drives ;
2016-03-30 14:20:52 -07:00
/// <summary>
/// Contains the drives that have been automounted by the system.
/// </summary>
// Initializing all of the session state items every time we create a new scope causes a measurable
// performance degradation, so we use lazy initialization for all of them.
private Dictionary < string , PSDriveInfo > GetAutomountedDrives ()
{
2016-08-02 22:25:13 -07:00
return _automountedDrives ??
( _automountedDrives = new Dictionary < string , PSDriveInfo >( StringComparer . OrdinalIgnoreCase ));
2016-03-30 14:20:52 -07:00
}
2016-08-02 22:25:13 -07:00
2018-12-22 01:26:06 -08:00
private Dictionary < string , PSDriveInfo > _automountedDrives ;
2016-03-30 14:20:52 -07:00
private Dictionary < string , PSVariable > _variables ;
private Dictionary < string , PSVariable > GetPrivateVariables ()
{
if ( _variables == null )
{
// Create the variables collection with the default parameters
_variables = new Dictionary < string , PSVariable >( StringComparer . OrdinalIgnoreCase );
// Create the default variables in the global scope.
// If the variable must propagate to each new scope,
// the AllScope option must be set.
AddSessionStateScopeDefaultVariables ();
}
return _variables ;
}
/// <summary>
/// Add the built-in variables defined by the session state scope.
/// </summary>
internal void AddSessionStateScopeDefaultVariables ()
{
if ( Parent == null )
{
// Create the default variables that are in every scope
// These variables will automatically propagate to new
// scopes since they are marked AllScope.
2016-07-29 13:02:49 -07:00
_variables . Add ( s_nullVar . Name , s_nullVar );
_variables . Add ( s_falseVar . Name , s_falseVar );
_variables . Add ( s_trueVar . Name , s_trueVar );
2016-03-30 14:20:52 -07:00
}
else
{
// Propagate all variables that are marked AllScope.
foreach ( PSVariable variable in Parent . GetPrivateVariables (). Values )
{
if ( variable . IsAllScope )
_variables . Add ( variable . Name , variable );
}
}
}
/// <summary>
/// A collection of the aliases defined for the session.
/// </summary>
// Initializing all of the session state items every time we create a new scope causes a measurable
// performance degradation, so we use lazy initialization for all of them.
private Dictionary < string , AliasInfo > GetAliases ()
{
if ( _alias == null )
{
// Create the alias table
_alias = new Dictionary < string , AliasInfo >( StringComparer . OrdinalIgnoreCase );
if ( Parent != null )
{
// Propagate all aliases that are marked AllScope
foreach ( AliasInfo newAlias in Parent . GetAliases (). Values )
{
if (( newAlias . Options & ScopedItemOptions . AllScope ) != 0 )
{
_alias . Add ( newAlias . Name , newAlias );
}
}
}
}
return _alias ;
}
private Dictionary < string , AliasInfo > _alias ;
/// <summary>
/// A collection of the functions defined in this scope...
/// </summary>
// Initializing all of the session state items every time we create a new scope causes a measurable
// performance degradation, so we use lazy initialization for all of them.
private Dictionary < string , FunctionInfo > GetFunctions ()
{
if ( _functions == null )
{
// Create the functions table
_functions = new Dictionary < string , FunctionInfo >( StringComparer . OrdinalIgnoreCase );
if ( Parent != null && Parent . _allScopeFunctions != null )
{
// Propagate all functions that are marked AllScope
foreach ( FunctionInfo newFunc in Parent . _allScopeFunctions . Values )
{
_functions . Add ( newFunc . Name , newFunc );
}
}
}
return _functions ;
}
private Dictionary < string , FunctionInfo > _functions ;
/// <summary>
/// All entries in this table should also be in the normal function
/// table. The entries in this table are automatically propagated
/// to new scopes.
/// </summary>
// Initializing all of the session state items every time we create a new scope causes a measurable
// performance degradation, so we use lazy initialization for all of them.
private Dictionary < string , FunctionInfo > GetAllScopeFunctions ()
{
if ( _allScopeFunctions == null )
{
if ( Parent != null && Parent . _allScopeFunctions != null )
{
return Parent . _allScopeFunctions ;
}
// Create the "AllScope" functions table
_allScopeFunctions = new Dictionary < string , FunctionInfo >( StringComparer . OrdinalIgnoreCase );
}
return _allScopeFunctions ;
}
private Dictionary < string , FunctionInfo > _allScopeFunctions ;
// The value for the cmdlet cache is a list of CmdletInfo objects because of the following reason
// Import-Module Mod1 -Cmdlet foo
// Import-Module Mod2 -Cmdlet foo
// Remove-Module Mod2
// foo
// The command "foo" from Mod1 is invoked.
// If we do not maintain a list, we break this behavior as we would have over-written Mod1\foo with Mod2\foo and then Mod2 is removed, we have nothing.
private readonly Dictionary < string , List < CmdletInfo >> _cmdlets = new Dictionary < string , List < CmdletInfo >>( StringComparer . OrdinalIgnoreCase );
/// <summary>
/// All entries in this table should also be in the normal cmdlet
/// table. The entries in this table are automatically propagated
/// to new scopes.
/// </summary>
private readonly Dictionary < string , List < CmdletInfo >> _allScopeCmdlets = new Dictionary < string , List < CmdletInfo >>( StringComparer . OrdinalIgnoreCase );
/// <summary>
/// The variable that represents $true in the language.
/// We don't need a new reference in each scope since it
/// is ScopedItemOptions.Constant.
/// </summary>
2016-07-29 13:02:49 -07:00
private static readonly PSVariable s_trueVar =
2016-03-30 14:20:52 -07:00
new PSVariable (
StringLiterals . True ,
true ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
"Boolean True" );
/// <summary>
/// The variable that represents $false in the language.
/// We don't need a new reference in each scope since it
/// is ScopedItemOptions.Constant.
/// </summary>
2016-07-29 13:02:49 -07:00
private static readonly PSVariable s_falseVar =
2016-03-30 14:20:52 -07:00
new PSVariable (
StringLiterals . False ,
false ,
ScopedItemOptions . Constant | ScopedItemOptions . AllScope ,
"Boolean False" );
/// <summary>
/// The variable that represents $null in the language.
/// We don't need a new reference in each scope since it
/// is ScopedItemOptions.Constant.
/// </summary>
2016-07-29 13:02:49 -07:00
private static readonly NullVariable s_nullVar =
2016-03-30 14:20:52 -07:00
new NullVariable ();
#endregion Private members
#region Alias mapping
2016-07-29 13:02:49 -07:00
private Dictionary < string , List < string >> _commandsToAliasesCache = new Dictionary < string , List < string >>( StringComparer . OrdinalIgnoreCase );
2016-03-30 14:20:52 -07:00
/// <summary>
2016-08-28 05:44:55 +00:00
/// Gets the aliases by command name (used by metadata-driven help)
2016-03-30 14:20:52 -07:00
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
internal IEnumerable < string > GetAliasesByCommandName ( string command )
{
List < string > commandsToAliases ;
2016-07-29 13:02:49 -07:00
if ( _commandsToAliasesCache . TryGetValue ( command , out commandsToAliases ))
2016-03-30 14:20:52 -07:00
{
foreach ( string str in commandsToAliases )
{
yield return str ;
}
}
yield break ;
}
/// <summary>
/// </summary>
/// <param name="alias"></param>
/// <param name="value"></param>
private void AddAliasToCache ( string alias , string value )
{
List < string > existingAliases ;
2016-07-29 13:02:49 -07:00
if (! _commandsToAliasesCache . TryGetValue ( value , out existingAliases ))
2016-03-30 14:20:52 -07:00
{
List < string > list = new List < string >();
list . Add ( alias );
2016-07-29 13:02:49 -07:00
_commandsToAliasesCache . Add ( value , list );
2016-03-30 14:20:52 -07:00
}
else
{
if (! existingAliases . Contains ( alias , StringComparer . OrdinalIgnoreCase ))
{
existingAliases . Add ( alias );
}
}
}
/// <summary>
/// </summary>
/// <param name="alias"></param>
/// <param name="value"></param>
private void RemoveAliasFromCache ( string alias , string value )
{
List < string > list ;
2016-07-29 13:02:49 -07:00
if (! _commandsToAliasesCache . TryGetValue ( value , out list ))
2016-03-30 14:20:52 -07:00
{
return ;
}
if ( list . Count <= 1 )
{
2016-07-29 13:02:49 -07:00
_commandsToAliasesCache . Remove ( value );
2016-03-30 14:20:52 -07:00
}
else
{
string itemToRemove = list . FirstOrDefault ( item => item . Equals ( alias , StringComparison . OrdinalIgnoreCase ));
if ( itemToRemove != null )
{
list . Remove ( itemToRemove );
}
}
}
2018-11-13 16:16:29 -08:00
private void CheckVariableChangeInConstrainedLanguage ( PSVariable variable )
{
var context = LocalPipeline . GetExecutionContextFromTLS ();
if ( context ?. LanguageMode == PSLanguageMode . ConstrainedLanguage )
{
if (( variable . Options & ScopedItemOptions . AllScope ) == ScopedItemOptions . AllScope )
{
// Don't let people set AllScope variables in ConstrainedLanguage, as they can be used to
// interfere with the session state of trusted commands.
throw new PSNotSupportedException ();
}
// Mark untrusted values for assignments to 'Global:' variables, and 'Script:' variables in
// a module scope, if it's necessary.
ExecutionContext . MarkObjectAsUntrustedForVariableAssignment ( variable , this , context . EngineSessionState );
}
}
2016-03-30 14:20:52 -07:00
#endregion
2018-12-08 18:53:30 +02:00
}
}
2016-03-30 14:20:52 -07:00