Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Couvreur <jcouv@users.noreply.github.com>2018-01-10 23:51:06 +0300
committerJan Kotas <jkotas@microsoft.com>2018-01-10 23:51:06 +0300
commitd84fb9958aff389e88ce4849fcd408ff3a62f68c (patch)
tree941eadb5670bcee586f50f2c87e03ddd4249d281 /src/ILVerify
parente00a6d9137a990e00f6e7b2bb1fec803524b9e54 (diff)
Add public API for ILVerify (#5186)
Diffstat (limited to 'src/ILVerify')
-rw-r--r--src/ILVerify/src/ILImporter.Verify.cs10
-rw-r--r--src/ILVerify/src/ILVerify.csproj7
-rw-r--r--src/ILVerify/src/ILVerifyTypeSystemContext.cs88
-rw-r--r--src/ILVerify/src/IResolver.cs47
-rw-r--r--src/ILVerify/src/Program.cs283
-rw-r--r--src/ILVerify/src/SimpleTypeSystemContext.cs134
-rw-r--r--src/ILVerify/src/VerificationResult.cs24
-rw-r--r--src/ILVerify/src/Verifier.cs234
-rw-r--r--src/ILVerify/src/VerifierError.cs9
-rw-r--r--src/ILVerify/tests/ILMethodTester.cs44
-rw-r--r--src/ILVerify/tests/TestDataLoader.cs54
11 files changed, 601 insertions, 333 deletions
diff --git a/src/ILVerify/src/ILImporter.Verify.cs b/src/ILVerify/src/ILImporter.Verify.cs
index 5dde3d6f3..cb168b88a 100644
--- a/src/ILVerify/src/ILImporter.Verify.cs
+++ b/src/ILVerify/src/ILImporter.Verify.cs
@@ -26,13 +26,11 @@ namespace Internal.IL
}
}
- struct VerificationErrorArgs
+ class VerifierException : Exception
{
- public VerifierError Code;
- public int Offset;
- public int Token;
- public string Found;
- public string Expected;
+ internal VerifierException(string message) : base(message)
+ {
+ }
}
partial class ILImporter
diff --git a/src/ILVerify/src/ILVerify.csproj b/src/ILVerify/src/ILVerify.csproj
index 27ce4fc9e..aad895452 100644
--- a/src/ILVerify/src/ILVerify.csproj
+++ b/src/ILVerify/src/ILVerify.csproj
@@ -15,11 +15,14 @@
<Compile Include="ILImporter.Verify.cs" />
<Compile Include="ILImporter.StackValue.cs" />
<Compile Include="SimpleArrayOfTRuntimeInterfacesAlgorithm.cs" />
- <Compile Include="SimpleTypeSystemContext.cs" />
+ <Compile Include="ILVerifyTypeSystemContext.cs" />
+ <Compile Include="Verifier.cs" />
<Compile Include="VerifierError.cs" />
<Compile Include="TypeSystemHelpers.cs" />
<Compile Include="InstantiatedGenericParameter.cs" />
<Compile Include="AccessVerificationHelpers.cs" />
+ <Compile Include="VerificationResult.cs" />
+ <Compile Include="IResolver.cs" />
</ItemGroup>
<ItemGroup>
@@ -299,4 +302,4 @@
<PackageReference Include="System.Reflection.Metadata" Version="1.4.1" />
<PackageReference Include="System.CommandLine" Version="0.1.0-e160909-1" />
</ItemGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/ILVerify/src/ILVerifyTypeSystemContext.cs b/src/ILVerify/src/ILVerifyTypeSystemContext.cs
new file mode 100644
index 000000000..528dc6a90
--- /dev/null
+++ b/src/ILVerify/src/ILVerifyTypeSystemContext.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using Internal.IL;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILVerify
+{
+ class ILVerifyTypeSystemContext : MetadataTypeSystemContext
+ {
+ internal readonly IResolver _resolver;
+
+ private RuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
+ private MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm();
+
+ private readonly Dictionary<PEReader, EcmaModule> _modulesCache = new Dictionary<PEReader, EcmaModule>();
+
+ public ILVerifyTypeSystemContext(IResolver resolver)
+ {
+ _resolver = resolver;
+ }
+
+ public override ModuleDesc ResolveAssembly(AssemblyName name, bool throwIfNotFound = true)
+ {
+ PEReader peReader = _resolver.Resolve(name);
+ if (peReader == null && throwIfNotFound)
+ {
+ throw new VerifierException("Assembly or module not found: " + name.Name);
+ }
+
+ var module = GetModule(peReader);
+ VerifyModuleName(name, module);
+ return module;
+ }
+
+ private static void VerifyModuleName(AssemblyName name, EcmaModule module)
+ {
+ MetadataReader metadataReader = module.MetadataReader;
+ StringHandle nameHandle = metadataReader.IsAssembly
+ ? metadataReader.GetAssemblyDefinition().Name
+ : metadataReader.GetModuleDefinition().Name;
+
+ string actualSimpleName = metadataReader.GetString(nameHandle);
+ if (!actualSimpleName.Equals(name.Name, StringComparison.OrdinalIgnoreCase))
+ {
+ throw new VerifierException($"Actual PE name '{actualSimpleName}' does not match provided name '{name}'");
+ }
+ }
+
+ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type)
+ {
+ if (_arrayOfTRuntimeInterfacesAlgorithm == null)
+ {
+ _arrayOfTRuntimeInterfacesAlgorithm = new SimpleArrayOfTRuntimeInterfacesAlgorithm(SystemModule);
+ }
+ return _arrayOfTRuntimeInterfacesAlgorithm;
+ }
+
+ protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDefType(DefType type)
+ {
+ return _metadataRuntimeInterfacesAlgorithm;
+ }
+
+ internal EcmaModule GetModule(PEReader peReader)
+ {
+ if (peReader == null)
+ {
+ return null;
+ }
+
+ if (_modulesCache.TryGetValue(peReader, out EcmaModule existingModule))
+ {
+ return existingModule;
+ }
+
+ EcmaModule module = EcmaModule.Create(this, peReader);
+ _modulesCache.Add(peReader, module);
+ return module;
+ }
+ }
+}
diff --git a/src/ILVerify/src/IResolver.cs b/src/ILVerify/src/IResolver.cs
new file mode 100644
index 000000000..f3706cf3b
--- /dev/null
+++ b/src/ILVerify/src/IResolver.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Reflection;
+using System.Reflection.PortableExecutable;
+
+namespace ILVerify
+{
+ public interface IResolver
+ {
+ /// <summary>
+ /// This method should return the same instance when queried multiple times.
+ /// </summary>
+ PEReader Resolve(AssemblyName name);
+ }
+
+ /// <summary>
+ /// Provides caching logic for implementations of IResolver
+ /// </summary>
+ public abstract class ResolverBase : IResolver
+ {
+ private readonly Dictionary<string, PEReader> _resolverCache = new Dictionary<string, PEReader>();
+
+ public PEReader Resolve(AssemblyName name)
+ {
+ // Note: we use simple names instead of full names to resolve, because we can't get a full name from an assembly without reading it
+ string simpleName = name.Name;
+ if (_resolverCache.TryGetValue(simpleName, out PEReader peReader))
+ {
+ return peReader;
+ }
+
+ PEReader result = ResolveCore(name);
+ if (result != null)
+ {
+ _resolverCache.Add(simpleName, result);
+ return result;
+ }
+
+ return null;
+ }
+
+ protected abstract PEReader ResolveCore(AssemblyName name);
+ }
+}
diff --git a/src/ILVerify/src/Program.cs b/src/ILVerify/src/Program.cs
index aec3b5c18..1c9b0961b 100644
--- a/src/ILVerify/src/Program.cs
+++ b/src/ILVerify/src/Program.cs
@@ -3,42 +3,31 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.IO;
using System.Collections.Generic;
using System.CommandLine;
-using System.Reflection;
+using System.IO;
using System.Linq;
+using System.Reflection;
using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
-using System.Text;
-
-using Internal.TypeSystem;
-using Internal.TypeSystem.Ecma;
-using Internal.IL;
-
-using Internal.CommandLine;
using System.Text.RegularExpressions;
-using System.Globalization;
-using System.Resources;
+using Internal.CommandLine;
+using Internal.TypeSystem.Ecma;
+using static System.Console;
namespace ILVerify
{
- class Program
+ class Program : ResolverBase
{
- private const string DefaultSystemModuleName = "mscorlib";
private bool _help;
- private string _systemModule = DefaultSystemModuleName;
- private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ private AssemblyName _systemModule = new AssemblyName("mscorlib");
+ private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // map of simple name to file path
+ private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); // map of simple name to file path
private IReadOnlyList<Regex> _includePatterns = Array.Empty<Regex>();
private IReadOnlyList<Regex> _excludePatterns = Array.Empty<Regex>();
- private SimpleTypeSystemContext _typeSystemContext;
- private ResourceManager _stringResourceManager;
-
- private int _numErrors;
+ private Verifier _verifier;
private Program()
{
@@ -46,9 +35,9 @@ namespace ILVerify
private void Help(string helpText)
{
- Console.WriteLine("ILVerify version " + typeof(Program).GetTypeInfo().Assembly.GetName().Version.ToString());
- Console.WriteLine();
- Console.WriteLine(helpText);
+ WriteLine("ILVerify version " + typeof(Program).GetTypeInfo().Assembly.GetName().Version.ToString());
+ WriteLine();
+ WriteLine(helpText);
}
public static IReadOnlyList<Regex> StringPatternsToRegexList(IReadOnlyList<string> patterns)
@@ -78,7 +67,7 @@ namespace ILVerify
syntax.HandleErrors = true;
syntax.DefineOption("h|help", ref _help, "Display this usage message");
- syntax.DefineOption("s|system-module", ref _systemModule, "System module name (default: mscorlib)");
+ syntax.DefineOption("s|system-module", ref _systemModule, s => new AssemblyName(s), "System module name (default: mscorlib)");
syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference metadata from the specified assembly");
syntax.DefineOptionList("i|include", ref includePatterns, "Use only methods/types/namespaces, which match the given regular expression(s)");
syntax.DefineOption("include-file", ref includeFile, "Same as --include, but the regular expression(s) are declared line by line in the specified file.");
@@ -97,7 +86,7 @@ namespace ILVerify
if (!string.IsNullOrEmpty(includeFile))
{
if (includePatterns.Count > 0)
- Console.WriteLine("[Warning] --include-file takes precedence over --include");
+ WriteLine("[Warning] --include-file takes precedence over --include");
includePatterns = File.ReadAllLines(includeFile);
}
_includePatterns = StringPatternsToRegexList(includePatterns);
@@ -105,7 +94,7 @@ namespace ILVerify
if (!string.IsNullOrEmpty(excludeFile))
{
if (excludePatterns.Count > 0)
- Console.WriteLine("[Warning] --exclude-file takes precedence over --exclude");
+ WriteLine("[Warning] --exclude-file takes precedence over --exclude");
excludePatterns = File.ReadAllLines(excludeFile);
}
_excludePatterns = StringPatternsToRegexList(excludePatterns);
@@ -113,153 +102,171 @@ namespace ILVerify
return argSyntax;
}
- private void VerifyMethod(MethodDesc method, MethodIL methodIL)
+ private int Run(string[] args)
{
- // Console.WriteLine("Verifying: " + method.ToString());
+ ArgumentSyntax syntax = ParseCommandLine(args);
+ if (_help)
+ {
+ Help(syntax.GetHelpText());
+ return 1;
+ }
- try
+ if (_inputFilePaths.Count == 0)
+ throw new CommandLineException("No input files specified");
+
+ _verifier = new Verifier(this);
+ _verifier.SetSystemModuleName(_systemModule);
+
+ foreach (var kvp in _inputFilePaths)
{
- var importer = new ILImporter(method, methodIL);
+ var results = VerifyAssembly(new AssemblyName(kvp.Key), out EcmaModule module);
+ int numErrors = 0;
- importer.ReportVerificationError = (args) =>
+ foreach (var result in results)
{
- var message = new StringBuilder();
-
- message.Append("[IL]: Error: ");
-
- message.Append("[");
- message.Append(_typeSystemContext.GetModulePath(((EcmaMethod)method).Module));
- message.Append(" : ");
- message.Append(((EcmaType)method.OwningType).Name);
- message.Append("::");
- message.Append(method.Name);
- message.Append("(");
- if (method.Signature._parameters != null && method.Signature._parameters.Length > 0)
- {
- foreach (TypeDesc parameter in method.Signature._parameters)
- {
- message.Append(parameter.ToString());
- message.Append(", ");
- }
- message.Remove(message.Length - 2, 2);
- }
- message.Append(")");
- message.Append("]");
+ numErrors++;
+ PrintResult(result, module, kvp.Value);
+ }
- message.Append("[offset 0x");
- message.Append(args.Offset.ToString("X8"));
- message.Append("]");
+ if (numErrors > 0)
+ WriteLine(numErrors + " Error(s) Verifying " + kvp.Value);
+ else
+ WriteLine("All Classes and Methods in " + kvp.Value + " Verified.");
+ }
- if (args.Found != null)
- {
- message.Append("[found ");
- message.Append(args.Found);
- message.Append("]");
- }
+ return 0;
+ }
- if (args.Expected != null)
- {
- message.Append("[expected ");
- message.Append(args.Expected);
- message.Append("]");
- }
+ private void PrintResult(VerificationResult result, EcmaModule module, string pathOrModuleName)
+ {
+ Write("[IL]: Error: ");
- if (args.Token != 0)
- {
- message.Append("[token 0x");
- message.Append(args.Token.ToString("X8"));
- message.Append("]");
- }
+ Write("[");
+ Write(pathOrModuleName);
+ Write(" : ");
- message.Append(" ");
+ MetadataReader metadataReader = module.MetadataReader;
- if (_stringResourceManager == null)
- {
- _stringResourceManager = new ResourceManager("ILVerify.Resources.Strings", Assembly.GetExecutingAssembly());
- }
+ TypeDefinition typeDef = metadataReader.GetTypeDefinition(metadataReader.GetMethodDefinition(result.Method).GetDeclaringType());
+ string typeName = metadataReader.GetString(typeDef.Name);
+ Write(typeName);
- var str = _stringResourceManager.GetString(args.Code.ToString(), CultureInfo.InvariantCulture);
- message.Append(string.IsNullOrEmpty(str) ? args.Code.ToString() : str);
+ Write("::");
+ var method = (EcmaMethod)module.GetMethod(result.Method);
+ PrintMethod(method);
+ Write("]");
- Console.WriteLine(message);
+ var args = result.Error;
+ if (args.Code != VerifierError.None)
+ {
+ Write("[offset 0x");
+ Write(args.Offset.ToString("X8"));
+ Write("]");
- _numErrors++;
- };
+ if (args.Found != null)
+ {
+ Write("[found ");
+ Write(args.Found);
+ Write("]");
+ }
- importer.Verify();
- }
- catch (NotImplementedException e)
- {
- Console.Error.WriteLine($"Error in {method}: {e.Message}");
- }
- catch (InvalidProgramException e)
- {
- Console.Error.WriteLine($"Error in {method}: {e.Message}");
- }
- catch (VerificationException)
- {
- }
- catch (BadImageFormatException)
- {
- Console.WriteLine("Unable to resolve token");
- }
- catch (PlatformNotSupportedException e)
- {
- Console.WriteLine(e.Message);
+ if (args.Expected != null)
+ {
+ Write("[expected ");
+ Write(args.Expected);
+ Write("]");
+ }
+
+ if (args.Token != 0)
+ {
+ Write("[token 0x");
+ Write(args.Token.ToString("X8"));
+ Write("]");
+ }
}
+
+ Write(" ");
+ WriteLine(result.Message);
}
- private void VerifyModule(EcmaModule module)
+ private static void PrintMethod(EcmaMethod method)
{
- foreach (var methodHandle in module.MetadataReader.MethodDefinitions)
+ Write(method.Name);
+ Write("(");
+
+ if (method.Signature._parameters != null && method.Signature._parameters.Length > 0)
{
- var method = (EcmaMethod)module.GetMethod(methodHandle);
+ bool first = true;
+ foreach (Internal.TypeSystem.TypeDesc parameter in method.Signature._parameters)
+ {
+ if (first)
+ {
+ first = false;
+ }
+ else
+ {
+ Write(", ");
+ }
- var methodIL = EcmaMethodIL.Create(method);
- if (methodIL == null)
- continue;
+ Write(parameter.ToString());
+ }
+ }
- var methodName = method.ToString();
- if (_includePatterns.Count > 0 && !_includePatterns.Any(p => p.IsMatch(methodName)))
- continue;
- if (_excludePatterns.Any(p => p.IsMatch(methodName)))
- continue;
+ Write(")");
+ }
- VerifyMethod(method, methodIL);
+ private IEnumerable<VerificationResult> VerifyAssembly(AssemblyName name, out EcmaModule module)
+ {
+ PEReader peReader = Resolve(name);
+ module = _verifier.GetModule(peReader);
+
+ return VerifyAssembly(peReader);
+ }
+
+ private IEnumerable<VerificationResult> VerifyAssembly(PEReader peReader)
+ {
+ MetadataReader metadataReader = peReader.GetMetadataReader();
+ foreach (var methodHandle in metadataReader.MethodDefinitions)
+ {
+ var methodName = metadataReader.GetString(metadataReader.GetMethodDefinition(methodHandle).Name);
+ if (ShouldVerifyMethod(methodName))
+ {
+ var results = _verifier.Verify(peReader, methodHandle);
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
}
}
- private int Run(string[] args)
+ private bool ShouldVerifyMethod(string methodName)
{
- ArgumentSyntax syntax = ParseCommandLine(args);
- if (_help)
+ if (_includePatterns.Count > 0 && !_includePatterns.Any(p => p.IsMatch(methodName)))
{
- Help(syntax.GetHelpText());
- return 1;
+ return false;
}
- if (_inputFilePaths.Count == 0)
- throw new CommandLineException("No input files specified");
+ if (_excludePatterns.Any(p => p.IsMatch(methodName)))
+ {
+ return false;
+ }
- _typeSystemContext = new SimpleTypeSystemContext();
- _typeSystemContext.InputFilePaths = _inputFilePaths;
- _typeSystemContext.ReferenceFilePaths = _referenceFilePaths;
+ return true;
+ }
- _typeSystemContext.SetSystemModule(_typeSystemContext.GetModuleForSimpleName(_systemModule));
+ protected override PEReader ResolveCore(AssemblyName name)
+ {
+ // Note: we use simple names instead of full names to resolve, because we can't get a full name from an assembly without reading it
+ string simpleName = name.Name;
- foreach (var inputPath in _inputFilePaths.Values)
+ string path = null;
+ if (_inputFilePaths.TryGetValue(simpleName, out path) || _referenceFilePaths.TryGetValue(simpleName, out path))
{
- _numErrors = 0;
-
- VerifyModule(_typeSystemContext.GetModuleFromPath(inputPath));
-
- if (_numErrors > 0)
- Console.WriteLine(_numErrors + " Error(s) Verifying " + inputPath);
- else
- Console.WriteLine("All Classes and Methods in " + inputPath + " Verified.");
+ return new PEReader(File.OpenRead(path));
}
- return 0;
+ return null;
}
private static int Main(string[] args)
diff --git a/src/ILVerify/src/SimpleTypeSystemContext.cs b/src/ILVerify/src/SimpleTypeSystemContext.cs
deleted file mode 100644
index 123e2e0d2..000000000
--- a/src/ILVerify/src/SimpleTypeSystemContext.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Reflection.Metadata;
-using System.Reflection.Metadata.Ecma335;
-using System.Reflection.PortableExecutable;
-
-using Internal.TypeSystem;
-using Internal.TypeSystem.Ecma;
-
-using Internal.CommandLine;
-
-namespace ILVerify
-{
- class SimpleTypeSystemContext : MetadataTypeSystemContext
- {
- private RuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
- private MetadataRuntimeInterfacesAlgorithm _metadataRuntimeInterfacesAlgorithm = new MetadataRuntimeInterfacesAlgorithm();
-
- Dictionary<string, EcmaModule> _modules = new Dictionary<string, EcmaModule>(StringComparer.OrdinalIgnoreCase);
-
- class ModuleData
- {
- public string Path;
- }
- Dictionary<EcmaModule, ModuleData> _moduleData = new Dictionary<EcmaModule, ModuleData>();
-
- public SimpleTypeSystemContext()
- {
- }
-
- public IDictionary<string, string> InputFilePaths
- {
- get;
- set;
- }
-
- public IDictionary<string, string> ReferenceFilePaths
- {
- get;
- set;
- }
-
- public override ModuleDesc ResolveAssembly(AssemblyName name, bool throwIfNotFound = true)
- {
- return GetModuleForSimpleName(name.Name);
- }
-
- public EcmaModule GetModuleForSimpleName(string simpleName)
- {
- EcmaModule existingModule;
- if (_modules.TryGetValue(simpleName, out existingModule))
- return existingModule;
-
- return CreateModuleForSimpleName(simpleName);
- }
-
- private EcmaModule CreateModuleForSimpleName(string simpleName)
- {
- string filePath;
- if (!InputFilePaths.TryGetValue(simpleName, out filePath))
- {
- if (!ReferenceFilePaths.TryGetValue(simpleName, out filePath))
- throw new CommandLineException("Assembly not found: " + simpleName);
- }
-
- PEReader peReader = new PEReader(File.OpenRead(filePath));
- EcmaModule module = EcmaModule.Create(this, peReader);
-
- MetadataReader metadataReader = module.MetadataReader;
- string actualSimpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
- if (!actualSimpleName.Equals(simpleName, StringComparison.OrdinalIgnoreCase))
- throw new CommandLineException("Assembly name does not match filename " + filePath);
-
- _modules.Add(simpleName, module);
-
- ModuleData moduleData = new ModuleData() { Path = filePath };
- _moduleData.Add(module, moduleData);
-
- return module;
- }
-
- public EcmaModule GetModuleFromPath(string filePath)
- {
- // This is called once for every assembly that should be verified, so linear search is acceptable.
- foreach (KeyValuePair<EcmaModule, ModuleData> entry in _moduleData)
- {
- EcmaModule curModule = entry.Key;
- ModuleData curData = entry.Value;
- if (curData.Path == filePath)
- return curModule;
- }
-
- PEReader peReader = new PEReader(File.OpenRead(filePath));
- EcmaModule module = EcmaModule.Create(this, peReader);
-
- MetadataReader metadataReader = module.MetadataReader;
- string simpleName = metadataReader.GetString(metadataReader.GetAssemblyDefinition().Name);
- if (_modules.ContainsKey(simpleName))
- throw new CommandLineException("Module with same simple name already exists " + filePath);
-
- _modules.Add(simpleName, module);
-
- ModuleData moduleData = new ModuleData() { Path = filePath };
- _moduleData.Add(module, moduleData);
-
- return module;
- }
-
- public string GetModulePath(EcmaModule module)
- {
- return _moduleData[module].Path;
- }
-
- protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForNonPointerArrayType(ArrayType type)
- {
- if (_arrayOfTRuntimeInterfacesAlgorithm == null)
- {
- _arrayOfTRuntimeInterfacesAlgorithm = new SimpleArrayOfTRuntimeInterfacesAlgorithm(SystemModule);
- }
- return _arrayOfTRuntimeInterfacesAlgorithm;
- }
-
- protected override RuntimeInterfacesAlgorithm GetRuntimeInterfacesAlgorithmForDefType(DefType type)
- {
- return _metadataRuntimeInterfacesAlgorithm;
- }
- }
-}
diff --git a/src/ILVerify/src/VerificationResult.cs b/src/ILVerify/src/VerificationResult.cs
new file mode 100644
index 000000000..e64208832
--- /dev/null
+++ b/src/ILVerify/src/VerificationResult.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection.Metadata;
+
+namespace ILVerify
+{
+ public class VerificationResult
+ {
+ public MethodDefinitionHandle Method { get; internal set; }
+ public VerificationErrorArgs Error { get; internal set; }
+ public string Message { get; internal set; }
+ }
+
+ public struct VerificationErrorArgs
+ {
+ public VerifierError Code { get; internal set; }
+ public int Offset { get; internal set; }
+ public int Token { get; internal set; }
+ public string Found { get; internal set; }
+ public string Expected { get; internal set; }
+ }
+}
diff --git a/src/ILVerify/src/Verifier.cs b/src/ILVerify/src/Verifier.cs
new file mode 100644
index 000000000..8e10ec212
--- /dev/null
+++ b/src/ILVerify/src/Verifier.cs
@@ -0,0 +1,234 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Reflection;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Resources;
+using Internal.IL;
+using Internal.TypeSystem;
+using Internal.TypeSystem.Ecma;
+
+namespace ILVerify
+{
+ public class Verifier
+ {
+ private Lazy<ResourceManager> _stringResourceManager =
+ new Lazy<ResourceManager>(() => new ResourceManager("ILVerify.Resources.Strings", Assembly.GetExecutingAssembly()));
+
+ private ILVerifyTypeSystemContext _typeSystemContext;
+
+ public Verifier(IResolver resolver)
+ {
+ _typeSystemContext = new ILVerifyTypeSystemContext(resolver);
+ }
+
+ internal Verifier(ILVerifyTypeSystemContext context)
+ {
+ _typeSystemContext = context;
+ }
+
+ public void SetSystemModuleName(AssemblyName name)
+ {
+ _typeSystemContext.SetSystemModule(_typeSystemContext.GetModule(_typeSystemContext._resolver.Resolve(name)));
+ }
+
+ internal EcmaModule GetModule(PEReader peReader)
+ {
+ return _typeSystemContext.GetModule(peReader);
+ }
+
+ public IEnumerable<VerificationResult> Verify(PEReader peReader)
+ {
+ if (peReader == null)
+ {
+ throw new ArgumentNullException(nameof(peReader));
+ }
+
+ if (_typeSystemContext.SystemModule == null)
+ {
+ ThrowMissingSystemModule();
+ }
+
+ IEnumerable<VerificationResult> results;
+ try
+ {
+ EcmaModule module = GetModule(peReader);
+ results = VerifyMethods(module, module.MetadataReader.MethodDefinitions);
+ }
+ catch (VerifierException e)
+ {
+ results = new[] { new VerificationResult() { Message = e.Message } };
+ }
+
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+
+ public IEnumerable<VerificationResult> Verify(PEReader peReader, TypeDefinitionHandle typeHandle)
+ {
+ if (peReader == null)
+ {
+ throw new ArgumentNullException(nameof(peReader));
+ }
+
+ if (typeHandle.IsNil)
+ {
+ throw new ArgumentNullException(nameof(typeHandle));
+ }
+
+ if (_typeSystemContext.SystemModule == null)
+ {
+ ThrowMissingSystemModule();
+ }
+
+ IEnumerable<VerificationResult> results;
+ try
+ {
+ EcmaModule module = GetModule(peReader);
+ TypeDefinition typeDef = peReader.GetMetadataReader().GetTypeDefinition(typeHandle);
+ results = VerifyMethods(module, typeDef.GetMethods());
+ }
+ catch (VerifierException e)
+ {
+ results = new[] { new VerificationResult() { Message = e.Message } };
+ }
+
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+
+ public IEnumerable<VerificationResult> Verify(PEReader peReader, MethodDefinitionHandle methodHandle)
+ {
+ if (peReader == null)
+ {
+ throw new ArgumentNullException(nameof(peReader));
+ }
+
+ if (methodHandle.IsNil)
+ {
+ throw new ArgumentNullException(nameof(methodHandle));
+ }
+
+ if (_typeSystemContext.SystemModule == null)
+ {
+ ThrowMissingSystemModule();
+ }
+
+ IEnumerable<VerificationResult> results;
+ try
+ {
+ EcmaModule module = GetModule(peReader);
+ results = VerifyMethods(module, new[] { methodHandle });
+ }
+ catch (VerifierException e)
+ {
+ results = new[] { new VerificationResult() { Message = e.Message } };
+ }
+
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+
+ private IEnumerable<VerificationResult> VerifyMethods(EcmaModule module, IEnumerable<MethodDefinitionHandle> methodHandles)
+ {
+ foreach (var methodHandle in methodHandles)
+ {
+ var method = (EcmaMethod)module.GetMethod(methodHandle);
+ var methodIL = EcmaMethodIL.Create(method);
+
+ if (methodIL != null)
+ {
+ var results = VerifyMethod(module, methodIL, methodHandle);
+ foreach (var result in results)
+ {
+ yield return result;
+ }
+ }
+ }
+ }
+
+ private IEnumerable<VerificationResult> VerifyMethod(EcmaModule module, MethodIL methodIL, MethodDefinitionHandle methodHandle)
+ {
+ var builder = new ArrayBuilder<VerificationResult>();
+ MethodDesc method = methodIL.OwningMethod;
+
+ try
+ {
+ var importer = new ILImporter(method, methodIL);
+
+ importer.ReportVerificationError = (args) =>
+ {
+ var codeResource = _stringResourceManager.Value.GetString(args.Code.ToString(), CultureInfo.InvariantCulture);
+
+ builder.Add(new VerificationResult()
+ {
+ Method = methodHandle,
+ Error = args,
+ Message = string.IsNullOrEmpty(codeResource) ? args.Code.ToString() : codeResource
+ });
+ };
+
+ importer.Verify();
+ }
+ catch (VerificationException)
+ {
+ // a result was reported already (before aborting)
+ }
+ catch (BadImageFormatException)
+ {
+ builder.Add(new VerificationResult()
+ {
+ Method = methodHandle,
+ Message = "Unable to resolve token"
+ });
+ }
+ catch (NotImplementedException e)
+ {
+ reportException(e);
+ }
+ catch (InvalidProgramException e)
+ {
+ reportException(e);
+ }
+ catch (PlatformNotSupportedException e)
+ {
+ reportException(e);
+ }
+ catch (VerifierException e)
+ {
+ reportException(e);
+ }
+ catch (TypeSystemException e)
+ {
+ reportException(e);
+ }
+
+ return builder.ToArray();
+
+ void reportException(Exception e)
+ {
+ builder.Add(new VerificationResult()
+ {
+ Method = methodHandle,
+ Message = e.Message
+ });
+ }
+ }
+
+ private void ThrowMissingSystemModule()
+ {
+ throw new VerifierException("No system module specified");
+ }
+ }
+}
diff --git a/src/ILVerify/src/VerifierError.cs b/src/ILVerify/src/VerifierError.cs
index 8b2db4831..508fe5154 100644
--- a/src/ILVerify/src/VerifierError.cs
+++ b/src/ILVerify/src/VerifierError.cs
@@ -2,16 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
namespace ILVerify
{
- enum VerifierError
+ public enum VerifierError
{
+ None = 0,
//E_HRESULT "[HRESULT 0x%08X]"
//E_OFFSET "[offset 0x%08X]"
//E_OPCODE "[opcode %s]"
diff --git a/src/ILVerify/tests/ILMethodTester.cs b/src/ILVerify/tests/ILMethodTester.cs
index 671bd3f91..c3f35f19c 100644
--- a/src/ILVerify/tests/ILMethodTester.cs
+++ b/src/ILVerify/tests/ILMethodTester.cs
@@ -2,11 +2,10 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
-using Internal.IL;
using Internal.TypeSystem.Ecma;
using Xunit;
@@ -19,16 +18,8 @@ namespace ILVerify.Tests
[Trait("", "Valid IL Tests")]
void TestMethodsWithValidIL(ValidILTestCase validIL)
{
- ILImporter importer = ConstructILImporter(validIL);
-
- var verifierErrors = new List<VerifierError>();
- importer.ReportVerificationError = new Action<VerificationErrorArgs>((err) =>
- {
- verifierErrors.Add(err.Code);
- });
-
- importer.Verify();
- Assert.Equal(0, verifierErrors.Count);
+ var results = Verify(validIL);
+ Assert.Equal(0, results.Count());
}
[Theory(DisplayName = "")]
@@ -36,17 +27,11 @@ namespace ILVerify.Tests
[Trait("", "Invalid IL Tests")]
void TestMethodsWithInvalidIL(InvalidILTestCase invalidIL)
{
- ILImporter importer = ConstructILImporter(invalidIL);
-
- var verifierErrors = new List<VerifierError>();
- importer.ReportVerificationError = new Action<VerificationErrorArgs>((err) =>
- {
- verifierErrors.Add(err.Code);
- });
+ IEnumerable<VerificationResult> results = null;
try
{
- importer.Verify();
+ results = Verify(invalidIL);
}
catch
{
@@ -57,23 +42,24 @@ namespace ILVerify.Tests
}
finally
{
- Assert.Equal(invalidIL.ExpectedVerifierErrors.Count, verifierErrors.Count);
+ Assert.NotNull(results);
+ Assert.Equal(invalidIL.ExpectedVerifierErrors.Count, results.Count());
foreach (var item in invalidIL.ExpectedVerifierErrors)
{
- var actual = verifierErrors.Select(e => e.ToString());
- Assert.True(verifierErrors.Contains(item), $"Actual errors where: {string.Join(',', actual)}");
+ var actual = results.Select(e => e.ToString());
+ Assert.True(results.Where(r => r.Error.Code == item).Count() > 0, $"Actual errors where: {string.Join(',', actual)}");
}
}
}
- private ILImporter ConstructILImporter(TestCase testCase)
+ private static IEnumerable<VerificationResult> Verify(TestCase testCase)
{
- var module = TestDataLoader.GetModuleForTestAssembly(testCase.ModuleName);
- var method = (EcmaMethod)module.GetMethod(MetadataTokens.EntityHandle(testCase.MetadataToken));
- var methodIL = EcmaMethodIL.Create(method);
-
- return new ILImporter(method, methodIL);
+ EcmaModule module = TestDataLoader.GetModuleForTestAssembly(testCase.ModuleName);
+ var methodHandle = (MethodDefinitionHandle) MetadataTokens.EntityHandle(testCase.MetadataToken);
+ var method = (EcmaMethod)module.GetMethod(methodHandle);
+ var verifier = new Verifier((ILVerifyTypeSystemContext)method.Context);
+ return verifier.Verify(module.PEReader, methodHandle);
}
}
}
diff --git a/src/ILVerify/tests/TestDataLoader.cs b/src/ILVerify/tests/TestDataLoader.cs
index 15dc8efde..ed22d1b1d 100644
--- a/src/ILVerify/tests/TestDataLoader.cs
+++ b/src/ILVerify/tests/TestDataLoader.cs
@@ -8,9 +8,8 @@ using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
+using System.Reflection.PortableExecutable;
using System.Text;
-using Internal.IL;
-using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Newtonsoft.Json;
using Xunit;
@@ -96,7 +95,7 @@ namespace ILVerify.Tests
private static TheoryData<TestCase> GetTestMethodsFromDll(Func<string[], MethodDefinitionHandle, TestCase> methodSelector)
{
- var retVal = new Xunit.TheoryData<TestCase>();
+ var retVal = new TheoryData<TestCase>();
foreach (var testDllName in GetAllTestDlls())
{
@@ -152,33 +151,54 @@ namespace ILVerify.Tests
private static IEnumerable<string> GetAllTestDlls()
{
- foreach (var item in System.IO.Directory.GetFiles(TESTASSEMBLYPATH))
+ foreach (var item in Directory.GetFiles(TESTASSEMBLYPATH))
{
if (item.ToLower().EndsWith(".dll"))
{
- yield return System.IO.Path.GetFileName(item);
+ yield return Path.GetFileName(item);
}
}
}
public static EcmaModule GetModuleForTestAssembly(string assemblyName)
{
- var typeSystemContext = new SimpleTypeSystemContext();
- var coreAssembly = typeof(Object).Assembly;
- var systemRuntime = Assembly.Load("System.Runtime");
+ var simpleNameToPathMap = new Dictionary<string, string>();
- typeSystemContext.InputFilePaths = new Dictionary<string, string>
+ foreach (var fileName in GetAllTestDlls())
{
- { coreAssembly.GetName().Name, coreAssembly.Location },
- { systemRuntime.GetName().Name, systemRuntime.Location }
- };
+ simpleNameToPathMap.Add(Path.GetFileNameWithoutExtension(fileName), TESTASSEMBLYPATH + fileName);
+ }
- typeSystemContext.ReferenceFilePaths = new Dictionary<string, string>();
- foreach (var fileName in GetAllTestDlls())
- typeSystemContext.ReferenceFilePaths.Add(Path.GetFileNameWithoutExtension(fileName), TESTASSEMBLYPATH + fileName);
+ Assembly coreAssembly = typeof(object).Assembly;
+ simpleNameToPathMap.Add(coreAssembly.GetName().Name, coreAssembly.Location);
+
+ Assembly systemRuntime = Assembly.Load("System.Runtime");
+ simpleNameToPathMap.Add(systemRuntime.GetName().Name, systemRuntime.Location);
+
+ var resolver = new TestResolver(simpleNameToPathMap);
+ var typeSystemContext = new ILVerifyTypeSystemContext(resolver);
+ typeSystemContext.SetSystemModule(typeSystemContext.GetModule(resolver.Resolve(coreAssembly.GetName())));
- typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(coreAssembly.GetName().Name));
- return typeSystemContext.GetModuleFromPath(TESTASSEMBLYPATH + assemblyName);
+ return typeSystemContext.GetModule(resolver.Resolve(new AssemblyName(Path.GetFileNameWithoutExtension(assemblyName))));
+ }
+
+ private sealed class TestResolver : ResolverBase
+ {
+ Dictionary<string, string> _simpleNameToPathMap;
+ public TestResolver(Dictionary<string, string> simpleNameToPathMap)
+ {
+ _simpleNameToPathMap = simpleNameToPathMap;
+ }
+
+ protected override PEReader ResolveCore(AssemblyName name)
+ {
+ if (_simpleNameToPathMap.TryGetValue(name.Name, out string path))
+ {
+ return new PEReader(File.OpenRead(path));
+ }
+
+ return null;
+ }
}
}