diff --git a/.editorconfig b/.editorconfig index 1800003f88..e824186f20 100644 --- a/.editorconfig +++ b/.editorconfig @@ -79,18 +79,18 @@ csharp_preserve_single_line_statements = false # @see: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules#c-style-rules csharp_prefer_braces = false:when_multiline csharp_prefer_simple_default_expression = true:suggestion -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion csharp_style_conditional_delegate_call = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_constructors = true:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_methods = true:silent -csharp_style_expression_bodied_operators = true:silent -csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion @@ -100,12 +100,12 @@ csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_throw_expression = true:suggestion -csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:warning -csharp_style_var_elsewhere = true:silent +csharp_style_var_elsewhere = true:suggestion # Default naming styles dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.severity = suggestion @@ -202,18 +202,18 @@ dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_null_propagation = true:suggestion dotnet_style_object_initializer = true:suggestion -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion dotnet_style_predefined_type_for_locals_parameters_members = true:warning -dotnet_style_predefined_type_for_member_access = true:silent +dotnet_style_predefined_type_for_member_access = true:suggestion -dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_auto_properties = true:suggestion dotnet_style_prefer_compound_assignment = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:silent -dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion +dotnet_style_prefer_conditional_expression_over_return = false:suggestion dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/CrossPlatformAssemblyResolver.cs b/src/Stryker.Core/Stryker.Core/Initialisation/CrossPlatformAssemblyResolver.cs new file mode 100644 index 0000000000..df56f15520 --- /dev/null +++ b/src/Stryker.Core/Stryker.Core/Initialisation/CrossPlatformAssemblyResolver.cs @@ -0,0 +1,409 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Mono.Cecil; + +namespace Stryker.Core.Initialisation +{ + // This (CrossPlatformAssemblyResolver) is a copy of Mono.Cecil's BaseAssemblyResolver with all the conditional compilation removed and changes made to "Resolve" + // Original: https://github.com/jbevain/cecil/blob/7b8ee049a151204997eecf587c69acc2f67c8405/Mono.Cecil/BaseAssemblyResolver.cs + // Author: + // Jb Evain (jbevain@gmail.com) + // + // Copyright (c) 2008 - 2015 Jb Evain + // Copyright (c) 2008 - 2011 Novell, Inc. + // + // Licensed under the MIT/X11 license. + // + public class CrossPlatformAssemblyResolver : IAssemblyResolver + { + private static readonly bool _onMono = Type.GetType("Mono.Runtime") != null; + private static readonly List _directories = new List(2) { ".", "bin" }; + + // Maps file names of available trusted platform assemblies to their full paths. + private static readonly Lazy> TrustedPlatformAssemblies = new Lazy>(CreateTrustedPlatformAssemblyMap); + + private List _gacPaths; + public event AssemblyResolveEventHandler ResolveFailure; + + public virtual AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new ReaderParameters()); + + public virtual AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (parameters == null) + { + throw new ArgumentNullException(nameof(parameters)); + } + + var assembly = SearchDirectory(name, _directories, parameters); + if (assembly != null) + { + return assembly; + } + + if (name.IsRetargetable) + { + // if the reference is retargetable, zero it + name = new AssemblyNameReference(name.Name, new Version(0, 0, 0, 0)) + { + PublicKeyToken = new byte[0], + }; + } + + //Try resolve as .NET core first (since stryker runs as .NET core, this is still the default) + assembly = SearchTrustedPlatformAssemblies(name, parameters); + if (assembly != null) + { + return assembly; + } + //If that fails, try as .NET framework + else + { + var framework_dir = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName); + var framework_dirs = _onMono + ? new[] { framework_dir, Path.Combine(framework_dir, "Facades") } + : new[] { framework_dir }; + + if (IsZero(name.Version)) + { + assembly = SearchDirectory(name, framework_dirs, parameters); + if (assembly != null) + { + return assembly; + } + } + + if (name.Name == "mscorlib") + { + assembly = GetCorlib(name, parameters); + if (assembly != null) + { + return assembly; + } + } + + assembly = GetAssemblyInGac(name, parameters); + if (assembly != null) + { + return assembly; + } + + assembly = SearchDirectory(name, framework_dirs, parameters); + if (assembly != null) + { + return assembly; + } + } + + if (ResolveFailure != null) + { + assembly = ResolveFailure(this, name); + if (assembly != null) + { + return assembly; + } + } + + throw new AssemblyResolutionException(name); + } + + private AssemblyDefinition GetAssembly(string file, ReaderParameters parameters) + { + if (parameters.AssemblyResolver == null) + { + parameters.AssemblyResolver = this; + } + + return ModuleDefinition.ReadModule(file, parameters).Assembly; + } + + private AssemblyDefinition SearchTrustedPlatformAssemblies(AssemblyNameReference name, ReaderParameters parameters) + { + if (name.IsWindowsRuntime) + { + return null; + } + + return TrustedPlatformAssemblies.Value.TryGetValue(name.Name, out var path) ? GetAssembly(path, parameters) : null; + } + + private static Dictionary CreateTrustedPlatformAssemblyMap() + { + var result = new Dictionary(StringComparer.OrdinalIgnoreCase); + + string paths; + + try + { + paths = (string)AppDomain.CurrentDomain.GetData("TRUSTED_PLATFORM_ASSEMBLIES"); + } + catch + { + paths = null; + } + + if (paths == null) + { + return result; + } + + foreach (var path in paths.Split(Path.PathSeparator)) + { + if (string.Equals(Path.GetExtension(path), ".dll", StringComparison.OrdinalIgnoreCase)) + { + result[Path.GetFileNameWithoutExtension(path)] = path; + } + } + + return result; + } + + protected virtual AssemblyDefinition SearchDirectory(AssemblyNameReference name, + IEnumerable directories, ReaderParameters parameters) + { + var extensions = name.IsWindowsRuntime ? new[] { ".winmd", ".dll" } : new[] { ".exe", ".dll" }; + foreach (var directory in directories) + { + foreach (var extension in extensions) + { + var file = Path.Combine(directory, name.Name + extension); + if (!File.Exists(file)) + { + continue; + } + + try + { + return GetAssembly(file, parameters); + } + catch (System.BadImageFormatException) + { + continue; + } + } + } + + return null; + } + + private static bool IsZero(Version version) => version.Major == 0 && version.Minor == 0 && version.Build == 0 && version.Revision == 0; + + private AssemblyDefinition GetCorlib(AssemblyNameReference reference, ReaderParameters parameters) + { + var version = reference.Version; + var corlib = typeof(object).Assembly.GetName(); + if (corlib.Version == version || IsZero(version)) + { + return GetAssembly(typeof(object).Module.FullyQualifiedName, parameters); + } + + var path = Directory.GetParent(Directory.GetParent(typeof(object).Module.FullyQualifiedName).FullName).FullName; + + if (_onMono) + { + if (version.Major == 1) + { + path = Path.Combine(path, "1.0"); + } + else if (version.Major == 2) + { + if (version.MajorRevision == 5) + { + path = Path.Combine(path, "2.1"); + } + else + { + path = Path.Combine(path, "2.0"); + } + } + else if (version.Major == 4) + { + path = Path.Combine(path, "4.0"); + } + else + { + throw new NotSupportedException("Version not supported: " + version); + } + } + else + { + switch (version.Major) + { + case 1: + path = version.MajorRevision == 3300 ? Path.Combine(path, "v1.0.3705") : Path.Combine(path, "v1.1.4322"); + break; + case 2: + path = Path.Combine(path, "v2.0.50727"); + break; + case 4: + path = Path.Combine(path, "v4.0.30319"); + break; + default: + throw new NotSupportedException("Version not supported: " + version); + } + } + + var file = Path.Combine(path, "mscorlib.dll"); + if (File.Exists(file)) + { + return GetAssembly(file, parameters); + } + + if (_onMono && Directory.Exists(path + "-api")) + { + file = Path.Combine(path + "-api", "mscorlib.dll"); + if (File.Exists(file)) + { + return GetAssembly(file, parameters); + } + } + + return null; + } + + private static List GetGacPaths() + { + if (_onMono) + { + return GetDefaultMonoGacPaths(); + } + + var paths = new List(2); + var windir = Environment.GetEnvironmentVariable("WINDIR"); + if (windir == null) + { + return paths; + } + + paths.Add(Path.Combine(windir, "assembly")); + paths.Add(Path.Combine(windir, Path.Combine("Microsoft.NET", "assembly"))); + return paths; + } + + private static List GetDefaultMonoGacPaths() + { + var paths = new List(1); + var gac = GetCurrentMonoGac(); + if (gac != null) + { + paths.Add(gac); + } + + var gac_paths_env = Environment.GetEnvironmentVariable("MONO_GAC_PREFIX"); + if (string.IsNullOrEmpty(gac_paths_env)) + { + return paths; + } + + var prefixes = gac_paths_env.Split(Path.PathSeparator); + foreach (var prefix in prefixes) + { + if (string.IsNullOrEmpty(prefix)) + { + continue; + } + + var gac_path = Path.Combine(Path.Combine(Path.Combine(prefix, "lib"), "mono"), "gac"); + if (Directory.Exists(gac_path) && !paths.Contains(gac)) + { + paths.Add(gac_path); + } + } + + return paths; + } + + private static string GetCurrentMonoGac() => Path.Combine(Directory.GetParent(Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName)).FullName, "gac"); + + private AssemblyDefinition GetAssemblyInGac(AssemblyNameReference reference, ReaderParameters parameters) + { + if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) + { + return null; + } + + if (_gacPaths == null) + { + _gacPaths = GetGacPaths(); + } + + if (_onMono) + { + return GetAssemblyInMonoGac(reference, parameters); + } + + return GetAssemblyInNetGac(reference, parameters); + } + + private AssemblyDefinition GetAssemblyInMonoGac(AssemblyNameReference reference, ReaderParameters parameters) + { + for (var i = 0; i < _gacPaths.Count; i++) + { + var gac_path = _gacPaths[i]; + var file = GetAssemblyFile(reference, string.Empty, gac_path); + if (File.Exists(file)) + { + return GetAssembly(file, parameters); + } + } + + return null; + } + + private AssemblyDefinition GetAssemblyInNetGac(AssemblyNameReference reference, ReaderParameters parameters) + { + var gacs = new[] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" }; + var prefixes = new[] { string.Empty, "v4.0_" }; + + for (var i = 0; i < _gacPaths.Count; i++) + { + for (var j = 0; j < gacs.Length; j++) + { + var gac = Path.Combine(_gacPaths[i], gacs[j]); + var file = GetAssemblyFile(reference, prefixes[i], gac); + if (Directory.Exists(gac) && File.Exists(file)) + { + return GetAssembly(file, parameters); + } + } + } + + return null; + } + + private static string GetAssemblyFile(AssemblyNameReference reference, string prefix, string gac) + { + var gacFolder = new StringBuilder() + .Append(prefix) + .Append(reference.Version) + .Append("__"); + + for (var i = 0; i < reference.PublicKeyToken.Length; i++) + { + _ = gacFolder.Append(reference.PublicKeyToken[i].ToString("x2")); + } + + return Path.Combine( + Path.Combine( + Path.Combine(gac, reference.Name), gacFolder.ToString()), + reference.Name + ".dll"); + } + + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } + } +} diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/EmbeddedResourcesGenerator.cs b/src/Stryker.Core/Stryker.Core/Initialisation/EmbeddedResourcesGenerator.cs index 3a327d546b..fbbb812183 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/EmbeddedResourcesGenerator.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/EmbeddedResourcesGenerator.cs @@ -10,20 +10,18 @@ namespace Stryker.Core.Initialisation { public static class EmbeddedResourcesGenerator { - private static IDictionary> _resourceDescriptions = new Dictionary>(); + private static readonly IDictionary> _resourceDescriptions = new Dictionary>(); public static IEnumerable GetManifestResources(string assemblyPath, ILogger logger) { if (!_resourceDescriptions.ContainsKey(assemblyPath)) { - using (var module = LoadModule(assemblyPath, logger)) + using var module = LoadModule(assemblyPath, logger); + if (module is null) { - if (module is null) - { - yield break; - } - _resourceDescriptions.Add(assemblyPath, ReadResourceDescriptionsFromModule(module).ToList()); + yield break; } + _resourceDescriptions.Add(assemblyPath, ReadResourceDescriptionsFromModule(module).ToList()); } foreach (var description in _resourceDescriptions[assemblyPath]) @@ -41,7 +39,8 @@ private static ModuleDefinition LoadModule(string assemblyPath, ILogger logger) new ReaderParameters(ReadingMode.Immediate) { InMemory = true, - ReadWrite = false + ReadWrite = false, + AssemblyResolver = new CrossPlatformAssemblyResolver() }); } catch (Exception e) @@ -61,7 +60,7 @@ private static IEnumerable ReadResourceDescriptionsFromModu var stream = moduleResource.GetResourceStream(); var bytes = new byte[stream.Length]; - stream.Read(bytes, 0, bytes.Length); + _ = stream.Read(bytes, 0, bytes.Length); var ms = new MemoryStream(); ms.Write(bytes, 0, bytes.Length);