diff --git a/src/System.Management.Automation/engine/runtime/Operations/ClassOps.cs b/src/System.Management.Automation/engine/runtime/Operations/ClassOps.cs
index f707df0679..ba5dda9ff0 100644
--- a/src/System.Management.Automation/engine/runtime/Operations/ClassOps.cs
+++ b/src/System.Management.Automation/engine/runtime/Operations/ClassOps.cs
@@ -70,12 +70,17 @@ namespace System.Management.Automation.Internal
/// It's not intended to be a public API, but because we generate type in a different assembly it has to be public.
/// Return type should be SessionStateInternal, but it violates accessibility consistency, so we use object.
///
+ ///
+ /// By default, PowerShell class instantiation usually happens in the same Runspace where the class is defined. In
+ /// that case, the created instance will be bound to the session state used to define that class in the Runspace.
+ /// However, if the instantiation happens in a different Runspace where the class is not defined, then the created
+ /// instance won't be bound to any session state.
+ ///
/// SessionStateInternal
public object GetSessionState()
{
SessionStateInternal ss = null;
- bool found = _stateMap.TryGetValue(Runspace.DefaultRunspace, out ss);
- Diagnostics.Assert(found, "We always should be able to find corresponding SessionState");
+ _stateMap.TryGetValue(Runspace.DefaultRunspace, out ss);
return ss;
}
}
diff --git a/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1 b/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1
index 26968ecfb1..08b97f1a37 100644
--- a/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1
+++ b/test/powershell/Language/Classes/Scripting.Classes.MiscOps.Tests.ps1
@@ -43,4 +43,75 @@ Describe 'Misc Test' -Tags "CI" {
[C1]::new().Bar() | should be "1;2;3;"
}
}
+
+ Context 'Class instantiation' {
+ Class C1 {
+ [string] Foo() {
+ return (Get-TestText)
+ }
+ }
+
+ BeforeAll {
+ $ExpectedTextFromBoundInstance = "Class C1 was defined in this Runspace"
+ $ExpectedTextFromUnboundInstance = "New Runspace without class C1 defined"
+
+ ## Define 'Get-TestText' in the current Runspace
+ function Get-TestText { return $ExpectedTextFromBoundInstance }
+
+ $NewRunspaceFunctionDefinitions = @"
+ ## Define 'Get-TestText' in the new Runspace
+ function Get-TestText { return '$ExpectedTextFromUnboundInstance' }
+
+ ## Define the function to create an instance of the given type using the default constructor
+ function New-UnboundInstance([Type]`$type) { `$type::new() }
+
+ ## Define the function to call 'Foo()' on the given C1 instance, and return the result
+ function Run-Foo(`$C1Instance) { `$C1Instance.Foo() }
+"@
+ ## Create a new Runspace and define helper functions in it
+ $powershell = [powershell]::Create()
+ $powershell.AddScript($NewRunspaceFunctionDefinitions).Invoke() > $null
+ $powershell.Commands.Clear()
+
+ function InstantiateInNewRunspace([Type]$type) {
+ try {
+ $result = $powershell.AddCommand("New-UnboundInstance").AddParameter("type", $type).Invoke()
+ $result.Count | Should Be 1 > $null
+ return $result[0]
+ } finally {
+ $powershell.Commands.Clear()
+ }
+ }
+
+ function RunFooInNewRunspace($instance) {
+ try {
+ $result = $powershell.AddCommand("Run-Foo").AddParameter("C1Instance", $instance).Invoke()
+ $result.Count | Should Be 1 > $null
+ return $result[0]
+ } finally {
+ $powershell.Commands.Clear()
+ }
+ }
+ }
+
+ AfterAll {
+ $powershell.Dispose()
+ }
+
+ It "Create instance that is bound to a SessionState" {
+ $instance = [C1]::new()
+ ## For a bound class instance, the execution of an instance method is
+ ## done in the Runspace/SessionState the instance is bound to.
+ $instance.Foo() | Should Be $ExpectedTextFromBoundInstance
+ RunFooInNewRunspace $instance | Should Be $ExpectedTextFromBoundInstance
+ }
+
+ It "Create instance that is NOT bound to a SessionState" {
+ $instance = InstantiateInNewRunspace ([C1])
+ ## For an unbound class instance, the execution of an instance method is done in
+ ## the Runspace/SessionState where the call to the instance method is made.
+ $instance.Foo() | Should Be $ExpectedTextFromBoundInstance
+ RunFooInNewRunspace $instance | Should Be $ExpectedTextFromUnboundInstance
+ }
+ }
}