Skip to content

Commit 79b8140

Browse files
authored
Make PowerShell class not affiliate with Runspace when declaring the NoRunspaceAffinity attribute (PowerShell#18138)
1 parent c978d46 commit 79b8140

File tree

6 files changed

+81
-5
lines changed

6 files changed

+81
-5
lines changed

src/System.Management.Automation/engine/parser/PSType.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,4 +1472,18 @@ private static void EmitLdarg(ILGenerator emitter, int c)
14721472
}
14731473
}
14741474
}
1475+
1476+
/// <summary>
1477+
/// The attribute for a PowerShell class to not affiliate with a particular Runspace\SessionState.
1478+
/// </summary>
1479+
[AttributeUsage(AttributeTargets.Class)]
1480+
public sealed class NoRunspaceAffinityAttribute : ParsingBaseAttribute
1481+
{
1482+
/// <summary>
1483+
/// Initializes a new instance of the attribute.
1484+
/// </summary>
1485+
public NoRunspaceAffinityAttribute()
1486+
{
1487+
}
1488+
}
14751489
}

src/System.Management.Automation/engine/parser/TypeResolver.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,7 @@ internal static class CoreTypes
750750
{ typeof(CimConverter), new[] { "cimconverter" } },
751751
{ typeof(ModuleSpecification), null },
752752
{ typeof(IPEndPoint), new[] { "IPEndpoint" } },
753+
{ typeof(NoRunspaceAffinityAttribute), new[] { "NoRunspaceAffinity" } },
753754
{ typeof(NullString), new[] { "NullString" } },
754755
{ typeof(OutputTypeAttribute), new[] { "OutputType" } },
755756
{ typeof(object[]), null },

src/System.Management.Automation/engine/runtime/Operations/ClassOps.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace System.Management.Automation.Internal
2222
/// <summary>
2323
/// Every Runspace in one process contains SessionStateInternal per module (module SessionState).
2424
/// Every RuntimeType is associated to only one SessionState in the Runspace, which creates it:
25-
/// it's ever global state or a module state.
25+
/// it's either global state or a module state.
2626
/// In the former case, module can be imported from the different runspaces in the same process.
2727
/// And so runspaces will share RuntimeType. But in every runspace, Type is associated with just one SessionState.
2828
/// We want type methods to be able access $script: variables and module-specific methods.

src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,8 +2300,13 @@ internal static void InitPowerShellTypesAtRuntime(TypeDefinitionAst[] types)
23002300
Diagnostics.Assert(t.Type != null, "TypeDefinitionAst.Type cannot be null");
23012301
if (t.IsClass)
23022302
{
2303-
var helperType =
2304-
t.Type.Assembly.GetType(t.Type.FullName + "_<staticHelpers>");
2303+
if (t.Type.IsDefined(typeof(NoRunspaceAffinityAttribute), inherit: true))
2304+
{
2305+
// Skip the initialization for session state affinity.
2306+
continue;
2307+
}
2308+
2309+
var helperType = t.Type.Assembly.GetType(t.Type.FullName + "_<staticHelpers>");
23052310
Diagnostics.Assert(helperType != null, "no corresponding " + t.Type.FullName + "_<staticHelpers> type found");
23062311
foreach (var p in helperType.GetFields(BindingFlags.Static | BindingFlags.NonPublic))
23072312
{
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
Describe "Class can be defined without Runspace affinity" -Tags "CI" {
5+
6+
It "Applying the 'NoRunspaceAffinity' attribute make the class not affiliate with a particular Runspace/SessionState" {
7+
[NoRunspaceAffinity()]
8+
class NoAffinity {
9+
[string] $Name;
10+
[int] $RunspaceId;
11+
12+
NoAffinity() {
13+
$this.RunspaceId = [runspace]::DefaultRunspace.Id
14+
}
15+
16+
static [int] Echo() {
17+
return [runspace]::DefaultRunspace.Id
18+
}
19+
20+
[int] SetAndEcho([string] $value) {
21+
$this.Name = $value
22+
return [runspace]::DefaultRunspace.Id
23+
}
24+
}
25+
26+
$t = [NoAffinity]
27+
$o = [NoAffinity]::new()
28+
29+
## Running directly should use the current Runspace/SessionState.
30+
$t::Echo() | Should -Be $Host.Runspace.Id
31+
$o.RunspaceId | Should -Be $Host.Runspace.Id
32+
$o.SetAndEcho('Blue') | Should -Be $Host.Runspace.Id
33+
$o.Name | Should -Be 'Blue'
34+
35+
## Running in a new Runspace should use that Runspace and its current SessionState.
36+
try {
37+
$ps = [powershell]::Create()
38+
$ps.AddScript('function CallEcho($type) { $type::Echo() }').Invoke() > $null; $ps.Commands.Clear()
39+
$ps.AddScript('function CallSetAndEcho($obj) { $obj.SetAndEcho(''Hello world'') }').Invoke() > $null; $ps.Commands.Clear()
40+
$ps.AddScript('function GetName($obj) { $obj.Name }').Invoke() > $null; $ps.Commands.Clear()
41+
$ps.AddScript('function NewObj($type) { $type::new().RunspaceId }').Invoke() > $null; $ps.Commands.Clear()
42+
43+
$ps.AddCommand('CallEcho').AddArgument($t).Invoke() | Should -Be $ps.Runspace.Id; $ps.Commands.Clear()
44+
$ps.AddCommand('CallSetAndEcho').AddArgument($o).Invoke() | Should -Be $ps.Runspace.Id; $ps.Commands.Clear()
45+
$ps.AddCommand('GetName').AddArgument($o).Invoke() | Should -Be 'Hello world'; $ps.Commands.Clear()
46+
$ps.AddCommand('NewObj').AddArgument($t).Invoke() | Should -Be $ps.Runspace.Id; $ps.Commands.Clear()
47+
}
48+
finally {
49+
$ps.Dispose()
50+
}
51+
}
52+
}

test/powershell/Language/Parser/TypeAccelerator.Tests.ps1

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,15 +410,19 @@ Describe "Type accelerators" -Tags "CI" {
410410
Accelerator = 'ordered'
411411
Type = [System.Collections.Specialized.OrderedDictionary]
412412
}
413+
@{
414+
Accelerator = 'NoRunspaceAffinity'
415+
Type = [System.Management.Automation.Language.NoRunspaceAffinityAttribute]
416+
}
413417
)
414418

415419
if ( !$IsWindows )
416420
{
417-
$totalAccelerators = 101
421+
$totalAccelerators = 102
418422
}
419423
else
420424
{
421-
$totalAccelerators = 106
425+
$totalAccelerators = 107
422426

423427
$extraFullPSAcceleratorTestCases = @(
424428
@{

0 commit comments

Comments
 (0)