Skip to content

Commit 07808a7

Browse files
Revert "Match strong-name identity when resolving PSES dependencies (#2303)"
This reverts commit b9fd1b3. #2303 is what broke `CanAttachScriptWithPathMappings` on Windows. A clean bisection shows its parent (#2304, 6ad4f46) passed Windows E2E in ~12 minutes, while #2303 itself hung for 5h51m on that exact test -- and every commit built on top of it inherited the hang. Months of green Windows runs precede #2303. The mechanism is in `PsesLoadContext.Load`. #2303 tightened `IsSatisfyingAssembly` to also require a matching public key token and culture. When a `$PSHOME` assembly previously satisfied a dependency by name+version, `Load` returned `null` and PSES *shared* PowerShell's single copy. Under the stricter check a token mismatch now fails that first test, so `Load` falls through and loads our *own* bundled copy into the isolated `PsesLoadContext` instead -- producing two copies of the same assembly in two load contexts and a split type identity. The debugger-attach handshake (`Debug-Runspace` subscribing to `RunspaceBase.AvailabilityChanged`, plus the stopped-event plumbing in SMA) relies on cross-context event wiring that silently breaks under such a split, so the attach never completes and the test waits forever. It only trips on Windows because that is where the `$PSHOME`-versus-bundled token divergence occurs. #2303's "no bundled dependency changes resolution" check was static and missed an assembly loaded dynamically during attach. #2303 was self-described as "a focused trial of tightening" the matching, so reverting it restores the long-standing, known-good behavior. We can re-attempt the hardening later with this attach test as a guard. Drafted by Copilot (Claude Opus 4.8). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 42096ab commit 07808a7

4 files changed

Lines changed: 3 additions & 210 deletions

File tree

src/PowerShellEditorServices.Hosting/Internal/PsesLoadContext.cs

Lines changed: 3 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -76,72 +76,10 @@ private static bool IsSatisfyingAssembly(AssemblyName requiredAssemblyName, stri
7676
return false;
7777
}
7878

79-
return IsSatisfyingAssembly(requiredAssemblyName, AssemblyName.GetAssemblyName(assemblyPath));
80-
}
81-
82-
// Internal (rather than private) purely so it can be unit tested with constructed
83-
// AssemblyName instances; it has no file-system dependency of its own.
84-
internal static bool IsSatisfyingAssembly(AssemblyName requiredAssemblyName, AssemblyName asmToLoadName)
85-
{
86-
// The simple name must match (case-insensitively, as assembly names are).
87-
if (!string.Equals(asmToLoadName.Name, requiredAssemblyName.Name, StringComparison.OrdinalIgnoreCase))
88-
{
89-
return false;
90-
}
91-
92-
// The candidate must be at least the requested version. We still accept newer
93-
// versions, since shared framework and $PSHOME assemblies are generally
94-
// forward-compatible via the runtime's binding.
95-
if (asmToLoadName.Version < requiredAssemblyName.Version)
96-
{
97-
return false;
98-
}
99-
100-
// The strong-name identity must match. Previously only the simple name and version
101-
// were compared, so a same-named assembly with a *different* public key token (i.e.
102-
// a genuinely different assembly) was treated as a drop-in replacement and would then
103-
// fail at runtime with a FileLoadException/TypeLoadException. Requiring the public key
104-
// token to match means we only short-circuit to a $PSHOME/Common assembly that can
105-
// actually satisfy the reference; otherwise we fall through and let the default load
106-
// context resolve it with its own (laxer) rules.
107-
if (!PublicKeyTokensMatch(requiredAssemblyName, asmToLoadName))
108-
{
109-
return false;
110-
}
111-
112-
// The culture must match so we never substitute a satellite resource assembly for the
113-
// neutral one (or vice versa).
114-
return string.Equals(
115-
asmToLoadName.CultureName ?? string.Empty,
116-
requiredAssemblyName.CultureName ?? string.Empty,
117-
StringComparison.OrdinalIgnoreCase);
118-
}
119-
120-
private static bool PublicKeyTokensMatch(AssemblyName requiredAssemblyName, AssemblyName candidateAssemblyName)
121-
{
122-
byte[] requiredToken = requiredAssemblyName.GetPublicKeyToken();
123-
124-
// A reference to a non-strong-named assembly imposes no public key token requirement.
125-
if (requiredToken is null || requiredToken.Length == 0)
126-
{
127-
return true;
128-
}
129-
130-
byte[] candidateToken = candidateAssemblyName.GetPublicKeyToken();
131-
if (candidateToken is null || candidateToken.Length != requiredToken.Length)
132-
{
133-
return false;
134-
}
135-
136-
for (int i = 0; i < requiredToken.Length; i++)
137-
{
138-
if (requiredToken[i] != candidateToken[i])
139-
{
140-
return false;
141-
}
142-
}
79+
AssemblyName asmToLoadName = AssemblyName.GetAssemblyName(assemblyPath);
14380

144-
return true;
81+
return string.Equals(asmToLoadName.Name, requiredAssemblyName.Name, StringComparison.OrdinalIgnoreCase)
82+
&& asmToLoadName.Version >= requiredAssemblyName.Version;
14583
}
14684
}
14785
}

src/PowerShellEditorServices.Hosting/PowerShellEditorServices.Hosting.csproj

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@
1111
<DefineConstants>$(DefineConstants);CoreCLR</DefineConstants>
1212
</PropertyGroup>
1313

14-
<ItemGroup>
15-
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
16-
<_Parameter1>Microsoft.PowerShell.EditorServices.Test</_Parameter1>
17-
</AssemblyAttribute>
18-
</ItemGroup>
19-
2014
<ItemGroup>
2115
<PackageReference Include="PowerShellStandard.Library" PrivateAssets="all" />
2216
<PackageReference Include="System.IO.Pipes.AccessControl" />

test/PowerShellEditorServices.Test/PowerShellEditorServices.Test.csproj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@
1616
<ProjectReference Include="..\PowerShellEditorServices.Test.Shared\PowerShellEditorServices.Test.Shared.csproj" />
1717
</ItemGroup>
1818

19-
<!-- The Hosting assembly (and thus PsesLoadContext) only exists on .NET Core. -->
20-
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
21-
<ProjectReference Include="..\..\src\PowerShellEditorServices.Hosting\PowerShellEditorServices.Hosting.csproj" />
22-
</ItemGroup>
23-
2419
<!-- PowerShell 7.4.x -->
2520
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
2621
<PackageReference Include="Microsoft.PowerShell.SDK" />

test/PowerShellEditorServices.Test/Session/PsesLoadContextTests.cs

Lines changed: 0 additions & 134 deletions
This file was deleted.

0 commit comments

Comments
 (0)