diff --git a/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs b/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs index 3b8f324b6..7cdef4c9a 100644 --- a/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs +++ b/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs @@ -20,7 +20,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection.Metadata.Ecma335; -using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Util; using Mono.Cecil; @@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.PdbProvider.Cecil var method = this.module.LookupToken(MetadataTokens.GetToken(handle)) as Mono.Cecil.MethodDefinition; if (method?.DebugInformation == null || !method.DebugInformation.HasSequencePoints) return EmptyList.Instance; - return method.DebugInformation.SequencePoints.Select(point => new Metadata.SequencePoint { + return method.DebugInformation.SequencePoints.Select(point => new SequencePoint { Offset = point.Offset, StartLine = point.StartLine, StartColumn = point.StartColumn, diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index 567a8427b..72d329a5b 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -348,8 +348,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var emitResult = compilation.Emit(peStream); peStream.Position = 0; - var moduleDefinition = new PEFile("TestAssembly.dll", peStream, false, PEStreamOptions.PrefetchEntireImage); - var decompiler = new CSharpDecompiler(moduleDefinition, new DecompilerSettings()); + var moduleDefinition = new PEFile("TestAssembly.dll", peStream, PEStreamOptions.PrefetchEntireImage); + var resolver = new UniversalAssemblyResolver("TestAssembly.dll", false, moduleDefinition.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchEntireImage); + var decompiler = new CSharpDecompiler(moduleDefinition, resolver, new DecompilerSettings()); return decompiler; } @@ -394,9 +395,11 @@ namespace ICSharpCode.Decompiler.Tests.Helpers public static string DecompileCSharp(string assemblyFileName, DecompilerSettings settings = null) { using (var file = new FileStream(assemblyFileName, FileMode.Open, FileAccess.Read)) { - var module = new PEFile(assemblyFileName, file, false, PEStreamOptions.PrefetchEntireImage); - var typeSystem = new DecompilerTypeSystem(module); - CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, settings ?? new DecompilerSettings()); + var module = new PEFile(assemblyFileName, file, PEStreamOptions.PrefetchEntireImage); + var resolver = new UniversalAssemblyResolver(assemblyFileName, false, + module.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchMetadata); + var typeSystem = new DecompilerTypeSystem(module, resolver); + CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, resolver, settings ?? new DecompilerSettings()); decompiler.AstTransforms.Insert(0, new RemoveEmbeddedAtttributes()); decompiler.AstTransforms.Insert(0, new RemoveCompilerAttribute()); decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); diff --git a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs index c061e54d4..a56c40496 100644 --- a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs +++ b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs @@ -144,11 +144,12 @@ namespace ICSharpCode.Decompiler.Tests Console.WriteLine($"Decompiling {fileToRoundtrip}..."); Stopwatch w = Stopwatch.StartNew(); using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) { - PEFile module = new PEFile(file, fileStream, false, PEStreamOptions.PrefetchEntireImage); - UniversalAssemblyResolver resolver = (UniversalAssemblyResolver)module.AssemblyResolver; + PEFile module = new PEFile(file, fileStream, PEStreamOptions.PrefetchEntireImage); + UniversalAssemblyResolver resolver = new UniversalAssemblyResolver(file, false, module.Reader.DetectTargetFrameworkId(), PEStreamOptions.PrefetchMetadata); resolver.AddSearchDirectory(inputDir); resolver.RemoveSearchDirectory("."); var decompiler = new TestProjectDecompiler(inputDir); + decompiler.AssemblyResolver = resolver; // use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes decompiler.ProjectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}"); decompiler.DecompileProject(module, decompiledDir); diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 24f460238..a3dfe539f 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -39,6 +39,8 @@ using System.Reflection.Metadata; using SRM = System.Reflection.Metadata; using ICSharpCode.Decompiler.Metadata; using System.Reflection.PortableExecutable; +using ICSharpCode.Decompiler.Documentation; +using ICSharpCode.Decompiler.DebugInfo; namespace ICSharpCode.Decompiler.CSharp { @@ -179,6 +181,18 @@ namespace ICSharpCode.Decompiler.CSharp public IDecompilerTypeSystem TypeSystem => typeSystem; + public IAssemblyResolver AssemblyResolver { get; } + + /// + /// Gets or sets the optional provider for debug info. + /// + public IDebugInfoProvider DebugInfoProvider { get; set; } + + /// + /// Gets or sets the optional provider for XML documentation strings. + /// + public IDocumentationProvider DocumentationProvider { get; set; } + /// /// IL transforms. /// @@ -193,21 +207,20 @@ namespace ICSharpCode.Decompiler.CSharp get { return astTransforms; } } - public CSharpDecompiler(string fileName, DecompilerSettings settings) - : this(LoadPEFile(fileName, settings), settings) + public CSharpDecompiler(string fileName, IAssemblyResolver assemblyResolver, DecompilerSettings settings) + : this(LoadPEFile(fileName, settings), assemblyResolver, settings) { } - public CSharpDecompiler(Metadata.PEFile module, DecompilerSettings settings) - : this(new DecompilerTypeSystem(module, settings), settings) + public CSharpDecompiler(PEFile module, IAssemblyResolver assemblyResolver, DecompilerSettings settings) + : this(new DecompilerTypeSystem(module, assemblyResolver, settings), assemblyResolver, settings) { } - public CSharpDecompiler(DecompilerTypeSystem typeSystem, DecompilerSettings settings) + public CSharpDecompiler(DecompilerTypeSystem typeSystem, IAssemblyResolver assemblyResolver, DecompilerSettings settings) { - if (typeSystem == null) - throw new ArgumentNullException(nameof(typeSystem)); - this.typeSystem = typeSystem; + this.typeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); + this.AssemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver)); this.settings = settings; } @@ -318,7 +331,6 @@ namespace ICSharpCode.Decompiler.CSharp return new PEFile( fileName, new FileStream(fileName, FileMode.Open, FileAccess.Read), - settings.ThrowOnAssemblyResolveErrors, options: settings.LoadInMemory ? PEStreamOptions.PrefetchEntireImage : PEStreamOptions.Default ); } @@ -436,7 +448,7 @@ namespace ICSharpCode.Decompiler.CSharp { var decompileRun = new DecompileRun(settings) { CancellationToken = CancellationToken }; RequiredNamespaceCollector.CollectNamespaces(function.Method, typeSystem, decompileRun.Namespaces); - return new ILTransformContext(function, typeSystem, settings) { + return new ILTransformContext(function, typeSystem, DebugInfoProvider, settings) { CancellationToken = CancellationToken, DecompileRun = decompileRun }; @@ -887,10 +899,13 @@ namespace ICSharpCode.Decompiler.CSharp { try { var specializingTypeSystem = typeSystem.GetSpecializingTypeSystem(decompilationContext); - var ilReader = new ILReader(specializingTypeSystem); - ilReader.UseDebugSymbols = settings.UseDebugSymbols; + var ilReader = new ILReader(specializingTypeSystem) { + UseDebugSymbols = settings.UseDebugSymbols, + DebugInfo = DebugInfoProvider + }; var methodDef = typeSystem.ModuleDefinition.Metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); - var function = ilReader.ReadIL(typeSystem.ModuleDefinition, (MethodDefinitionHandle)method.MetadataToken, typeSystem.ModuleDefinition.Reader.GetMethodBody(methodDef.RelativeVirtualAddress), CancellationToken); + var methodBody = typeSystem.ModuleDefinition.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); + var function = ilReader.ReadIL(typeSystem.ModuleDefinition, (MethodDefinitionHandle)method.MetadataToken, methodBody, CancellationToken); function.CheckInvariant(ILPhase.Normal); if (entityDecl != null) { @@ -911,7 +926,7 @@ namespace ICSharpCode.Decompiler.CSharp localSettings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls = true; } - var context = new ILTransformContext(function, specializingTypeSystem, localSettings) { + var context = new ILTransformContext(function, specializingTypeSystem, DebugInfoProvider, localSettings) { CancellationToken = CancellationToken, DecompileRun = decompileRun }; @@ -1126,7 +1141,7 @@ namespace ICSharpCode.Decompiler.CSharp /// /// This only works correctly when the nodes in the syntax tree have line/column information. /// - public Dictionary> CreateSequencePoints(SyntaxTree syntaxTree) + public Dictionary> CreateSequencePoints(SyntaxTree syntaxTree) { SequencePointBuilder spb = new SequencePointBuilder(); syntaxTree.AcceptVisitor(spb); diff --git a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs index b036f7985..5ad23f4e3 100644 --- a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - readonly List<(ILFunction, Metadata.SequencePoint)> sequencePoints = new List<(ILFunction, Metadata.SequencePoint)>(); + readonly List<(ILFunction, DebugInfo.SequencePoint)> sequencePoints = new List<(ILFunction, DebugInfo.SequencePoint)>(); readonly HashSet mappedInstructions = new HashSet(); // Stack holding information for outer statements. @@ -219,7 +219,7 @@ namespace ICSharpCode.Decompiler.CSharp // use LongSet to deduplicate and merge the intervals var longSet = new LongSet(current.Intervals.Select(i => new LongInterval(i.Start, i.End))); Debug.Assert(!longSet.IsEmpty); - sequencePoints.Add((current.Function, new Metadata.SequencePoint { + sequencePoints.Add((current.Function, new DebugInfo.SequencePoint { Offset = (int)longSet.Intervals[0].Start, EndOffset = (int)longSet.Intervals[0].End, StartLine = startLocation.Line, @@ -277,18 +277,18 @@ namespace ICSharpCode.Decompiler.CSharp /// /// Called after the visitor is done to return the results. /// - internal Dictionary> GetSequencePoints() + internal Dictionary> GetSequencePoints() { - var dict = new Dictionary>(); + var dict = new Dictionary>(); foreach (var (function, sequencePoint) in this.sequencePoints) { if (!dict.TryGetValue(function, out var list)) { - dict.Add(function, list = new List()); + dict.Add(function, list = new List()); } list.Add(sequencePoint); } foreach (var (function, list) in dict.ToList()) { // For each function, sort sequence points and fix overlaps+gaps - var newList = new List(); + var newList = new List(); int pos = 0; foreach (var sequencePoint in list.OrderBy(sp => sp.Offset).ThenBy(sp => sp.EndOffset)) { if (sequencePoint.Offset < pos) { @@ -305,7 +305,7 @@ namespace ICSharpCode.Decompiler.CSharp } } else if (sequencePoint.Offset > pos) { // insert hidden sequence point in the gap. - var hidden = new Metadata.SequencePoint(); + var hidden = new DebugInfo.SequencePoint(); hidden.Offset = pos; hidden.EndOffset = sequencePoint.Offset; hidden.SetHidden(); @@ -315,7 +315,7 @@ namespace ICSharpCode.Decompiler.CSharp pos = sequencePoint.EndOffset; } if (pos < function.CodeSize) { - var hidden = new Metadata.SequencePoint(); + var hidden = new DebugInfo.SequencePoint(); hidden.Offset = pos; hidden.EndOffset = function.CodeSize; hidden.SetHidden(); diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 74c639267..25c4aa3e6 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -34,6 +34,7 @@ using System.Reflection.PortableExecutable; using System.Reflection.Metadata; using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; +using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.CSharp { @@ -56,6 +57,8 @@ namespace ICSharpCode.Decompiler.CSharp } } + public IAssemblyResolver AssemblyResolver { get; set; } + /// /// The MSBuild ProjectGuid to use for the new project. /// null to automatically generate a new GUID. @@ -218,7 +221,7 @@ namespace ICSharpCode.Decompiler.CSharp if (r.Name != "mscorlib") { w.WriteStartElement("Reference"); w.WriteAttributeString("Include", r.Name); - var asm = module.AssemblyResolver.Resolve(r); + var asm = AssemblyResolver.Resolve(r); if (!IsGacAssembly(r, asm)) { if (asm != null) { w.WriteElementString("HintPath", asm.FileName); @@ -275,7 +278,7 @@ namespace ICSharpCode.Decompiler.CSharp CSharpDecompiler CreateDecompiler(DecompilerTypeSystem ts) { - var decompiler = new CSharpDecompiler(ts, settings); + var decompiler = new CSharpDecompiler(ts, AssemblyResolver, settings); decompiler.AstTransforms.Add(new EscapeInvalidIdentifiers()); decompiler.AstTransforms.Add(new RemoveCLSCompliantAttribute()); return decompiler; @@ -314,7 +317,7 @@ namespace ICSharpCode.Decompiler.CSharp return Path.Combine(dir, file); } }, StringComparer.OrdinalIgnoreCase).ToList(); - DecompilerTypeSystem ts = new DecompilerTypeSystem(module); + DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver); Parallel.ForEach( files, new ParallelOptions { diff --git a/ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs b/ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs new file mode 100644 index 000000000..c35c47bf2 --- /dev/null +++ b/ICSharpCode.Decompiler/DebugInfo/IDebugInfoProvider.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Reflection.Metadata; +using System.Text; + +namespace ICSharpCode.Decompiler.DebugInfo +{ + public struct Variable + { + public string Name { get; set; } + } + + public interface IDebugInfoProvider + { + IList GetSequencePoints(MethodDefinitionHandle method); + IList GetVariables(MethodDefinitionHandle method); + bool TryGetName(MethodDefinitionHandle method, int index, out string name); + } +} diff --git a/ICSharpCode.Decompiler/Pdb/PortablePdbWriter.cs b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs similarity index 98% rename from ICSharpCode.Decompiler/Pdb/PortablePdbWriter.cs rename to ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs index dbd849374..0cf6fdb03 100644 --- a/ICSharpCode.Decompiler/Pdb/PortablePdbWriter.cs +++ b/ICSharpCode.Decompiler/DebugInfo/PortablePdbWriter.cs @@ -17,7 +17,7 @@ using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; -namespace ICSharpCode.Decompiler.Pdb +namespace ICSharpCode.Decompiler.DebugInfo { public class PortablePdbWriter { @@ -39,7 +39,7 @@ namespace ICSharpCode.Decompiler.Pdb var hasher = SHA256.Create(); var sequencePointBlobs = new Dictionary(); var importScopeBlobs = new Dictionary(); - var emptyList = new List(); + var emptyList = new List(); foreach (var handle in reader.GetTopLevelTypeDefinitions()) { var type = reader.GetTypeDefinition(handle); @@ -156,7 +156,7 @@ namespace ICSharpCode.Decompiler.Pdb return metadata.GetOrAddBlob(writer); } - static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List sequencePoints) + static BlobHandle EncodeSequencePoints(MetadataBuilder metadata, int localSignatureRowId, List sequencePoints) { if (sequencePoints.Count == 0) return default; diff --git a/ICSharpCode.Decompiler/Metadata/SequencePoint.cs b/ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs similarity index 97% rename from ICSharpCode.Decompiler/Metadata/SequencePoint.cs rename to ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs index 5534b21fc..441731f72 100644 --- a/ICSharpCode.Decompiler/Metadata/SequencePoint.cs +++ b/ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs @@ -18,7 +18,7 @@ using System; -namespace ICSharpCode.Decompiler.Metadata +namespace ICSharpCode.Decompiler.DebugInfo { /// /// A sequence point read from a PDB file or produced by the decompiler. diff --git a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs index 856640e80..638fa4dab 100644 --- a/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs @@ -21,7 +21,7 @@ using System.Collections.Generic; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Threading; - +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; @@ -51,7 +51,12 @@ namespace ICSharpCode.Decompiler.Disassembler /// public bool ShowMetadataTokens { get; set; } - IList sequencePoints; + /// + /// Optional provider for sequence points. + /// + public IDebugInfoProvider DebugInfo { get; set; } + + IList sequencePoints; int nextSequencePointIndex; // cache info @@ -68,7 +73,7 @@ namespace ICSharpCode.Decompiler.Disassembler public virtual void Disassemble(PEFile module, MethodDefinitionHandle handle) { - this.module = module; + this.module = module ?? throw new ArgumentNullException(nameof(module)); metadata = module.Metadata; genericContext = new GenericContext(handle, module); signatureDecoder = new DisassemblerSignatureProvider(module, output); @@ -94,7 +99,7 @@ namespace ICSharpCode.Decompiler.Disassembler DisassembleLocalsBlock(body); output.WriteLine(); - sequencePoints = module.DebugInfo?.GetSequencePoints(handle) ?? EmptyList.Instance; + sequencePoints = DebugInfo?.GetSequencePoints(handle) ?? EmptyList.Instance; nextSequencePointIndex = 0; if (DetectControlStructure && blob.Length > 0) { blob.Reset(); @@ -278,7 +283,7 @@ namespace ICSharpCode.Decompiler.Disassembler { int offset = blob.Offset; if (ShowSequencePoints && nextSequencePointIndex < sequencePoints?.Count) { - Metadata.SequencePoint sp = sequencePoints[nextSequencePointIndex]; + var sp = sequencePoints[nextSequencePointIndex]; if (sp.Offset <= offset) { output.Write("// sequence point: "); if (sp.Offset != offset) { diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index 4d045e034..4514887b8 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -27,6 +27,7 @@ using System.Threading; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.DebugInfo; namespace ICSharpCode.Decompiler.Disassembler { @@ -55,8 +56,15 @@ namespace ICSharpCode.Decompiler.Disassembler set => methodBodyDisassembler.ShowMetadataTokens = value; } + public IDebugInfoProvider DebugInfo { + get => methodBodyDisassembler.DebugInfo; + set => methodBodyDisassembler.DebugInfo = value; + } + public bool ExpandMemberDefinitions { get; set; } = false; + public IAssemblyResolver AssemblyResolver { get; set; } + public ReflectionDisassembler(ITextOutput output, CancellationToken cancellationToken) : this(output, new MethodBodyDisassembler(output, cancellationToken), cancellationToken) { @@ -362,59 +370,61 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(secdecl.Action.ToString()); break; } - output.WriteLine(" = {"); - output.Indent(); - var blob = metadata.GetBlobReader(secdecl.PermissionSet); - if ((char)blob.ReadByte() != '.') { - blob.Reset(); - WriteXmlSecurityDeclaration(blob.ReadUTF8(blob.RemainingBytes)); + if (AssemblyResolver == null) { + output.Write(" = "); + WriteBlob(secdecl.PermissionSet, metadata); } else { - string currentAssemblyName = null; - string currentFullAssemblyName = null; - if (metadata.IsAssembly) { - currentAssemblyName = metadata.GetString(metadata.GetAssemblyDefinition().Name); - currentFullAssemblyName = metadata.GetFullAssemblyName(); - } - int count = blob.ReadCompressedInteger(); - for (int i = 0; i < count; i++) { - var typeName = blob.ReadSerializedString(); - string[] nameParts = typeName.Split(new[] { ", " }, StringSplitOptions.None); - if (nameParts.Length < 2 || nameParts[1] == currentAssemblyName) { - output.Write("class "); - output.Write(DisassemblerHelpers.Escape(typeName + ", " + currentFullAssemblyName)); - } else { - string[] typeNameParts = typeName.Split(new[] { ", " }, StringSplitOptions.None); - if (typeNameParts.Length < 2) - throw new NotImplementedException(); - output.Write('['); - output.Write(typeNameParts[1]); - output.Write(']'); - output.WriteReference(typeNameParts[0], null); // TODO : hyperlink! + output.WriteLine(" = {"); + output.Indent(); + var blob = metadata.GetBlobReader(secdecl.PermissionSet); + if ((char)blob.ReadByte() != '.') { + blob.Reset(); + WriteXmlSecurityDeclaration(blob.ReadUTF8(blob.RemainingBytes)); + } else { + string currentAssemblyName = null; + string currentFullAssemblyName = null; + if (metadata.IsAssembly) { + currentAssemblyName = metadata.GetString(metadata.GetAssemblyDefinition().Name); + currentFullAssemblyName = metadata.GetFullAssemblyName(); } - output.Write(" = {"); - blob.ReadCompressedInteger(); // ? - // The specification seems to be incorrect here, so I'm using the logic from Cecil instead. - int argCount = blob.ReadCompressedInteger(); - if (argCount > 0) { - output.WriteLine(); - output.Indent(); - - for (int j = 0; j < argCount; j++) { - WriteSecurityDeclarationArgument(module, ref blob); + int count = blob.ReadCompressedInteger(); + for (int i = 0; i < count; i++) { + var typeName = blob.ReadSerializedString(); + string[] nameParts = typeName.Split(new[] { ", " }, StringSplitOptions.None); + if (nameParts.Length < 2 || nameParts[1] == currentAssemblyName) { + output.Write("class "); + output.Write(DisassemblerHelpers.Escape(typeName + ", " + currentFullAssemblyName)); + } else { + output.Write('['); + output.Write(nameParts[1]); + output.Write(']'); + output.WriteReference(nameParts[0], null); // TODO : hyperlink! + } + output.Write(" = {"); + blob.ReadCompressedInteger(); // ? + // The specification seems to be incorrect here, so I'm using the logic from Cecil instead. + int argCount = blob.ReadCompressedInteger(); + if (argCount > 0) { output.WriteLine(); + output.Indent(); + + for (int j = 0; j < argCount; j++) { + WriteSecurityDeclarationArgument(module, ref blob); + output.WriteLine(); + } + + output.Unindent(); } + output.Write('}'); - output.Unindent(); + if (i + 1 < count) + output.Write(','); + output.WriteLine(); } - output.Write('}'); - - if (i + 1 < count) - output.Write(','); - output.WriteLine(); } + output.Unindent(); + output.WriteLine("}"); } - output.Unindent(); - output.WriteLine("}"); } } @@ -525,7 +535,7 @@ namespace ICSharpCode.Decompiler.Disassembler PEFile containingModule = null; // if we deal with an assembly-qualified name, resolve the assembly if (nameParts.Length == 2) - containingModule = module.AssemblyResolver.Resolve(AssemblyNameReference.Parse(nameParts[1])); + containingModule = AssemblyResolver.Resolve(AssemblyNameReference.Parse(nameParts[1])); if (containingModule != null) { // try to find the type in the assembly var handle = FindType(containingModule, typeNameParts); @@ -539,7 +549,7 @@ namespace ICSharpCode.Decompiler.Disassembler var handle = FindType(module, typeNameParts); if (handle.IsNil) { // otherwise try mscorlib - var mscorlib = module.AssemblyResolver.Resolve(AssemblyNameReference.Parse("mscorlib")); + var mscorlib = AssemblyResolver.Resolve(AssemblyNameReference.Parse("mscorlib")); handle = FindType(mscorlib, typeNameParts); if (handle.IsNil) throw new NotImplementedException(); @@ -1352,27 +1362,27 @@ namespace ICSharpCode.Decompiler.Disassembler output.Write(".custom "); var attr = metadata.GetCustomAttribute(a); attr.Constructor.WriteTo(module, output, GenericContext.Empty); - byte[] blob = metadata.GetBlobBytes(attr.Value); - if (blob.Length > 0) { + if (!attr.Value.IsNil) { output.Write(" = "); - WriteBlob(blob); + WriteBlob(attr.Value, metadata); } output.WriteLine(); } } - void WriteBlob(byte[] blob) + void WriteBlob(BlobHandle blob, MetadataReader metadata) { + var reader = metadata.GetBlobReader(blob); output.Write("("); output.Indent(); - for (int i = 0; i < blob.Length; i++) { - if (i % 16 == 0 && i < blob.Length - 1) { + for (int i = 0; i < reader.Length; i++) { + if (i % 16 == 0 && i < reader.Length - 1) { output.WriteLine(); } else { output.Write(' '); } - output.Write(blob[i].ToString("x2")); + output.Write(reader.ReadByte().ToString("x2")); } output.WriteLine(); @@ -1484,10 +1494,9 @@ namespace ICSharpCode.Decompiler.Disassembler OpenBlock(false); WriteAttributes(module, asm.GetCustomAttributes()); WriteSecurityDeclarations(module, asm.GetDeclarativeSecurityAttributes()); - var publicKey = metadata.GetBlobBytes(asm.PublicKey); - if (publicKey.Length > 0) { + if (!asm.PublicKey.IsNil) { output.Write(".publickey = "); - WriteBlob(publicKey); + WriteBlob(asm.PublicKey, metadata); output.WriteLine(); } if (asm.HashAlgorithm != AssemblyHashAlgorithm.None) { @@ -1518,7 +1527,7 @@ namespace ICSharpCode.Decompiler.Disassembler OpenBlock(false); if (!aref.PublicKeyOrToken.IsNil) { output.Write(".publickeytoken = "); - WriteBlob(metadata.GetBlobBytes(aref.PublicKeyOrToken)); + WriteBlob(aref.PublicKeyOrToken, metadata); output.WriteLine(); } if (aref.Version != null) { diff --git a/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs b/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs index 289138caa..f590fc6c5 100644 --- a/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs +++ b/ICSharpCode.Decompiler/Documentation/XmlDocumentationProvider.cs @@ -23,9 +23,15 @@ using System.IO; using System.Runtime.Serialization; using System.Text; using System.Xml; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Documentation { + public interface IDocumentationProvider + { + string GetDocumentation(IEntity entity); + } + /// /// Provides documentation from an .xml file (as generated by the Microsoft C# compiler). /// @@ -36,7 +42,7 @@ namespace ICSharpCode.Decompiler.Documentation /// If the .xml file is changed, the index will automatically be recreated. /// [Serializable] - public class XmlDocumentationProvider : IDeserializationCallback + public class XmlDocumentationProvider : IDeserializationCallback, IDocumentationProvider { #region Cache sealed class XmlDocumentationCache @@ -316,7 +322,17 @@ namespace ICSharpCode.Decompiler.Documentation throw new ArgumentNullException("key"); return GetDocumentation(key, true); } - + + /// + /// Get the documentation for the specified member. + /// + public string GetDocumentation(IEntity entity) + { + if (entity == null) + throw new ArgumentNullException(nameof(entity)); + return GetDocumentation(XmlDocKeyProvider.GetKey(entity)); + } + string GetDocumentation(string key, bool allowReload) { int hashcode = GetHashCode(key); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 0cc240f7c..92a342f21 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -306,7 +306,7 @@ - + @@ -342,7 +342,8 @@ - + + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index 975769c3c..408844da6 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -384,9 +384,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow ) ); } - var il = new ILReader(typeSystem).ReadIL(typeSystem.ModuleDefinition, method, typeSystem.ModuleDefinition.Reader.GetMethodBody(methodDef.RelativeVirtualAddress), context.CancellationToken); + var body = typeSystem.ModuleDefinition.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); + var il = context.CreateILReader(typeSystem) + .ReadIL(typeSystem.ModuleDefinition, method, body, context.CancellationToken); il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), - new ILTransformContext(il, typeSystem, context.Settings) { + new ILTransformContext(il, typeSystem, context.DebugInfo, context.Settings) { CancellationToken = context.CancellationToken, DecompileRun = context.DecompileRun }); diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 90bfc182f..c442f3f3f 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.IL readonly IDecompilerTypeSystem typeSystem; public bool UseDebugSymbols { get; set; } + public DebugInfo.IDebugInfoProvider DebugInfo { get; set; } public List Warnings { get; } = new List(); public ILReader(IDecompilerTypeSystem typeSystem) @@ -53,7 +54,6 @@ namespace ICSharpCode.Decompiler.IL MetadataReader metadata; IMethod method; MethodBodyBlock body; - Metadata.IDebugInfoProvider debugInfo; StackType methodReturnStackType; BlobReader reader; ImmutableStack currentStack; @@ -79,7 +79,6 @@ namespace ICSharpCode.Decompiler.IL var methodDefinition = metadata.GetMethodDefinition(methodDefinitionHandle); this.body = body; this.reader = body.GetILReader(); - this.debugInfo = module.DebugInfo; this.currentStack = ImmutableStack.Empty; this.unionFind = new UnionFind(); this.stackMismatchPairs = new List<(ILVariable, ILVariable)>(); @@ -177,7 +176,7 @@ namespace ICSharpCode.Decompiler.IL kind = VariableKind.Local; } ILVariable ilVar = new ILVariable(kind, type, index); - if (!UseDebugSymbols || debugInfo == null || !debugInfo.TryGetName((MethodDefinitionHandle)method.MetadataToken, index, out string name)) { + if (!UseDebugSymbols || DebugInfo == null || !DebugInfo.TryGetName((MethodDefinitionHandle)method.MetadataToken, index, out string name)) { ilVar.Name = "V_" + index; ilVar.HasGeneratedName = true; } else if (string.IsNullOrWhiteSpace(name)) { @@ -410,7 +409,8 @@ namespace ICSharpCode.Decompiler.IL /// /// Debugging helper: writes the decoded instruction stream interleaved with the inferred evaluation stack layout. /// - public void WriteTypedIL(Metadata.PEFile module, MethodDefinitionHandle method, MethodBodyBlock body, ITextOutput output, CancellationToken cancellationToken = default(CancellationToken)) + public void WriteTypedIL(Metadata.PEFile module, + MethodDefinitionHandle method, MethodBodyBlock body, ITextOutput output, CancellationToken cancellationToken = default) { Init(module, method, body); ReadInstructions(cancellationToken); diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 20a7f0b2d..626c339cf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -145,8 +145,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!methodDefinition.HasBody()) return null; var localTypeSystem = context.TypeSystem.GetSpecializingTypeSystem(targetMethod.Substitution); - var ilReader = new ILReader(localTypeSystem); - ilReader.UseDebugSymbols = context.Settings.UseDebugSymbols; + var ilReader = context.CreateILReader(localTypeSystem); var function = ilReader.ReadIL(context.TypeSystem.ModuleDefinition, (MethodDefinitionHandle)targetMethod.MetadataToken, context.TypeSystem.ModuleDefinition.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress), context.CancellationToken); function.DelegateType = value.Method.DeclaringType; function.CheckInvariant(ILPhase.Normal); @@ -156,7 +155,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms v.Name = contextPrefix + v.Name; } - var nestedContext = new ILTransformContext(function, localTypeSystem, context.Settings) { + var nestedContext = new ILTransformContext(function, localTypeSystem, context.DebugInfo, context.Settings) { CancellationToken = context.CancellationToken, DecompileRun = context.DecompileRun }; diff --git a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs index 20cdead3c..a549f07c4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/IILTransform.cs @@ -22,6 +22,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using ICSharpCode.Decompiler.CSharp.TypeSystem; +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -42,6 +43,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public ILFunction Function { get; } public IDecompilerTypeSystem TypeSystem { get; } + public IDebugInfoProvider DebugInfo { get; } public DecompilerSettings Settings { get; } public CancellationToken CancellationToken { get; set; } public Stepper Stepper { get; set; } @@ -49,11 +51,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal DecompileRun DecompileRun { get; set; } internal ResolvedUsingScope UsingScope => DecompileRun.UsingScope.Resolve(TypeSystem.Compilation); - public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, DecompilerSettings settings = null) + public ILTransformContext(ILFunction function, IDecompilerTypeSystem typeSystem, IDebugInfoProvider debugInfo, DecompilerSettings settings = null) { this.Function = function ?? throw new ArgumentNullException(nameof(function)); this.TypeSystem = typeSystem ?? throw new ArgumentNullException(nameof(typeSystem)); this.Settings = settings ?? new DecompilerSettings(); + this.DebugInfo = debugInfo; Stepper = new Stepper(); } @@ -61,12 +64,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms { this.Function = context.Function; this.TypeSystem = context.TypeSystem; + this.DebugInfo = context.DebugInfo; this.Settings = context.Settings; this.DecompileRun = context.DecompileRun; this.CancellationToken = context.CancellationToken; this.Stepper = context.Stepper; } + public ILReader CreateILReader(IDecompilerTypeSystem typeSystem = null) + { + if (typeSystem == null) + typeSystem = this.TypeSystem; + return new ILReader(typeSystem) { + UseDebugSymbols = Settings.UseDebugSymbols, + DebugInfo = DebugInfo + }; + } + /// /// Call this method immediately before performing a transform step. /// Unlike context.Stepper.Step(), calls to this method are only compiled in debug builds. diff --git a/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs b/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs index 5583144af..f2a3c84fb 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs @@ -31,20 +31,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; // partially copied from CSharpDecompiler var specializingTypeSystem = context.TypeSystem.GetSpecializingTypeSystem(inst.Method.Substitution); - var ilReader = new ILReader(specializingTypeSystem); - System.Threading.CancellationToken cancellationToken = new System.Threading.CancellationToken(); - var proxyFunction = ilReader.ReadIL(module, handle, module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress), cancellationToken); - var transformContext = new ILTransformContext(proxyFunction, specializingTypeSystem, context.Settings) { - CancellationToken = cancellationToken, + var ilReader = context.CreateILReader(specializingTypeSystem); + var body = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); + var proxyFunction = ilReader.ReadIL(module, handle, body, context.CancellationToken); + var transformContext = new ILTransformContext(proxyFunction, specializingTypeSystem, context.DebugInfo, context.Settings) { + CancellationToken = context.CancellationToken, DecompileRun = context.DecompileRun }; - foreach (var transform in CSharp.CSharpDecompiler.GetILTransforms()) { - if (transform.GetType() != typeof(ProxyCallReplacer)) { // don't call itself on itself - cancellationToken.ThrowIfCancellationRequested(); - transform.Run(proxyFunction, transformContext); - } - } - + proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext); if (!(proxyFunction.Body is BlockContainer blockContainer)) return; if (blockContainer.Blocks.Count != 1) diff --git a/ICSharpCode.Decompiler/Metadata/Dom.cs b/ICSharpCode.Decompiler/Metadata/Dom.cs index 61a201a2c..ccca23987 100644 --- a/ICSharpCode.Decompiler/Metadata/Dom.cs +++ b/ICSharpCode.Decompiler/Metadata/Dom.cs @@ -15,23 +15,6 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.Metadata { - public interface IAssemblyDocumentationResolver - { - XmlDocumentationProvider GetProvider(); - } - - public struct Variable - { - public string Name { get; set; } - } - - public interface IDebugInfoProvider - { - IList GetSequencePoints(MethodDefinitionHandle method); - IList GetVariables(MethodDefinitionHandle method); - bool TryGetName(MethodDefinitionHandle method, int index, out string name); - } - public enum TargetRuntime { Unknown, diff --git a/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs b/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs index 4c54ab314..df2476683 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataResolver.cs @@ -40,18 +40,18 @@ namespace ICSharpCode.Decompiler.Metadata readonly IAssemblyResolver assemblyResolver; readonly Dictionary loadedModules; - public SimpleMetadataResolveContext(PEFile mainModule) + public SimpleMetadataResolveContext(PEFile mainModule, IAssemblyResolver assemblyResolver) { - this.mainModule = mainModule; - this.assemblyResolver = mainModule.AssemblyResolver; + this.mainModule = mainModule ?? throw new ArgumentNullException(nameof(mainModule)); + this.assemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver)); this.loadedModules = new Dictionary(); } - public SimpleMetadataResolveContext(PEFile mainModule, IMetadataResolveContext parentContext) + public SimpleMetadataResolveContext(PEFile mainModule, SimpleMetadataResolveContext parentContext) { - this.mainModule = mainModule; - this.assemblyResolver = mainModule.AssemblyResolver; - this.loadedModules = parentContext is SimpleMetadataResolveContext simple ? simple.loadedModules : new Dictionary(); + this.mainModule = mainModule ?? throw new ArgumentNullException(nameof(mainModule)); + this.assemblyResolver = parentContext.assemblyResolver; + this.loadedModules = parentContext.loadedModules; } public PEFile CurrentModule => mainModule; diff --git a/ICSharpCode.Decompiler/Metadata/PEFile.cs b/ICSharpCode.Decompiler/Metadata/PEFile.cs index 2a04b9149..0d9190df7 100644 --- a/ICSharpCode.Decompiler/Metadata/PEFile.cs +++ b/ICSharpCode.Decompiler/Metadata/PEFile.cs @@ -33,24 +33,17 @@ namespace ICSharpCode.Decompiler.Metadata public string FileName { get; } public PEReader Reader { get; } public MetadataReader Metadata { get; } - public IAssemblyResolver AssemblyResolver { get; } - public IAssemblyDocumentationResolver DocumentationResolver { get; set; } - public IDebugInfoProvider DebugInfo { get; set; } - public PEFile(string fileName, Stream stream, bool throwOnResolveError = false, PEStreamOptions options = PEStreamOptions.Default) + public PEFile(string fileName, Stream stream, PEStreamOptions options = PEStreamOptions.Default) + : this(fileName, new PEReader(stream, options)) { - this.FileName = fileName; - this.Reader = new PEReader(stream, options); - this.Metadata = Reader.GetMetadataReader(); - this.AssemblyResolver = new UniversalAssemblyResolver(fileName, throwOnResolveError, Reader.DetectTargetFrameworkId(), options); } - public PEFile(string fileName, Stream stream, IAssemblyResolver assemblyResolver, PEStreamOptions options = PEStreamOptions.Default) + public PEFile(string fileName, PEReader reader) { - this.FileName = fileName; - this.Reader = new PEReader(stream, options); - this.Metadata = Reader.GetMetadataReader(); - this.AssemblyResolver = assemblyResolver; + this.FileName = fileName ?? throw new ArgumentNullException(nameof(fileName)); + this.Reader = reader ?? throw new ArgumentNullException(nameof(reader)); + this.Metadata = reader.GetMetadataReader(); } public bool IsAssembly => Metadata.IsAssembly; diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 735757173..a735e70f2 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.Decompiler.Metadata throw new AssemblyResolutionException(name); return null; } - return new PEFile(file, new FileStream(file, FileMode.Open, FileAccess.Read), this, options); + return new PEFile(file, new FileStream(file, FileMode.Open, FileAccess.Read), options); } public string FindAssemblyFile(IAssemblyReference name) diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index f270e3d97..c4fde1556 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -26,6 +26,7 @@ using ICSharpCode.Decompiler.Util; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using System.Diagnostics; using System.Collections.Immutable; +using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { @@ -77,18 +78,21 @@ namespace ICSharpCode.Decompiler.TypeSystem { readonly Metadata.PEFile moduleDefinition; readonly ICompilation compilation; + readonly IAssemblyResolver assemblyResolver; readonly TypeSystemOptions typeSystemOptions; readonly MetadataAssembly mainAssembly; - public DecompilerTypeSystem(Metadata.PEFile moduleDefinition) : this(moduleDefinition, new DecompilerSettings()) + public DecompilerTypeSystem(Metadata.PEFile moduleDefinition, IAssemblyResolver assemblyResolver) + : this(moduleDefinition, assemblyResolver, new DecompilerSettings()) { } - public DecompilerTypeSystem(Metadata.PEFile moduleDefinition, DecompilerSettings settings) + public DecompilerTypeSystem(PEFile moduleDefinition, IAssemblyResolver assemblyResolver, DecompilerSettings settings) { if (settings == null) throw new ArgumentNullException(nameof(settings)); - this.moduleDefinition = moduleDefinition; + this.moduleDefinition = moduleDefinition ?? throw new ArgumentNullException(nameof(moduleDefinition)); + this.assemblyResolver = assemblyResolver ?? throw new ArgumentNullException(nameof(assemblyResolver)); typeSystemOptions = TypeSystemOptions.None; if (settings.Dynamic) typeSystemOptions |= TypeSystemOptions.Dynamic; @@ -106,7 +110,7 @@ namespace ICSharpCode.Decompiler.TypeSystem var asmRef = assemblyReferenceQueue.Dequeue(); if (!processedAssemblyReferences.Add(asmRef)) continue; - var asm = moduleDefinition.AssemblyResolver.Resolve(asmRef); + var asm = assemblyResolver.Resolve(asmRef); if (asm != null) { referencedAssemblies.Add(asm.WithOptions(typeSystemOptions)); var metadata = asm.Metadata; diff --git a/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs index 9aa7f7e48..e82032161 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IAssembly.cs @@ -18,6 +18,7 @@ using System.Collections.Generic; using System.Reflection.Metadata; +using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.Decompiler.TypeSystem { @@ -65,6 +66,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// public interface IAssembly : ICompilationProvider { + /// + /// Gets the underlying metadata file. May return null, if the IAssembly was not created from a PE file. + /// + PEFile PEFile { get; } + /// /// Gets whether this assembly is the main assembly of the compilation. /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 218eefe31..33d485691 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -329,6 +329,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.AssemblyAttributes = unresolved.AssemblyAttributes.CreateResolvedAttributes(context); this.ModuleAttributes = unresolved.ModuleAttributes.CreateResolvedAttributes(context); } + + public Metadata.PEFile PEFile => null; public IUnresolvedAssembly UnresolvedAssembly { get { return unresolvedAssembly; } diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs index 45b2f22f7..26eebcea7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataAssembly.cs @@ -38,7 +38,6 @@ namespace ICSharpCode.Decompiler.TypeSystem public class MetadataAssembly : IAssembly { public ICompilation Compilation { get; } - public readonly Metadata.PEFile PEFile; internal readonly MetadataReader metadata; readonly TypeSystemOptions options; internal readonly TypeProvider TypeProvider; @@ -86,6 +85,8 @@ namespace ICSharpCode.Decompiler.TypeSystem public TypeSystemOptions TypeSystemOptions => options; #region IAssembly interface + public PEFile PEFile { get; } + public bool IsMainAssembly => this == Compilation.MainAssembly; public string AssemblyName { get; } diff --git a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs index 1e3b3b217..9eb758512 100644 --- a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs +++ b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs @@ -114,13 +114,13 @@ namespace ILSpy.BamlDecompiler.Tests { using (var fileStream = new FileStream(asmPath, FileMode.Open, FileAccess.Read)) { var module = new PEFile(asmPath, fileStream); - var resolver = (UniversalAssemblyResolver)module.AssemblyResolver; + var resolver = new UniversalAssemblyResolver(asmPath, false, module.Reader.DetectTargetFrameworkId()); resolver.RemoveSearchDirectory("."); resolver.AddSearchDirectory(Path.GetDirectoryName(asmPath)); var res = module.Resources.First(); Stream bamlStream = LoadBaml(res, name + ".baml"); Assert.IsNotNull(bamlStream); - XDocument document = BamlResourceEntryNode.LoadIntoDocument(module, bamlStream, CancellationToken.None); + XDocument document = BamlResourceEntryNode.LoadIntoDocument(module, resolver, bamlStream, CancellationToken.None); XamlIsEqual(File.ReadAllText(sourcePath), document.ToString()); } diff --git a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs index 61b26deb6..fd9ac282e 100644 --- a/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs +++ b/ILSpy.BamlDecompiler/BamlResourceEntryNode.cs @@ -50,30 +50,32 @@ namespace ILSpy.BamlDecompiler { var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; Data.Position = 0; - XDocument xamlDocument = LoadIntoDocument(asm.GetPEFileOrNull(), Data, cancellationToken); + XDocument xamlDocument = LoadIntoDocument(asm.GetPEFileOrNull(), asm.GetAssemblyResolver(), Data, cancellationToken); output.Write(xamlDocument.ToString()); return true; } - internal static XDocument LoadIntoDocument(PEFile module, Stream stream, CancellationToken cancellationToken) + internal static XDocument LoadIntoDocument(PEFile module, IAssemblyResolver assemblyResolver, + Stream stream, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); XDocument xamlDocument; - using (XmlBamlReader reader = new XmlBamlReader(stream, new NRTypeResolver(module))) { + using (XmlBamlReader reader = new XmlBamlReader(stream, new NRTypeResolver(module, assemblyResolver))) { xamlDocument = XDocument.Load(reader); - ConvertConnectionIds(xamlDocument, module, cancellationToken); + ConvertConnectionIds(xamlDocument, module, assemblyResolver, cancellationToken); ConvertToEmptyElements(xamlDocument.Root); MoveNamespacesToRoot(xamlDocument, reader.XmlnsDefinitions); return xamlDocument; } } - static void ConvertConnectionIds(XDocument xamlDocument, PEFile asm, CancellationToken cancellationToken) + static void ConvertConnectionIds(XDocument xamlDocument, PEFile asm, IAssemblyResolver assemblyResolver, + CancellationToken cancellationToken) { var attr = xamlDocument.Root.Attribute(XName.Get("Class", XmlBamlReader.XWPFNamespace)); if (attr != null) { string fullTypeName = attr.Value; - var mappings = new ConnectMethodDecompiler().DecompileEventMappings(asm, fullTypeName, cancellationToken); + var mappings = new ConnectMethodDecompiler().DecompileEventMappings(asm, assemblyResolver, fullTypeName, cancellationToken); RemoveConnectionIds(xamlDocument.Root, mappings); } } diff --git a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs index 740611e07..f0c177b32 100644 --- a/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs +++ b/ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs @@ -36,7 +36,7 @@ namespace ILSpy.BamlDecompiler public string WriteResourceToFile(LoadedAssembly assembly, string fileName, Stream stream, DecompilationOptions options) { - var document = BamlResourceEntryNode.LoadIntoDocument(assembly.GetPEFileOrNull(), stream, options.CancellationToken); + var document = BamlResourceEntryNode.LoadIntoDocument(assembly.GetPEFileOrNull(), assembly.GetAssemblyResolver(), stream, options.CancellationToken); fileName = Path.ChangeExtension(fileName, ".xaml"); document.Save(Path.Combine(options.SaveAsProjectDirectory, fileName)); return fileName; diff --git a/ILSpy.BamlDecompiler/CecilTypeResolver.cs b/ILSpy.BamlDecompiler/CecilTypeResolver.cs index 0164fe120..877ec778d 100644 --- a/ILSpy.BamlDecompiler/CecilTypeResolver.cs +++ b/ILSpy.BamlDecompiler/CecilTypeResolver.cs @@ -4,6 +4,7 @@ using System; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.ILSpy; using Ricciolo.StylesExplorer.MarkupReflection; namespace ILSpy.BamlDecompiler @@ -17,10 +18,10 @@ namespace ILSpy.BamlDecompiler readonly DecompilerTypeSystem typeSystem; readonly ICompilation compilation; - public NRTypeResolver(PEFile module) + public NRTypeResolver(PEFile module, IAssemblyResolver resolver) { - this.module = module; - this.typeSystem = new DecompilerTypeSystem(module); + this.module = module ?? throw new ArgumentNullException(nameof(module)); + this.typeSystem = new DecompilerTypeSystem(module, resolver); this.compilation = typeSystem.Compilation; } diff --git a/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs b/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs index 10eb55937..2dbd549f2 100644 --- a/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs +++ b/ILSpy.BamlDecompiler/ConnectMethodDecompiler.cs @@ -9,9 +9,11 @@ using System.Threading; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.Transforms; +using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy; using Metadata = ICSharpCode.Decompiler.Metadata; namespace ILSpy.BamlDecompiler @@ -29,11 +31,11 @@ namespace ILSpy.BamlDecompiler /// sealed class ConnectMethodDecompiler { - public List<(LongSet, EventRegistration[])> DecompileEventMappings(Metadata.PEFile module, + public List<(LongSet, EventRegistration[])> DecompileEventMappings(Metadata.PEFile module, IAssemblyResolver assemblyResolver, string fullTypeName, CancellationToken cancellationToken) { var result = new List<(LongSet, EventRegistration[])>(); - var typeSystem = new DecompilerTypeSystem(module); + var typeSystem = new DecompilerTypeSystem(module, assemblyResolver); var typeDefinition = typeSystem.Compilation.FindType(new FullTypeName(fullTypeName)).GetDefinition(); if (typeDefinition == null) @@ -58,7 +60,7 @@ namespace ILSpy.BamlDecompiler var function = ilReader.ReadIL(module, (MethodDefinitionHandle)method.MetadataToken, module.Reader.GetMethodBody(metadataEntry.RelativeVirtualAddress), cancellationToken); - var context = new ILTransformContext(function, typeSystem) { + var context = new ILTransformContext(function, typeSystem, null) { CancellationToken = cancellationToken }; function.RunTransforms(CSharpDecompiler.GetILTransforms(), context); diff --git a/ILSpy/DebugInfo/DiaSymNativeDebugInfoProvider.cs b/ILSpy/DebugInfo/DiaSymNativeDebugInfoProvider.cs index 069bee8ae..add40845d 100644 --- a/ILSpy/DebugInfo/DiaSymNativeDebugInfoProvider.cs +++ b/ILSpy/DebugInfo/DiaSymNativeDebugInfoProvider.cs @@ -25,6 +25,7 @@ using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; using System.Text; using System.Threading.Tasks; +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Util; using Microsoft.DiaSymReader; @@ -48,12 +49,12 @@ namespace ICSharpCode.ILSpy.DebugInfo this.reader = SymUnmanagedReaderFactory.CreateReader(stream, this); } - public IList GetSequencePoints(MethodDefinitionHandle handle) + public IList GetSequencePoints(MethodDefinitionHandle handle) { var method = reader.GetMethod(MetadataTokens.GetToken(handle)); if (method == null || method.GetSequencePointCount(out int count) != 0) - return Empty.Array; - var sequencePoints = new Decompiler.Metadata.SequencePoint[count]; + return Empty.Array; + var sequencePoints = new Decompiler.DebugInfo.SequencePoint[count]; var points = method.GetSequencePoints(); int i = 0; var buffer = new char[1024]; @@ -64,7 +65,7 @@ namespace ICSharpCode.ILSpy.DebugInfo } else { url = ""; } - sequencePoints[i] = new Decompiler.Metadata.SequencePoint() { + sequencePoints[i] = new Decompiler.DebugInfo.SequencePoint() { Offset = point.Offset, StartLine = point.StartLine, StartColumn = point.StartColumn, diff --git a/ILSpy/DebugInfo/PortableDebugInfoProvider.cs b/ILSpy/DebugInfo/PortableDebugInfoProvider.cs index f50568a2e..6ff89db90 100644 --- a/ILSpy/DebugInfo/PortableDebugInfoProvider.cs +++ b/ILSpy/DebugInfo/PortableDebugInfoProvider.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Reflection.Metadata; +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy.DebugInfo @@ -34,11 +35,11 @@ namespace ICSharpCode.ILSpy.DebugInfo this.provider = provider; } - public IList GetSequencePoints(MethodDefinitionHandle method) + public IList GetSequencePoints(MethodDefinitionHandle method) { var metadata = provider.GetMetadataReader(); var debugInfo = metadata.GetMethodDebugInformation(method); - var sequencePoints = new List(); + var sequencePoints = new List(); foreach (var point in debugInfo.GetSequencePoints()) { string documentFileName; @@ -50,7 +51,7 @@ namespace ICSharpCode.ILSpy.DebugInfo documentFileName = ""; } - sequencePoints.Add(new Decompiler.Metadata.SequencePoint() { + sequencePoints.Add(new Decompiler.DebugInfo.SequencePoint() { Offset = point.Offset, StartLine = point.StartLine, StartColumn = point.StartColumn, diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index efd3f5741..b4420f480 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -120,6 +120,7 @@ + @@ -135,6 +136,7 @@ + diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index a75239b11..a4d7cdad3 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -110,9 +110,9 @@ namespace ICSharpCode.ILSpy } } - CSharpDecompiler CreateDecompiler(Decompiler.Metadata.PEFile module, DecompilationOptions options) + CSharpDecompiler CreateDecompiler(PEFile module, DecompilationOptions options) { - CSharpDecompiler decompiler = new CSharpDecompiler(module, options.DecompilerSettings); + CSharpDecompiler decompiler = new CSharpDecompiler(module, module.GetAssemblyResolver(), options.DecompilerSettings); decompiler.CancellationToken = options.CancellationToken; while (decompiler.AstTransforms.Count > transformCount) decompiler.AstTransforms.RemoveAt(decompiler.AstTransforms.Count - 1); @@ -129,19 +129,19 @@ namespace ICSharpCode.ILSpy syntaxTree.AcceptVisitor(new CSharpOutputVisitor(tokenWriter, settings.CSharpFormattingOptions)); } - public override void DecompileMethod(Decompiler.Metadata.MethodDefinition method, ITextOutput output, DecompilationOptions options) + public override void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { - AddReferenceWarningMessage(method.Module, output); - var md = method.Module.Metadata.GetMethodDefinition(method.Handle); - WriteCommentLine(output, TypeToString(new Entity(method.Module, md.GetDeclaringType()), includeNamespace: true)); - CSharpDecompiler decompiler = CreateDecompiler(method.Module, options); - var methodDefinition = decompiler.TypeSystem.ResolveAsMethod(method.Handle); + PEFile assembly = method.ParentAssembly.PEFile; + AddReferenceWarningMessage(assembly, output); + WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler(assembly, options); + var methodDefinition = decompiler.TypeSystem.ResolveAsMethod(method.MetadataToken); if (methodDefinition.IsConstructor && methodDefinition.DeclaringType.IsReferenceType != false) { var members = CollectFieldsAndCtors(methodDefinition.DeclaringTypeDefinition, methodDefinition.IsStatic); decompiler.AstTransforms.Add(new SelectCtorTransform(methodDefinition)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(members), decompiler.TypeSystem); } else { - WriteCode(output, options.DecompilerSettings, decompiler.Decompile(method.Handle), decompiler.TypeSystem); + WriteCode(output, options.DecompilerSettings, decompiler.Decompile(method.MetadataToken), decompiler.TypeSystem); } } @@ -196,28 +196,26 @@ namespace ICSharpCode.ILSpy } } - public override void DecompileProperty(Decompiler.Metadata.PropertyDefinition property, ITextOutput output, DecompilationOptions options) + public override void DecompileProperty(IProperty property, ITextOutput output, DecompilationOptions options) { - AddReferenceWarningMessage(property.Module, output); - CSharpDecompiler decompiler = CreateDecompiler(property.Module, options); - var metadata = property.Module.Metadata; - var accessorHandle = metadata.GetPropertyDefinition(property.Handle).GetAccessors().GetAny(); - WriteCommentLine(output, TypeToString(new Decompiler.Metadata.TypeDefinition(property.Module, metadata.GetMethodDefinition(accessorHandle).GetDeclaringType()), includeNamespace: true)); - WriteCode(output, options.DecompilerSettings, decompiler.Decompile(property.Handle), decompiler.TypeSystem); + PEFile assembly = property.ParentAssembly.PEFile; + AddReferenceWarningMessage(assembly, output); + CSharpDecompiler decompiler = CreateDecompiler(assembly, options); + WriteCommentLine(output, TypeToString(property.DeclaringType, includeNamespace: true)); + WriteCode(output, options.DecompilerSettings, decompiler.Decompile(property.MetadataToken), decompiler.TypeSystem); } - public override void DecompileField(Decompiler.Metadata.FieldDefinition field, ITextOutput output, DecompilationOptions options) + public override void DecompileField(IField field, ITextOutput output, DecompilationOptions options) { - AddReferenceWarningMessage(field.Module, output); - var fd = field.Module.Metadata.GetFieldDefinition(field.Handle); - WriteCommentLine(output, TypeToString(new Decompiler.Metadata.TypeDefinition(field.Module, fd.GetDeclaringType()), includeNamespace: true)); - CSharpDecompiler decompiler = CreateDecompiler(field.Module, options); - var fieldDefinition = decompiler.TypeSystem.ResolveAsField(field.Handle); - if (fd.HasFlag(FieldAttributes.Literal)) { - WriteCode(output, options.DecompilerSettings, decompiler.Decompile(field.Handle), decompiler.TypeSystem); + PEFile assembly = field.ParentAssembly.PEFile; + AddReferenceWarningMessage(assembly, output); + WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler(assembly, options); + if (field.IsConst) { + WriteCode(output, options.DecompilerSettings, decompiler.Decompile(field.MetadataToken), decompiler.TypeSystem); } else { - var members = CollectFieldsAndCtors(fieldDefinition.DeclaringTypeDefinition, fieldDefinition.IsStatic); - decompiler.AstTransforms.Add(new SelectFieldTransform(fieldDefinition)); + var members = CollectFieldsAndCtors(field.DeclaringTypeDefinition, field.IsStatic); + decompiler.AstTransforms.Add(new SelectFieldTransform(field)); WriteCode(output, options.DecompilerSettings, decompiler.Decompile(members), decompiler.TypeSystem); } } @@ -266,25 +264,25 @@ namespace ICSharpCode.ILSpy } } - public override void DecompileEvent(Decompiler.Metadata.EventDefinition ev, ITextOutput output, DecompilationOptions options) + public override void DecompileEvent(IEvent @event, ITextOutput output, DecompilationOptions options) { - AddReferenceWarningMessage(ev.Module, output); - var metadata = ev.Module.Metadata; - var accessorHandle = metadata.GetEventDefinition(ev.Handle).GetAccessors().GetAny(); - base.WriteCommentLine(output, TypeToString(new Decompiler.Metadata.TypeDefinition(ev.Module, metadata.GetMethodDefinition(accessorHandle).GetDeclaringType()), includeNamespace: true)); - CSharpDecompiler decompiler = CreateDecompiler(ev.Module, options); - WriteCode(output, options.DecompilerSettings, decompiler.Decompile(ev.Handle), decompiler.TypeSystem); + PEFile assembly = @event.ParentAssembly.PEFile; + AddReferenceWarningMessage(assembly, output); + base.WriteCommentLine(output, TypeToString(@event.DeclaringType, includeNamespace: true)); + CSharpDecompiler decompiler = CreateDecompiler(assembly, options); + WriteCode(output, options.DecompilerSettings, decompiler.Decompile(@event.MetadataToken), decompiler.TypeSystem); } - public override void DecompileType(Decompiler.Metadata.TypeDefinition type, ITextOutput output, DecompilationOptions options) + public override void DecompileType(ITypeDefinition type, ITextOutput output, DecompilationOptions options) { - AddReferenceWarningMessage(type.Module, output); + PEFile assembly = type.ParentAssembly.PEFile; + AddReferenceWarningMessage(assembly, output); WriteCommentLine(output, TypeToString(type, includeNamespace: true)); - CSharpDecompiler decompiler = CreateDecompiler(type.Module, options); - WriteCode(output, options.DecompilerSettings, decompiler.Decompile(type.Handle), decompiler.TypeSystem); + CSharpDecompiler decompiler = CreateDecompiler(assembly, options); + WriteCode(output, options.DecompilerSettings, decompiler.Decompile(type.MetadataToken), decompiler.TypeSystem); } - void AddReferenceWarningMessage(Decompiler.Metadata.PEFile assembly, ITextOutput output) + void AddReferenceWarningMessage(PEFile assembly, ITextOutput output) { var loadedAssembly = MainWindow.Instance.CurrentAssemblyList.GetAssemblies().FirstOrDefault(la => la.GetPEFileOrNull() == assembly); if (loadedAssembly == null || !loadedAssembly.LoadedAssemblyReferencesInfo.HasErrors) @@ -320,7 +318,7 @@ namespace ICSharpCode.ILSpy public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { - var module = assembly.GetPEFileAsync().Result; + var module = assembly.GetPEFileOrNull(); if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { var decompiler = new ILSpyWholeProjectDecompiler(assembly, options); decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); @@ -328,23 +326,24 @@ namespace ICSharpCode.ILSpy AddReferenceWarningMessage(module, output); output.WriteLine(); base.DecompileAssembly(assembly, output, options); - var metadata = module.Metadata; - - if (metadata.TypeDefinitions.Count > 0) { + IAssemblyResolver assemblyResolver = assembly.GetAssemblyResolver(); + var typeSystem = new DecompilerTypeSystem(module, assemblyResolver, options.DecompilerSettings); + var globalType = typeSystem.MainAssembly.TypeDefinitions.FirstOrDefault(); + if (globalType != null) { output.Write("// Global type: "); - var globalType = metadata.TypeDefinitions.First(); - output.WriteReference(globalType.GetFullTypeName(metadata).ToString(), new Decompiler.Metadata.TypeDefinition(module, globalType)); + output.WriteReference(globalType.FullName, globalType); output.WriteLine(); } + var metadata = module.Metadata; var corHeader = module.Reader.PEHeaders.CorHeader; var entrypointHandle = MetadataTokenHelpers.EntityHandleOrNil(corHeader.EntryPointTokenOrRelativeVirtualAddress); if (!entrypointHandle.IsNil && entrypointHandle.Kind == HandleKind.MethodDefinition) { - var entrypoint = metadata.GetMethodDefinition((MethodDefinitionHandle)entrypointHandle); - output.Write("// Entry point: "); - string name = entrypoint.GetDeclaringType().GetFullTypeName(metadata) + "." + metadata.GetString(entrypoint.Name); - var entrypointEntity = new Decompiler.Metadata.MethodDefinition(module, (MethodDefinitionHandle)entrypointHandle); - output.WriteReference(name, entrypointEntity); - output.WriteLine(); + var entrypoint = typeSystem.ResolveAsMethod(entrypointHandle); + if (entrypoint != null) { + output.Write("// Entry point: "); + output.WriteReference(entrypoint.DeclaringType.FullName + "." + entrypoint.Name, entrypoint); + output.WriteLine(); + } } output.WriteLine("// Architecture: " + GetPlatformDisplayName(module)); if ((corHeader.Flags & System.Reflection.PortableExecutable.CorFlags.ILOnly) == 0) { @@ -358,7 +357,7 @@ namespace ICSharpCode.ILSpy // don't automatically load additional assemblies when an assembly node is selected in the tree view using (options.FullDecompilation ? null : LoadedAssembly.DisableAssemblyLoad()) { - CSharpDecompiler decompiler = new CSharpDecompiler(module, options.DecompilerSettings); + CSharpDecompiler decompiler = new CSharpDecompiler(typeSystem, assemblyResolver, options.DecompilerSettings); decompiler.CancellationToken = options.CancellationToken; SyntaxTree st; if (options.FullDecompilation) { @@ -405,82 +404,35 @@ namespace ICSharpCode.ILSpy } } - public override string TypeToString(Entity type, Decompiler.Metadata.GenericContext genericContext = null, bool includeNamespace = true) + public override string TypeToString(IType type, bool includeNamespace) { - if (type.Handle.IsNil) + if (type == null) throw new ArgumentNullException(nameof(type)); - var metadata = type.Module.Metadata; - ConvertTypeOptions convertTypeOptions = ConvertTypeOptions.IncludeTypeParameterDefinitions; - if (includeNamespace) - convertTypeOptions |= ConvertTypeOptions.IncludeNamespace; - //if (includeTypeName) - // convertTypeOptions |= ConvertTypeOptions.IncludeOuterTypeName; - var builder = new AstTypeBuilder(convertTypeOptions); - AstType astType; - switch (type.Handle.Kind) { - case HandleKind.TypeReference: - astType = builder.GetTypeFromReference(metadata, (TypeReferenceHandle)type.Handle, 0); - return TypeToString(astType, metadata, null); - case HandleKind.TypeDefinition: - var td = metadata.GetTypeDefinition((TypeDefinitionHandle)type.Handle); - var genericParams = td.GetGenericParameters(); - - var buffer = new System.Text.StringBuilder(); - - var name = td.GetFullTypeName(metadata); - - if (includeNamespace) - buffer.Append(name.ToString()); - else - buffer.Append(name.Name); - - if (genericParams.Count > 0) { - buffer.Append('<'); - int i = 0; - foreach (var h in genericParams) { - var gp = metadata.GetGenericParameter(h); - if (i > 0) - buffer.Append(", "); - buffer.Append(metadata.GetString(gp.Name)); - i++; - } - buffer.Append('>'); - } + TypeSystemAstBuilder builder = new TypeSystemAstBuilder(); + AstType astType = builder.ConvertType(type); + StringWriter w = new StringWriter(); - return buffer.ToString(); - case HandleKind.TypeSpecification: - var ts = metadata.GetTypeSpecification((TypeSpecificationHandle)type.Handle); - astType = builder.GetTypeFromSpecification(metadata, genericContext ?? GenericContext.Empty, (TypeSpecificationHandle)type.Handle, 0); - return TypeToString(astType, metadata, ts.GetCustomAttributes()); - default: - throw new NotSupportedException(); - } + astType.AcceptVisitor(new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateAllman())); + return w.ToString(); } - public override string FieldToString(Decompiler.Metadata.FieldDefinition field, bool includeTypeName, bool includeNamespace) + public override string FieldToString(IField field, bool includeTypeName, bool includeNamespace) { - if (field.Handle.IsNil) + if (field == null) throw new ArgumentNullException(nameof(field)); - ConvertTypeOptions convertTypeOptions = ConvertTypeOptions.IncludeTypeParameterDefinitions; - if (includeNamespace) - convertTypeOptions |= ConvertTypeOptions.IncludeNamespace; - if (includeTypeName) - convertTypeOptions |= ConvertTypeOptions.IncludeOuterTypeName; - var metadata = field.Module.Metadata; - var fd = metadata.GetFieldDefinition(field.Handle); - AstType fieldType = fd.DecodeSignature(new AstTypeBuilder(convertTypeOptions), new GenericContext(fd.GetDeclaringType(), field.Module)); - string simple = metadata.GetString(fd.Name) + " : " + TypeToString(fieldType, metadata, fd.GetCustomAttributes()); + + string simple = field.Name + " : " + TypeToString(field.Type, includeNamespace); if (!includeTypeName) return simple; - var typeName = fd.GetDeclaringType().GetFullTypeName(metadata); + var typeName = field.DeclaringTypeDefinition.FullTypeName; if (!includeNamespace) return typeName.Name + "." + simple; return typeName + "." + simple; } - - public override string PropertyToString(Decompiler.Metadata.PropertyDefinition property, bool includeTypeName, bool includeNamespace, bool? isIndexer = null) + /* + public override string PropertyToString(IProperty property, bool includeTypeName, bool includeNamespace, bool? isIndexer = null) { - if (property.IsNil) + if (property == null) throw new ArgumentNullException(nameof(property)); ConvertTypeOptions convertTypeOptions = ConvertTypeOptions.IncludeTypeParameterDefinitions; if (includeNamespace) @@ -699,43 +651,18 @@ namespace ICSharpCode.ILSpy buffer.Append(" : "); buffer.Append(TypeToString(signature, metadata, returnTypeAttributes)); return buffer.ToString(); - } + }*/ - public override bool ShowMember(IMetadataEntity member) + public override bool ShowMember(IEntity member) { - return showAllMembers || !CSharpDecompiler.MemberIsHidden(member.Module, member.Handle, new DecompilationOptions().DecompilerSettings); + PEFile assembly = member.ParentAssembly.PEFile; + return showAllMembers || !CSharpDecompiler.MemberIsHidden(assembly, member.MetadataToken, new DecompilationOptions().DecompilerSettings); } - public override string GetTooltip(Entity entity) + public override string GetTooltip(IEntity entity) { - var decompilerTypeSystem = new DecompilerTypeSystem(entity.Module); - ISymbol symbol; - switch (entity.Handle.Kind) { - case HandleKind.MethodDefinition: - symbol = decompilerTypeSystem.ResolveAsMethod(entity.Handle); - if (symbol == null) return base.GetTooltip(entity); - break; - case HandleKind.PropertyDefinition: - symbol = decompilerTypeSystem.ResolveAsProperty(entity.Handle); - if (symbol == null) return base.GetTooltip(entity); - break; - case HandleKind.EventDefinition: - symbol = decompilerTypeSystem.ResolveAsEvent(entity.Handle); - if (symbol == null) return base.GetTooltip(entity); - break; - case HandleKind.FieldDefinition: - symbol = decompilerTypeSystem.ResolveAsField(entity.Handle); - if (symbol == null) return base.GetTooltip(entity); - break; - case HandleKind.TypeDefinition: - symbol = decompilerTypeSystem.ResolveAsType(entity.Handle).GetDefinition(); - if (symbol == null) return base.GetTooltip(entity); - break; - default: - return base.GetTooltip(entity); - } var flags = ConversionFlags.All & ~ConversionFlags.ShowBody; - return new CSharpAmbience() { ConversionFlags = flags }.ConvertSymbol(symbol); + return new CSharpAmbience() { ConversionFlags = flags }.ConvertSymbol(entity); } public override CodeMappingInfo GetCodeMappingInfo(PEFile module, EntityHandle member) diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 68c613ecd..02503c124 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -21,9 +21,11 @@ using System.Collections.Generic; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; @@ -39,6 +41,8 @@ namespace ICSharpCode.ILSpy /// public sealed class LoadedAssembly { + internal static readonly ConditionalWeakTable loadedAssemblies = new ConditionalWeakTable(); + readonly Task assemblyTask; readonly AssemblyList assemblyList; readonly string fileName; @@ -65,6 +69,8 @@ namespace ICSharpCode.ILSpy public ReferenceLoadInfo LoadedAssemblyReferencesInfo { get; } = new ReferenceLoadInfo(); + IDebugInfoProvider debugInfoProvider; + /// /// Gets the Cecil ModuleDefinition. /// @@ -87,12 +93,20 @@ namespace ICSharpCode.ILSpy } } - public ICompilation GetTypeSystem() + ICompilation typeSystem; + + /// + /// Gets a type system containing all types from this assembly + primitve types from mscorlib. + /// Returns null in case of load errors. + /// + public ICompilation GetTypeSystemOrNull() { + if (typeSystem != null) + return typeSystem; var module = GetPEFileOrNull(); if (module == null) return null; - return new SimpleCompilation(module, MinimalCorlib.Instance); + return typeSystem = new SimpleCompilation(module, MinimalCorlib.Instance); } public AssemblyList AssemblyList => assemblyList; @@ -129,12 +143,12 @@ namespace ICSharpCode.ILSpy if (stream != null) { // Read the module from a precrafted stream - module = new PEFile(fileName, stream, new MyAssemblyResolver(this), PEStreamOptions.Default); + module = new PEFile(fileName, stream); } else { // Read the module from disk (by default) - module = new PEFile(fileName, new FileStream(fileName, FileMode.Open, FileAccess.Read), new MyAssemblyResolver(this), PEStreamOptions.PrefetchEntireImage); + module = new PEFile(fileName, new FileStream(fileName, FileMode.Open, FileAccess.Read), PEStreamOptions.PrefetchEntireImage); } if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) { @@ -146,6 +160,9 @@ namespace ICSharpCode.ILSpy // ignore any errors during symbol loading } } + lock (loadedAssemblies) { + loadedAssemblies.Add(module, this); + } return module; } @@ -154,13 +171,13 @@ namespace ICSharpCode.ILSpy var reader = module.Reader; // try to open portable pdb file/embedded pdb info: if (reader.TryOpenAssociatedPortablePdb(fileName, OpenStream, out var provider, out var pdbFileName)) { - module.DebugInfo = new PortableDebugInfoProvider(pdbFileName, provider); + debugInfoProvider = new PortableDebugInfoProvider(pdbFileName, provider); } else { // search for pdb in same directory as dll string pdbDirectory = Path.GetDirectoryName(fileName); pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb"); if (File.Exists(pdbFileName)) { - module.DebugInfo = new DiaSymNativeDebugInfoProvider(module, pdbFileName, OpenStream(pdbFileName)); + debugInfoProvider = new DiaSymNativeDebugInfoProvider(module, pdbFileName, OpenStream(pdbFileName)); return; } @@ -222,6 +239,16 @@ namespace ICSharpCode.ILSpy { return new MyAssemblyResolver(this); } + + /// + /// Returns the debug info for this assembly. Returns null in case of load errors or no debug info is available. + /// + public IDebugInfoProvider GetDebugInfoOrNull() + { + if (GetPEFileOrNull() == null) + return null; + return debugInfoProvider; + } public LoadedAssembly LookupReferencedAssembly(Decompiler.Metadata.IAssemblyReference reference) { diff --git a/ILSpy/LoadedAssemblyExtensions.cs b/ILSpy/LoadedAssemblyExtensions.cs new file mode 100644 index 000000000..059b4e6ca --- /dev/null +++ b/ILSpy/LoadedAssemblyExtensions.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using ICSharpCode.Decompiler.Metadata; + +namespace ICSharpCode.ILSpy +{ + public static class LoadedAssemblyExtensions + { + public static IAssemblyResolver GetAssemblyResolver(this PEFile file) + { + if (file == null) + throw new ArgumentNullException(nameof(file)); + LoadedAssembly loadedAssembly; + lock (LoadedAssembly.loadedAssemblies) { + if (!LoadedAssembly.loadedAssemblies.TryGetValue(file, out loadedAssembly)) + throw new ArgumentException("The specified file is not associated with a LoadedAssembly!"); + } + return loadedAssembly.GetAssemblyResolver(); + } + } +} diff --git a/ILSpy/SearchPane.cs b/ILSpy/SearchPane.cs index f97b1273d..c55c72563 100644 --- a/ILSpy/SearchPane.cs +++ b/ILSpy/SearchPane.cs @@ -217,7 +217,7 @@ namespace ICSharpCode.ILSpy try { var searcher = GetSearchStrategy(searchMode, searchTerm); foreach (var loadedAssembly in assemblies) { - var typeSystem = loadedAssembly.GetTypeSystem(); + var typeSystem = loadedAssembly.GetTypeSystemOrNull(); if (typeSystem == null) continue; CancellationToken cancellationToken = cts.Token; diff --git a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs index a257bfe62..7af0a700e 100644 --- a/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs +++ b/ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs @@ -233,7 +233,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer IEnumerable FindReferencesInAssembly(Decompiler.Metadata.PEFile module, CancellationToken ct) { - IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(module) : null; + IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) : null; var metadata = module.Metadata; foreach (var type in TreeTraversal.PreOrder(metadata.TypeDefinitions, t => metadata.GetTypeDefinition(t).GetNestedTypes())) { ct.ThrowIfCancellationRequested(); @@ -247,7 +247,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer IEnumerable FindReferencesInTypeScope(CancellationToken ct) { - IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(assemblyScope) : null; + IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(assemblyScope, assemblyScope.GetAssemblyResolver()) : null; foreach (var type in TreeTraversal.PreOrder(typeScopeHandle, t => assemblyScope.Metadata.GetTypeDefinition(t).GetNestedTypes())) { ct.ThrowIfCancellationRequested(); var codeMappingInfo = language.GetCodeMappingInfo(assemblyScope, type); @@ -260,7 +260,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer IEnumerable FindReferencesInEnclosingTypeScope(CancellationToken ct) { - IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(assemblyScope) : null; + IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(assemblyScope, assemblyScope.GetAssemblyResolver()) : null; var codeMappingInfo = language.GetCodeMappingInfo(assemblyScope, typeScope.GetDeclaringType()); foreach (var type in TreeTraversal.PreOrder(typeScope.GetDeclaringType(), t => assemblyScope.Metadata.GetTypeDefinition(t).GetNestedTypes())) { ct.ThrowIfCancellationRequested(); diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index bb3cba6a4..a56b324f9 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -135,7 +135,7 @@ namespace ICSharpCode.ILSpy.TreeNodes // if we crashed on loading, then we don't have any children return; } - typeSystem = new SimpleCompilation(module, MinimalCorlib.Instance); + typeSystem = LoadedAssembly.GetTypeSystemOrNull(); var assembly = (MetadataAssembly)typeSystem.MainAssembly; var metadata = module.Metadata; diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs index 42eef9028..7c4362a58 100644 --- a/ILSpy/TreeNodes/BaseTypesEntryNode.cs +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -65,16 +65,16 @@ namespace ICSharpCode.ILSpy.TreeNodes protected override void LoadChildren() { - DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module); + DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver()); var t = typeSystem.ResolveAsType(handle).GetDefinition(); if (t != null) { - BaseTypesTreeNode.AddBaseTypes(this.Children, ((MetadataAssembly)t.ParentAssembly).PEFile, t); + BaseTypesTreeNode.AddBaseTypes(this.Children, t.ParentAssembly.PEFile, t); } } public override void ActivateItem(System.Windows.RoutedEventArgs e) { - DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module); + DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver()); var t = typeSystem.ResolveAsType(handle).GetDefinition(); e.Handled = ActivateItem(this, t); } @@ -98,7 +98,7 @@ namespace ICSharpCode.ILSpy.TreeNodes IEntity IMemberTreeNode.Member { get { - DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module); + DecompilerTypeSystem typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver()); var t = typeSystem.ResolveAsType(handle).GetDefinition(); return t; } diff --git a/ILSpy/TreeNodes/GeneratePdbContextMenuEntry.cs b/ILSpy/TreeNodes/GeneratePdbContextMenuEntry.cs index e8374e0ba..244a995ad 100644 --- a/ILSpy/TreeNodes/GeneratePdbContextMenuEntry.cs +++ b/ILSpy/TreeNodes/GeneratePdbContextMenuEntry.cs @@ -7,7 +7,7 @@ using System.Text; using System.Threading.Tasks; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.Pdb; +using ICSharpCode.Decompiler.DebugInfo; using ICSharpCode.ILSpy.TextView; using Microsoft.Win32; @@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy.TreeNodes using (FileStream stream = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write)) { try { var file = assembly.GetPEFileOrNull(); - var decompiler = new CSharpDecompiler(file, options.DecompilerSettings); + var decompiler = new CSharpDecompiler(file, assembly.GetAssemblyResolver(), options.DecompilerSettings); PortablePdbWriter.WritePdb(file, decompiler, options.DecompilerSettings, stream); } catch (OperationCanceledException) { output.WriteLine();