Skip to content

Commit c266c8e

Browse files
PaulHigindaxian-dbw
authored andcommitted
Fix for duplicate types in TypeTable (PowerShell#3141)
When a TypeTable is created it includes the types from type files provided along with references to the type files. When the InitialSessionState (ISS) object processes these types it reads the type files again and ends up with duplicate type entries. PowerShell V5.1 ISS type processing was re-written to improve performance and no longer removes duplicate types, so that this scenario (runspace ISS reuse) results in errors causing a regression. The fix is to copy only type data when a TypeTable is passed to the ISS.
1 parent d60dc73 commit c266c8e

3 files changed

Lines changed: 113 additions & 3 deletions

File tree

src/System.Management.Automation/engine/InitialSessionState.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,7 @@ public void Add(T item)
13091309
}
13101310

13111311
/// <summary>
1312-
///
1312+
/// Add items to this collection.
13131313
/// </summary>
13141314
/// <param name="items"></param>
13151315
public void Add(IEnumerable<T> items)
@@ -1326,6 +1326,27 @@ public void Add(IEnumerable<T> items)
13261326
}
13271327
}
13281328

1329+
/// <summary>
1330+
/// Special add for TypeTable type entries that removes redundant file entries.
1331+
/// </summary>
1332+
internal void AddTypeTableTypesInfo(IEnumerable<T> items)
1333+
{
1334+
if (typeof(T) != typeof(SessionStateTypeEntry)) { throw new PSInvalidOperationException(); }
1335+
1336+
lock (_syncObject)
1337+
{
1338+
foreach (var element in items)
1339+
{
1340+
var typeEntry = element as SessionStateTypeEntry;
1341+
if (typeEntry.TypeData != null)
1342+
{
1343+
// Skip type file entries.
1344+
_internalCollection.Add(element);
1345+
}
1346+
}
1347+
}
1348+
}
1349+
13291350
/// <summary>
13301351
/// Get enumerator for this collection.
13311352
/// </summary>
@@ -3851,7 +3872,12 @@ internal void UpdateTypes(ExecutionContext context, bool updateOnly)
38513872
context.TypeTable = typeTable;
38523873

38533874
Types.Clear();
3854-
Types.Add(typeTable.typesInfo);
3875+
3876+
// A TypeTable contains types info along with type file references used to create the types info,
3877+
// which is redundant information. When resused in a runspace the ISS unpacks the file types again
3878+
// resulting in duplicate types and duplication errors when processed.
3879+
// So use this special Add method to filter all types files found in the TypeTable.
3880+
Types.AddTypeTableTypesInfo(typeTable.typesInfo);
38553881

38563882
return;
38573883
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
3+
<Types>
4+
<Type>
5+
<Name>System.Array</Name>
6+
<Members>
7+
<AliasProperty>
8+
<Name>Counts</Name>
9+
<ReferencedMemberName>Length</ReferencedMemberName>
10+
</AliasProperty>
11+
</Members>
12+
</Type>
13+
</Types>

test/powershell/engine/InitialSessionState.Tests.ps1

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,75 @@ Describe "InitialSessionState capacity" -Tags CI {
5454
$ps.AddScript('New-Alias -Name a5000 -Value f1; a5000').Invoke() | Should Be "fn f1"
5555
$ps.Streams.Error | Should Be $null
5656
}
57-
}
57+
}
58+
59+
##
60+
## A reused InitialSessionState created from a TypeTable should not have duplicate types.
61+
##
62+
Describe "TypeTable duplicate types in reused runspace InitialSessionState TypeTable" -Tags 'Feature' {
63+
64+
Context "No duplicate types test" {
65+
66+
BeforeAll {
67+
68+
$typeTable = [System.Management.Automation.Runspaces.TypeTable]::new([string[]](Join-Path $PSScriptRoot "../Common/TestTypeFile.ps1xml"))
69+
[initialsessionstate] $iss = [initialsessionstate]::Create()
70+
$iss.Types.Add($typeTable)
71+
[runspace] $rs1 = [runspacefactory]::CreateRunspace($iss)
72+
73+
# Process TypeTable types from ISS
74+
$rs1.Open()
75+
76+
# Get processed ISS from runspace.
77+
$issReused = $rs1.InitialSessionState.Clone()
78+
$issReused.ThrowOnRunspaceOpenError = $true
79+
80+
# Create new runspace with reused ISS.
81+
$rs2 = [runspacefactory]::CreateRunspace($issReused)
82+
}
83+
84+
AfterAll {
85+
86+
if ($rs1 -ne $null) { $rs1.Dispose() }
87+
if ($rs2 -ne $null) { $rs2.Dispose() }
88+
}
89+
90+
It "Verifies that a reused InitialSessionState object created from a TypeTable object does not have duplicate types" {
91+
92+
{ $rs2.Open() } | Should Not Throw
93+
}
94+
}
95+
96+
Context "Cannot use shared TypeTable in ISS test" {
97+
98+
BeforeAll {
99+
100+
# Create default ISS and add shared TypeTable.
101+
$typeTable = [System.Management.Automation.Runspaces.TypeTable]::new([string[]](Join-Path $PSScriptRoot "../Common/TestTypeFile.ps1xml"))
102+
[initialsessionstate] $iss = [initialsessionstate]::CreateDefault2()
103+
$iss.Types.Add($typeTable)
104+
$iss.ThrowOnRunspaceOpenError = $true
105+
[runspace] $rs = [runspacefactory]::CreateRunspace($iss)
106+
}
107+
108+
AfterAll {
109+
110+
if ($rs -ne $null) { $rs.Dispose() }
111+
}
112+
113+
It "Verifies that shared TypeTable is not allowed in ISS" {
114+
115+
# Process TypeTable types from ISS.
116+
$errorId = ""
117+
try
118+
{
119+
$rs.Open()
120+
throw "No Exception!"
121+
}
122+
catch
123+
{
124+
$_.Exception.InnerException.ErrorRecord.FullyQualifiedErrorId | Should Be "ErrorsUpdatingTypes"
125+
}
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)