Skip to content

Pass InitialSessionState instead of LanguageMode #1523

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ private EditorServicesConfig CreateConfigObject()
var profile = (PSObject)GetVariableValue("profile");

var hostInfo = new HostInfo(HostName, HostProfileId, HostVersion);
var iss = Runspace.DefaultRunspace.InitialSessionState;
iss.LanguageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode;
var editorServicesConfig = new EditorServicesConfig(hostInfo, Host, SessionDetailsPath, bundledModulesPath, LogPath)
{
FeatureFlags = FeatureFlags,
Expand All @@ -357,7 +359,7 @@ private EditorServicesConfig CreateConfigObject()
AdditionalModules = AdditionalModules,
LanguageServiceTransport = GetLanguageServiceTransport(),
DebugServiceTransport = GetDebugServiceTransport(),
LanguageMode = Runspace.DefaultRunspace.SessionStateProxy.LanguageMode,
InitialSessionState = iss,
ProfilePaths = new ProfilePathConfig
{
AllUsersAllHosts = GetProfilePathFromProfileObject(profile, ProfileUserKind.AllUsers, ProfileHostKind.AllHosts),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;

namespace Microsoft.PowerShell.EditorServices.Hosting
{
Expand Down Expand Up @@ -111,10 +112,9 @@ public EditorServicesConfig(
public ProfilePathConfig ProfilePaths { get; set; }

/// <summary>
/// The language mode inherited from the orginal PowerShell process.
/// This will be used when creating runspaces so that we honor the same language mode.
/// The InitialSessionState to use when creating runspaces. LanguageMode can be set here.
/// </summary>
public PSLanguageMode LanguageMode { get; internal set; }
public InitialSessionState InitialSessionState { get; internal set; }

public string StartupBanner { get; set; } = @"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ private HostStartupInfo CreateHostStartupInfo()
profilePaths,
_config.FeatureFlags,
_config.AdditionalModules,
_config.LanguageMode,
_config.InitialSessionState,
_config.LogPath,
(int)_config.LogLevel,
consoleReplEnabled: _config.ConsoleRepl != ConsoleReplKind.None,
Expand Down
13 changes: 7 additions & 6 deletions src/PowerShellEditorServices/Hosting/HostStartupInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Host;
using System.Management.Automation.Runspaces;

namespace Microsoft.PowerShell.EditorServices.Hosting
{
Expand Down Expand Up @@ -92,10 +93,10 @@ public sealed class HostStartupInfo
public string LogPath { get; }

/// <summary>
/// The language mode inherited from the orginal PowerShell process.
/// This will be used when creating runspaces so that we honor the same language mode.
/// The initialSessionState will be inherited from the orginal PowerShell process.
/// This will be used when creating runspaces so that we honor the same initialSessionState including allowed modules, cmdlets and language mode.
/// </summary>
public PSLanguageMode LanguageMode { get; }
public InitialSessionState InitialSessionState { get; }

/// <summary>
/// The minimum log level of log events to be logged.
Expand Down Expand Up @@ -135,7 +136,7 @@ public sealed class HostStartupInfo
/// <param name="currentUsersProfilePath">The path to the user specific profile.</param>
/// <param name="featureFlags">Flags of features to enable.</param>
/// <param name="additionalModules">Names or paths of additional modules to import.</param>
/// <param name="languageMode">The language mode inherited from the orginal PowerShell process. This will be used when creating runspaces so that we honor the same language mode.</param>
/// <param name="initialSessionState">The language mode inherited from the orginal PowerShell process. This will be used when creating runspaces so that we honor the same initialSessionState including allowed modules, cmdlets and language mode.</param>
/// <param name="logPath">The path to log to.</param>
/// <param name="logLevel">The minimum log event level.</param>
/// <param name="consoleReplEnabled">Enable console if true.</param>
Expand All @@ -149,7 +150,7 @@ public HostStartupInfo(
ProfilePathInfo profilePaths,
IReadOnlyList<string> featureFlags,
IReadOnlyList<string> additionalModules,
PSLanguageMode languageMode,
InitialSessionState initialSessionState,
string logPath,
int logLevel,
bool consoleReplEnabled,
Expand All @@ -163,7 +164,7 @@ public HostStartupInfo(
ProfilePaths = profilePaths;
FeatureFlags = featureFlags ?? Array.Empty<string>();
AdditionalModules = additionalModules ?? Array.Empty<string>();
LanguageMode = languageMode;
InitialSessionState = initialSessionState;
LogPath = logPath;
LogLevel = logLevel;
ConsoleReplEnabled = consoleReplEnabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static PowerShellContextService()
{
MethodInfo setterInfo = typeof(Runspace).GetProperty("ApartmentState").GetSetMethod();
Delegate setter = Delegate.CreateDelegate(typeof(Action<Runspace, ApartmentState>), firstArgument: null, method: setterInfo);
s_runspaceApartmentStateSetter = (Action<Runspace, ApartmentState>)setter;
s_runspaceApartmentStateSetter = (Action<Runspace, ApartmentState>) setter;
}

if (VersionUtils.IsPS7OrGreater)
Expand Down Expand Up @@ -230,7 +230,7 @@ public static PowerShellContextService Create(
logger);

logger.LogTrace("Creating initial PowerShell runspace");
Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost, hostStartupInfo.LanguageMode);
Runspace initialRunspace = PowerShellContextService.CreateRunspace(psHost, hostStartupInfo.InitialSessionState);
powerShellContext.Initialize(hostStartupInfo.ProfilePaths, initialRunspace, true, hostUserInterface);
powerShellContext.ImportCommandsModuleAsync();

Expand All @@ -251,7 +251,7 @@ public static PowerShellContextService Create(
sendErrorToHost: true);
#pragma warning restore CS4014
}

return powerShellContext;
}

Expand All @@ -263,7 +263,7 @@ public static PowerShellContextService Create(
/// <param name="hostUserInterface">The EditorServicesPSHostUserInterface to use for this instance.</param>
/// <param name="logger">An ILogger implementation to use for this instance.</param>
/// <returns></returns>
public static Runspace CreateRunspace(
public static Runspace CreateTestRunspace(
HostStartupInfo hostDetails,
PowerShellContextService powerShellContext,
EditorServicesPSHostUserInterface hostUserInterface,
Expand All @@ -274,7 +274,7 @@ public static Runspace CreateRunspace(
var psHost = new EditorServicesPSHost(powerShellContext, hostDetails, hostUserInterface, logger);
powerShellContext.ConsoleWriter = hostUserInterface;
powerShellContext.ConsoleReader = hostUserInterface;
return CreateRunspace(psHost, hostDetails.LanguageMode);
return CreateRunspace(psHost, hostDetails.InitialSessionState);
}

/// <summary>
Expand All @@ -283,29 +283,8 @@ public static Runspace CreateRunspace(
/// <param name="psHost">The PSHost that will be used for this Runspace.</param>
/// <param name="languageMode">The language mode inherited from the orginal PowerShell process. This will be used when creating runspaces so that we honor the same language mode.</param>
/// <returns></returns>
public static Runspace CreateRunspace(PSHost psHost, PSLanguageMode languageMode)
public static Runspace CreateRunspace(PSHost psHost, InitialSessionState initialSessionState)
{
InitialSessionState initialSessionState;
if (Environment.GetEnvironmentVariable("PSES_TEST_USE_CREATE_DEFAULT") == "1") {
initialSessionState = InitialSessionState.CreateDefault();
} else {
initialSessionState = InitialSessionState.CreateDefault2();
}

// Create and initialize a new Runspace while honoring the LanguageMode of the original runspace
// that started PowerShell Editor Services. This is because the PowerShell Integrated Console
// should have the same LanguageMode of whatever is set by the system.
initialSessionState.LanguageMode = languageMode;

// We set the process scope's execution policy (which is really the runspace's scope) to
// Bypass so we can import our bundled modules. This is equivalent in scope to the CLI
// argument `-Bypass`, which (for instance) the extension passes. Thus we emulate this
// behavior for consistency such that unit tests can pass in a similar environment.
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
initialSessionState.ExecutionPolicy = ExecutionPolicy.Bypass;
}

Runspace runspace = RunspaceFactory.CreateRunspace(psHost, initialSessionState);

// Windows PowerShell must be hosted in STA mode
Expand Down Expand Up @@ -433,11 +412,6 @@ public void Initialize(
{
this.PromptContext = new LegacyReadLineContext(this);
}

if (VersionUtils.IsWindows)
{
this.SetExecutionPolicy();
}
}

/// <summary>
Expand Down Expand Up @@ -1014,7 +988,7 @@ public Task<IEnumerable<object>> ExecuteScriptStringAsync(
Validate.IsNotNull(nameof(scriptString), scriptString);

PSCommand command = null;
if(CurrentRunspace.Runspace.SessionStateProxy.LanguageMode != PSLanguageMode.FullLanguage)
if (CurrentRunspace.Runspace.SessionStateProxy.LanguageMode != PSLanguageMode.FullLanguage)
{
try
{
Expand All @@ -1029,7 +1003,7 @@ public Task<IEnumerable<object>> ExecuteScriptStringAsync(
}

// fall back to old behavior
if(command == null)
if (command == null)
{
command = new PSCommand().AddScript(scriptString.Trim());
}
Expand Down Expand Up @@ -2152,7 +2126,7 @@ private static string GetStringForPSCommand(PSCommand psCommand)
return stringBuilder.ToString();
}

private void SetExecutionPolicy()
internal void RestoreExecutionPolicy()
{
this.logger.LogTrace("Setting execution policy...");

Expand All @@ -2178,14 +2152,14 @@ private void SetExecutionPolicy()
// set to expected values, so we must sift through those.

ExecutionPolicy policyToSet = ExecutionPolicy.Bypass;
var currentUserPolicy = (ExecutionPolicy)policies[policies.Count - 2].Members["ExecutionPolicy"].Value;
var currentUserPolicy = (ExecutionPolicy) policies[policies.Count - 2].Members["ExecutionPolicy"].Value;
if (currentUserPolicy != ExecutionPolicy.Undefined)
{
policyToSet = currentUserPolicy;
}
else
{
var localMachinePolicy = (ExecutionPolicy)policies[policies.Count - 1].Members["ExecutionPolicy"].Value;
var localMachinePolicy = (ExecutionPolicy) policies[policies.Count - 1].Members["ExecutionPolicy"].Value;
if (localMachinePolicy != ExecutionPolicy.Undefined)
{
policyToSet = localMachinePolicy;
Expand Down Expand Up @@ -2357,7 +2331,7 @@ private void HandleRunspaceStateChanged(object sender, RunspaceStateEventArgs ar
case RunspaceState.Closed:
case RunspaceState.Broken:
// If the runspace closes or fails, pop the runspace
((IHostSupportsInteractiveSession)this).PopRunspace();
((IHostSupportsInteractiveSession) this).PopRunspace();
break;
}
}
Expand All @@ -2369,7 +2343,7 @@ private static IEnumerable<string> GetLoadableProfilePaths(ProfilePathInfo profi
yield break;
}

foreach (string path in new [] { profilePaths.AllUsersAllHosts, profilePaths.AllUsersCurrentHost, profilePaths.CurrentUserAllHosts, profilePaths.CurrentUserCurrentHost })
foreach (string path in new[] { profilePaths.AllUsersAllHosts, profilePaths.AllUsersCurrentHost, profilePaths.CurrentUserAllHosts, profilePaths.CurrentUserCurrentHost })
{
if (path != null && File.Exists(path))
{
Expand Down Expand Up @@ -2401,12 +2375,12 @@ private void StartCommandLoopOnRunspaceAvailable()
void availabilityChangedHandler(object runspace, RunspaceAvailabilityEventArgs eventArgs)
{
if (eventArgs.RunspaceAvailability != RunspaceAvailability.Available ||
this.versionSpecificOperations.IsDebuggerStopped(this.PromptNest, (Runspace)runspace))
this.versionSpecificOperations.IsDebuggerStopped(this.PromptNest, (Runspace) runspace))
{
return;
}

((Runspace)runspace).AvailabilityChanged -= availabilityChangedHandler;
((Runspace) runspace).AvailabilityChanged -= availabilityChangedHandler;
Interlocked.Exchange(ref this.isCommandLoopRestarterSet, 0);
this.ConsoleReader?.StartCommandLoop();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="OmniSharp.Extensions.LanguageClient" Version="0.19.2" />
<PackageReference Include="OmniSharp.Extensions.DebugAdapter.Client" Version="0.19.2" />
<PackageReference Include="xunit" Version="2.4.1" />
Expand Down
28 changes: 20 additions & 8 deletions test/PowerShellEditorServices.Test/PowerShellContextFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
Expand All @@ -13,6 +15,7 @@
using Microsoft.PowerShell.EditorServices.Services;
using Microsoft.PowerShell.EditorServices.Services.PowerShellContext;
using Microsoft.PowerShell.EditorServices.Test.Shared;
using Microsoft.PowerShell.EditorServices.Utility;

namespace Microsoft.PowerShell.EditorServices.Test
{
Expand All @@ -36,11 +39,19 @@ internal static class PowerShellContextFactory
TestUtilities.NormalizePath("../../../../../module"));

public static System.Management.Automation.Runspaces.Runspace InitialRunspace;

public static PowerShellContextService Create(ILogger logger)
{
PowerShellContextService powerShellContext = new PowerShellContextService(logger, null, isPSReadLineEnabled: false);

var initialSessionState = InitialSessionState.CreateDefault();
// We set the process scope's execution policy (which is really the runspace's scope) to
// Bypass so we can import our bundled modules. This is equivalent in scope to the CLI
// argument `-Bypass`, which (for instance) the extension passes. Thus we emulate this
// behavior for consistency such that unit tests can pass in a similar environment.
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
initialSessionState.ExecutionPolicy = ExecutionPolicy.Bypass;
}
HostStartupInfo testHostDetails = new HostStartupInfo(
"PowerShell Editor Services Test Host",
"Test.PowerShellEditorServices",
Expand All @@ -49,27 +60,28 @@ public static PowerShellContextService Create(ILogger logger)
TestProfilePaths,
new List<string>(),
new List<string>(),
// TODO: We want to replace this property with an entire initial session state,
// which would then also control the process-scoped execution policy.
PSLanguageMode.FullLanguage,
initialSessionState,
null,
0,
consoleReplEnabled: false,
usesLegacyReadLine: false,
bundledModulePath: BundledModulePath);

InitialRunspace = PowerShellContextService.CreateRunspace(
InitialRunspace = PowerShellContextService.CreateTestRunspace(
testHostDetails,
powerShellContext,
new TestPSHostUserInterface(powerShellContext, logger),
logger);

powerShellContext.Initialize(
TestProfilePaths,
InitialRunspace,
ownsInitialRunspace: true,
consoleHost: null);

if(VersionUtils.IsWindows)
{
powerShellContext.RestoreExecutionPolicy();
}
return powerShellContext;
}
}
Expand Down