diff --git a/.editorconfig b/.editorconfig index e7ef5f29..8705144d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -161,6 +161,7 @@ dotnet_diagnostic.CA1835.severity = none dotnet_diagnostic.IDE0290.severity = none # Use primary constructor dotnet_diagnostic.IDE0065.severity = none # Using directives must be placed outside of namespace +dotnet_diagnostic.IDE0350.severity = none # Lambda expression can be simplified # Collection initialization can be simplified. # This relies on implicit conversions in ways that are sometimes undesirable. diff --git a/src/NodeApi.DotNetHost/ManagedHost.cs b/src/NodeApi.DotNetHost/ManagedHost.cs index 65860e50..402befe6 100644 --- a/src/NodeApi.DotNetHost/ManagedHost.cs +++ b/src/NodeApi.DotNetHost/ManagedHost.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -49,13 +50,13 @@ public sealed class ManagedHost : JSEventEmitter, IDisposable /// /// Mapping from assembly file paths to loaded assemblies. /// - private readonly Dictionary _loadedAssembliesByPath = new(); + private readonly ConcurrentDictionary _loadedAssembliesByPath = new(); /// /// Mapping from assembly names (not including version or other parts) to /// loaded assemblies. /// - private readonly Dictionary _loadedAssembliesByName = new(); + private readonly ConcurrentDictionary _loadedAssembliesByName = new(); /// /// Tracks names of assemblies that have been exported to JS. @@ -467,7 +468,7 @@ public JSValue LoadAssembly(JSCallbackArgs args) assembly = LoadAssembly(assemblyNameOrFilePath, allowNativeLibrary: true); } - if (!_exportedAssembliesByName.Contains(assembly.GetName().Name!)) + if (assembly != null && !_exportedAssembliesByName.Contains(assembly.GetName().Name!)) { _typeExporter.ExportAssemblyTypes(assembly); _exportedAssembliesByName.Add(assembly.GetName().Name!); @@ -487,7 +488,7 @@ private JSValue ResolveAssembly(JSCallbackArgs args) return default; } - private Assembly LoadAssembly(string assemblyNameOrFilePath, bool allowNativeLibrary) + private Assembly? LoadAssembly(string assemblyNameOrFilePath, bool allowNativeLibrary) { Trace($"> ManagedHost.LoadAssembly({assemblyNameOrFilePath})"); @@ -525,41 +526,44 @@ private Assembly LoadAssembly(string assemblyNameOrFilePath, bool allowNativeLib "or the name of a system assembly (without path or DLL extension)."); } - Assembly assembly; - try + Assembly? assembly = _loadedAssembliesByPath.GetOrAdd(assemblyFilePath, _ => { + try + { #if NETFRAMEWORK || NETSTANDARD - // TODO: Load assemblies in a separate appdomain. - assembly = Assembly.LoadFrom(assemblyFilePath); + // TODO: Load assemblies in a separate appdomain. + return Assembly.LoadFrom(assemblyFilePath); #else - assembly = _loadContext.LoadFromAssemblyPath(assemblyFilePath); + return _loadContext.LoadFromAssemblyPath(assemblyFilePath); #endif - } - catch (BadImageFormatException) - { - if (!allowNativeLibrary) - { - throw; } + catch (BadImageFormatException) + { + if (!allowNativeLibrary) + { + throw; + } - // This might be a native DLL, not a managed assembly. - // Load the native library, which enables it to be auto-resolved by - // any later DllImport operations for the same library name. - NativeLibrary.Load(assemblyFilePath); + // This might be a native DLL, not a managed assembly. + // Load the native library, which enables it to be auto-resolved by + // any later DllImport operations for the same library name. + NativeLibrary.Load(assemblyFilePath); + return null; + } + catch (FileNotFoundException fnfex) + { + throw new FileNotFoundException( + $"Assembly file not found: {assemblyNameOrFilePath}", fnfex); + } + }); - Trace($"< ManagedHost.LoadAssembly() => {assemblyFilePath} (native library)"); - return null!; - } - catch (FileNotFoundException fnfex) + if (assembly != null) { - throw new FileNotFoundException( - $"Assembly file not found: {assemblyNameOrFilePath}", fnfex); + _loadedAssembliesByName.GetOrAdd(assembly.GetName().Name!, assembly); } - _loadedAssembliesByPath.Add(assemblyFilePath, assembly); - _loadedAssembliesByName.Add(assembly.GetName().Name!, assembly); - - Trace($"< ManagedHost.LoadAssembly() => {assemblyFilePath}, {assembly.GetName().Version}"); + string version = assembly?.GetName().Version?.ToString() ?? "(native library)"; + Trace($"< ManagedHost.LoadAssembly() => {assemblyFilePath} {version}"); return assembly; }