Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 75 additions & 8 deletions src/System.Management.Automation/engine/InitialSessionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2966,13 +2966,20 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport(
HashSet<string> unresolvedCmdsToExpose)
{
RunspaceOpenModuleLoadException exceptionToReturn = null;
List<PSModuleInfo> processedModules = new List<PSModuleInfo>();

foreach (object module in moduleList)
{
string moduleName = module as string;
if (moduleName != null)
{
exceptionToReturn = ProcessOneModule(initializedRunspace, moduleName, null, path, publicCommands);
exceptionToReturn = ProcessOneModule(
initializedRunspace: initializedRunspace,
name: moduleName,
moduleInfoToLoad: null,
path: path,
publicCommands: publicCommands,
processedModules: processedModules);
}
else
{
Expand All @@ -2983,15 +2990,27 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport(
{
// if only name is specified in the module spec, just try import the module
// ie., don't take the performance overhead of calling GetModule.
exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, null, path, publicCommands);
exceptionToReturn = ProcessOneModule(
initializedRunspace: initializedRunspace,
name: moduleSpecification.Name,
moduleInfoToLoad: null,
path: path,
publicCommands: publicCommands,
processedModules: processedModules);
}
else
{
Collection<PSModuleInfo> moduleInfos = ModuleCmdletBase.GetModuleIfAvailable(moduleSpecification, initializedRunspace);

if (moduleInfos != null && moduleInfos.Count > 0)
{
exceptionToReturn = ProcessOneModule(initializedRunspace, moduleSpecification.Name, moduleInfos[0], path, publicCommands);
exceptionToReturn = ProcessOneModule(
initializedRunspace: initializedRunspace,
name: moduleSpecification.Name,
moduleInfoToLoad: moduleInfos[0],
path: path,
publicCommands: publicCommands,
processedModules: processedModules);
}
else
{
Expand Down Expand Up @@ -3040,7 +3059,11 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport(
string commandToMakeVisible = Utils.ParseCommandName(unresolvedCommand, out moduleName);
bool found = false;

foreach (CommandInfo cmd in LookupCommands(commandToMakeVisible, moduleName, initializedRunspace.ExecutionContext))
foreach (CommandInfo cmd in LookupCommands(
commandPattern: commandToMakeVisible,
moduleName: moduleName,
context: initializedRunspace.ExecutionContext,
processedModules: processedModules))
{
if (!found)
{
Expand Down Expand Up @@ -3091,11 +3114,13 @@ private RunspaceOpenModuleLoadException ProcessModulesToImport(
/// <param name="commandPattern"></param>
/// <param name="moduleName"></param>
/// <param name="context"></param>
/// <param name="processedModules"></param>
/// <returns></returns>
private static IEnumerable<CommandInfo> LookupCommands(
string commandPattern,
string moduleName,
ExecutionContext context)
ExecutionContext context,
List<PSModuleInfo> processedModules)
{
bool isWildCardPattern = WildcardPattern.ContainsWildcardCharacters(commandPattern);
var searchOptions = isWildCardPattern ?
Expand All @@ -3109,7 +3134,11 @@ private static IEnumerable<CommandInfo> LookupCommands(
CommandOrigin cmdOrigin = CommandOrigin.Runspace;
while (true)
{
foreach (CommandInfo commandInfo in context.SessionState.InvokeCommand.GetCommands(commandPattern, CommandTypes.All, searchOptions, cmdOrigin))
foreach (CommandInfo commandInfo in context.SessionState.InvokeCommand.GetCommands(
name: commandPattern,
commandTypes: CommandTypes.All,
options: searchOptions,
commandOrigin: cmdOrigin))
{
// If module name is provided then use it to restrict returned results.
if (haveModuleName && !moduleName.Equals(commandInfo.ModuleName, StringComparison.OrdinalIgnoreCase))
Expand Down Expand Up @@ -3139,13 +3168,43 @@ private static IEnumerable<CommandInfo> LookupCommands(
// Next try internal search.
cmdOrigin = CommandOrigin.Internal;
}

// If the command is associated with a module, try finding the command in the imported module list.
// The SessionState function table holds only one command name, and if two or more modules contain
// a command with the same name, only one of them will appear in the function table search above.
if (!found && haveModuleName)
{
var pattern = new WildcardPattern(commandPattern);

foreach (PSModuleInfo moduleInfo in processedModules)
{
if (moduleInfo.Name.Equals(moduleName, StringComparison.OrdinalIgnoreCase))
{
foreach (var cmd in moduleInfo.ExportedCommands.Values)
{
if (pattern.IsMatch(cmd.Name))
{
yield return cmd;
}
}

break;
}
}
}
}

/// <summary>
/// If <paramref name="moduleInfoToLoad"/> is null, import module using <paramref name="name"/>. Otherwise,
/// import module using <paramref name="moduleInfoToLoad"/>
/// </summary>
private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRunspace, string name, PSModuleInfo moduleInfoToLoad, string path, HashSet<CommandInfo> publicCommands)
private RunspaceOpenModuleLoadException ProcessOneModule(
Runspace initializedRunspace,
string name,
PSModuleInfo moduleInfoToLoad,
string path,
HashSet<CommandInfo> publicCommands,
List<PSModuleInfo> processedModules)
{
using (PowerShell pse = PowerShell.Create())
{
Expand Down Expand Up @@ -3182,6 +3241,11 @@ private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRun
c = new CmdletInfo("Out-Default", typeof(OutDefaultCommand), null, null, initializedRunspace.ExecutionContext);
pse.AddCommand(new Command(c));
}
else
{
// For runspace init module processing, pass back the PSModuleInfo to the output pipeline.
cmd.Parameters.Add("PassThru");
}

pse.Runspace = initializedRunspace;
// Module import should be run in FullLanguage mode since it is running in
Expand All @@ -3190,7 +3254,10 @@ private RunspaceOpenModuleLoadException ProcessOneModule(Runspace initializedRun
pse.Runspace.ExecutionContext.LanguageMode = PSLanguageMode.FullLanguage;
try
{
pse.Invoke();
// For runspace init module processing, collect the imported PSModuleInfo returned in the output pipeline.
// In other cases, this collection will be empty.
Collection<PSModuleInfo> moduleInfos = pse.Invoke<PSModuleInfo>();
processedModules.AddRange(moduleInfos);
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7192,6 +7192,9 @@ private static void ImportFunctions(FunctionInfo func, SessionStateInternal targ
CommandOrigin.Internal,
targetSessionState.ExecutionContext);

// Note that the module 'func' and the function table 'functionInfo' instances are now linked
// together (see 'CopiedCommand' in CommandInfo class), so setting visibility on one also
// sets it on the other.
SetCommandVisibility(isImportModulePrivate, functionInfo);
functionInfo.Module = sourceModule;

Expand Down
134 changes: 69 additions & 65 deletions src/System.Management.Automation/engine/SessionStateScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1245,93 +1245,97 @@ internal FunctionInfo SetFunction(
name != null,
"The caller should verify the name");

var functionInfos = GetFunctions();
FunctionInfo existingValue;
Dictionary<string, FunctionInfo> functionInfos = GetFunctions();
FunctionInfo result;
if (!functionInfos.TryGetValue(name, out existingValue))

// Functions are equal only if they have the same name and if they come from the same module (if any).
// If the function is not associated with a module then the info 'ModuleName' property is set to empty string.
// If the new function has the same name of an existing function, but different module names, then the
// existing table function is replaced with the new function.
if (!functionInfos.TryGetValue(name, out FunctionInfo existingValue) ||
(originalFunction != null &&
!existingValue.ModuleName.Equals(originalFunction.ModuleName, StringComparison.OrdinalIgnoreCase)))
{
// Add new function info to function table and return.
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);
return result;
}

throw e;
}
// Update the existing function.

// Ensure we are not trying to set the function to constant as this can only be
// done at creation time.
// Make sure the function isn't constant or readonly.
SessionState.ThrowIfNotVisible(origin, existingValue);

if ((options & ScopedItemOptions.Constant) != 0)
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException(
name,
SessionStateCategory.Function,
"FunctionCannotBeMadeConstant",
SessionStateStrings.FunctionCannotBeMadeConstant);
if (IsFunctionOptionSet(existingValue, ScopedItemOptions.Constant) ||
(!force && IsFunctionOptionSet(existingValue, ScopedItemOptions.ReadOnly)))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException(
name,
SessionStateCategory.Function,
"FunctionNotWritable",
SessionStateStrings.FunctionNotWritable);

throw e;
}
throw e;
}

// Ensure we are not trying to remove the AllScope option
// 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);

if ((options & ScopedItemOptions.AllScope) == 0 &&
IsFunctionOptionSet(existingValue, ScopedItemOptions.AllScope))
{
SessionStateUnauthorizedAccessException e =
new SessionStateUnauthorizedAccessException(
name,
SessionStateCategory.Function,
"FunctionAllScopeOptionCannotBeRemoved",
SessionStateStrings.FunctionAllScopeOptionCannotBeRemoved);
throw e;
}

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);

FunctionInfo existingFunction = existingValue;
FunctionInfo newValue = null;
throw e;
}

// 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);
FunctionInfo existingFunction = existingValue;

bool changesFunctionType = existingFunction.GetType() != newValue.GetType();
// If the function type changes (i.e.: function to workflow or back)
// then we need to replace what was there.
FunctionInfo newValue = functionFactory(name, function, originalFunction, options, context, helpFile);

// 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;
bool changesFunctionType = existingFunction.GetType() != newValue.GetType();

existingFunction.Update(newValue, applyForce, options, helpFile);
result = existingFunction;
}
// 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;
existingFunction.Update(newValue, applyForce, options, helpFile);
result = existingFunction;
}

return result;
Expand Down