Skip to content

Commit 1545d7b

Browse files
authored
Avoid null refs in HotReloadAgent when accessing Assembly.Modules (#33263)
Accessing Assembly.Modules for arbitrary loaded assemblies may throw which currently results in the hot reload agent to produce an unhandled exception crashing the app. This change attempts to perform a targeted fix for that issue. Fixes #33152
1 parent c5fc9fa commit 1545d7b

File tree

1 file changed

+44
-6
lines changed

1 file changed

+44
-6
lines changed

src/Components/WebAssembly/WebAssembly/src/HotReload/HotReloadAgent.cs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4-
// Based on the implementation in https://github.com/raw/dotnet/sdk/0304792ef1f53d64182bcfed2ea490e40983a5c5/src/BuiltInTools/DotNetDeltaApplier/HotReloadAgent.cs
4+
// Based on the implementation in https://github.com/raw/dotnet/sdk/4eaeb44f850903af2e0da8d74b7796f9df11c29d/src/BuiltInTools/DotNetDeltaApplier/HotReloadAgent.cs
55

66
using System;
77
using System.Collections.Concurrent;
@@ -30,7 +30,7 @@ private void OnAssemblyLoad(object? _, AssemblyLoadEventArgs eventArgs)
3030
{
3131
_handlerActions = null;
3232
var loadedAssembly = eventArgs.LoadedAssembly;
33-
var moduleId = loadedAssembly.Modules.FirstOrDefault()?.ModuleVersionId;
33+
var moduleId = TryGetModuleId(loadedAssembly);
3434
if (moduleId is null)
3535
{
3636
return;
@@ -39,7 +39,7 @@ private void OnAssemblyLoad(object? _, AssemblyLoadEventArgs eventArgs)
3939
if (_deltas.TryGetValue(moduleId.Value, out var updateDeltas) && _appliedAssemblies.TryAdd(loadedAssembly, loadedAssembly))
4040
{
4141
// A delta for this specific Module exists and we haven't called ApplyUpdate on this instance of Assembly as yet.
42-
ApplyDeltas(updateDeltas);
42+
ApplyDeltas(loadedAssembly, updateDeltas);
4343
}
4444
}
4545

@@ -192,10 +192,12 @@ public void ApplyDeltas(IReadOnlyList<UpdateDelta> deltas)
192192
for (var i = 0; i < deltas.Count; i++)
193193
{
194194
var item = deltas[i];
195-
var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.Modules.FirstOrDefault() is Module m && m.ModuleVersionId == item.ModuleId);
196-
if (assembly is not null)
195+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
197196
{
198-
System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assembly, item.MetadataDelta, item.ILDelta, ReadOnlySpan<byte>.Empty);
197+
if (TryGetModuleId(assembly) is Guid moduleId && moduleId == item.ModuleId)
198+
{
199+
System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assembly, item.MetadataDelta, item.ILDelta, ReadOnlySpan<byte>.Empty);
200+
}
199201
}
200202

201203
// Additionally stash the deltas away so it may be applied to assemblies loaded later.
@@ -214,9 +216,45 @@ public void ApplyDeltas(IReadOnlyList<UpdateDelta> deltas)
214216
}
215217
}
216218

219+
public void ApplyDeltas(Assembly assembly, IReadOnlyList<UpdateDelta> deltas)
220+
{
221+
try
222+
{
223+
// Defer discovering the receiving deltas until the first hot reload delta.
224+
// This should give enough opportunity for AppDomain.GetAssemblies() to be sufficiently populated.
225+
_handlerActions ??= GetMetadataUpdateHandlerActions();
226+
var handlerActions = _handlerActions;
227+
228+
foreach (var item in deltas)
229+
{
230+
System.Reflection.Metadata.AssemblyExtensions.ApplyUpdate(assembly, item.MetadataDelta, item.ILDelta, ReadOnlySpan<byte>.Empty);
231+
}
232+
233+
_log("Deltas applied.");
234+
}
235+
catch (Exception ex)
236+
{
237+
_log(ex.ToString());
238+
}
239+
}
240+
217241
public void Dispose()
218242
{
219243
AppDomain.CurrentDomain.AssemblyLoad -= _assemblyLoad;
220244
}
245+
246+
private static Guid? TryGetModuleId(Assembly loadedAssembly)
247+
{
248+
try
249+
{
250+
return loadedAssembly.Modules.FirstOrDefault()?.ModuleVersionId;
251+
}
252+
catch
253+
{
254+
// Assembly.Modules might throw. See https://github.com/dotnet/aspnetcore/issues/33152
255+
}
256+
257+
return default;
258+
}
221259
}
222260
}

0 commit comments

Comments
 (0)